Various fixes and improvements...
Issue #19912529: VI: VoiceInteractor callback ClassCastException
Fix to use correct argument.
Issue #19912636: VI: Documentation for VoiceInteractionSession.onBackPressed
Added documentation.
Issue #19912703: VI: VoiceInteractionSession NPE on Abort Request
Maybe fix this -- don't crash if there is no active session.
Issue #19953731: VI: Add value index to...
...android.app.VoiceInteractor.PickOptionRequest.Option
There is now an optional index integer that can be associated with
every Option object.
Issue #19912635: VI: Behavior of startActivity when in voice...
...interaction is unexpected
We now forcibly finish the current voice interaction task whenever
another activity takes focus from it.
Issue #20066569: Add API to request heap dumps
New ActivityManager API to set the pss limit to generate heap
dumps.
Also added app ops for assist receiving structure and screenshot
data, so that we can track when it does these things.
Change-Id: I688d4ff8f0bd0b8b9e3390a32375b4bb7875c1a1
diff --git a/api/current.txt b/api/current.txt
index e3658d7..109fdd6 100644
--- a/api/current.txt
+++ b/api/current.txt
@@ -3553,6 +3553,7 @@
public class ActivityManager {
method public int addAppTask(android.app.Activity, android.content.Intent, android.app.ActivityManager.TaskDescription, android.graphics.Bitmap);
method public boolean clearApplicationUserData();
+ method public void clearWatchHeapLimit();
method public void dumpPackageState(java.io.FileDescriptor, java.lang.String);
method public android.util.Size getAppTaskThumbnailSize();
method public java.util.List<android.app.ActivityManager.AppTask> getAppTasks();
@@ -3579,6 +3580,8 @@
method public void moveTaskToFront(int, int);
method public void moveTaskToFront(int, int, android.os.Bundle);
method public deprecated void restartPackage(java.lang.String);
+ method public void setWatchHeapLimit(long);
+ field public static final java.lang.String ACTION_REPORT_HEAP_LIMIT = "android.app.action.REPORT_HEAP_LIMIT";
field public static final int LOCK_TASK_MODE_LOCKED = 1; // 0x1
field public static final int LOCK_TASK_MODE_NONE = 0; // 0x0
field public static final int LOCK_TASK_MODE_PINNED = 2; // 0x2
@@ -5434,10 +5437,12 @@
public static final class VoiceInteractor.PickOptionRequest.Option implements android.os.Parcelable {
ctor public VoiceInteractor.PickOptionRequest.Option(java.lang.CharSequence);
+ ctor public VoiceInteractor.PickOptionRequest.Option(java.lang.CharSequence, int);
method public android.app.VoiceInteractor.PickOptionRequest.Option addSynonym(java.lang.CharSequence);
method public int countSynonyms();
method public int describeContents();
method public android.os.Bundle getExtras();
+ method public int getIndex();
method public java.lang.CharSequence getLabel();
method public java.lang.CharSequence getSynonymAt(int);
method public void setExtras(android.os.Bundle);
@@ -28208,6 +28213,7 @@
ctor public VoiceInteractionSession(android.content.Context);
ctor public VoiceInteractionSession(android.content.Context, android.os.Handler);
method public void finish();
+ method public android.content.Context getContext();
method public android.view.LayoutInflater getLayoutInflater();
method public android.app.Dialog getWindow();
method public void hide();
diff --git a/api/system-current.txt b/api/system-current.txt
index 43cc40f..b53247d 100644
--- a/api/system-current.txt
+++ b/api/system-current.txt
@@ -3641,6 +3641,7 @@
public class ActivityManager {
method public int addAppTask(android.app.Activity, android.content.Intent, android.app.ActivityManager.TaskDescription, android.graphics.Bitmap);
method public boolean clearApplicationUserData();
+ method public void clearWatchHeapLimit();
method public void dumpPackageState(java.io.FileDescriptor, java.lang.String);
method public android.util.Size getAppTaskThumbnailSize();
method public java.util.List<android.app.ActivityManager.AppTask> getAppTasks();
@@ -3668,6 +3669,8 @@
method public void moveTaskToFront(int, int);
method public void moveTaskToFront(int, int, android.os.Bundle);
method public deprecated void restartPackage(java.lang.String);
+ method public void setWatchHeapLimit(long);
+ field public static final java.lang.String ACTION_REPORT_HEAP_LIMIT = "android.app.action.REPORT_HEAP_LIMIT";
field public static final int LOCK_TASK_MODE_LOCKED = 1; // 0x1
field public static final int LOCK_TASK_MODE_NONE = 0; // 0x0
field public static final int LOCK_TASK_MODE_PINNED = 2; // 0x2
@@ -5525,10 +5528,12 @@
public static final class VoiceInteractor.PickOptionRequest.Option implements android.os.Parcelable {
ctor public VoiceInteractor.PickOptionRequest.Option(java.lang.CharSequence);
+ ctor public VoiceInteractor.PickOptionRequest.Option(java.lang.CharSequence, int);
method public android.app.VoiceInteractor.PickOptionRequest.Option addSynonym(java.lang.CharSequence);
method public int countSynonyms();
method public int describeContents();
method public android.os.Bundle getExtras();
+ method public int getIndex();
method public java.lang.CharSequence getLabel();
method public java.lang.CharSequence getSynonymAt(int);
method public void setExtras(android.os.Bundle);
@@ -30283,6 +30288,7 @@
ctor public VoiceInteractionSession(android.content.Context);
ctor public VoiceInteractionSession(android.content.Context, android.os.Handler);
method public void finish();
+ method public android.content.Context getContext();
method public android.view.LayoutInflater getLayoutInflater();
method public android.app.Dialog getWindow();
method public void hide();
diff --git a/cmds/am/src/com/android/commands/am/Am.java b/cmds/am/src/com/android/commands/am/Am.java
index 0a53371..908d46e 100644
--- a/cmds/am/src/com/android/commands/am/Am.java
+++ b/cmds/am/src/com/android/commands/am/Am.java
@@ -1189,12 +1189,12 @@
private void runSetWatchHeap() throws Exception {
String proc = nextArgRequired();
String limit = nextArgRequired();
- mAm.setDumpHeapDebugLimit(proc, Long.parseLong(limit));
+ mAm.setDumpHeapDebugLimit(proc, 0, Long.parseLong(limit), null);
}
private void runClearWatchHeap() throws Exception {
String proc = nextArgRequired();
- mAm.setDumpHeapDebugLimit(proc, -1);
+ mAm.setDumpHeapDebugLimit(proc, 0, -1, null);
}
private void runBugReport() throws Exception {
diff --git a/core/java/android/app/ActivityManager.java b/core/java/android/app/ActivityManager.java
index d143f8b..8f125d7 100644
--- a/core/java/android/app/ActivityManager.java
+++ b/core/java/android/app/ActivityManager.java
@@ -2682,6 +2682,47 @@
}
/**
+ * Request that the system start watching for the calling process to exceed a pss
+ * size as given here. Once called, the system will look for any occassions where it
+ * sees the associated process with a larger pss size and, when this happens, automatically
+ * pull a heap dump from it and allow the user to share the data. Note that this request
+ * continues running even if the process is killed and restarted. To remove the watch,
+ * use {@link #clearWatchHeapLimit()}.
+ *
+ * <p>This API only work if running on a debuggable (userdebug or eng) build.</p>
+ *
+ * <p>Callers can optionally implement {@link #ACTION_REPORT_HEAP_LIMIT} to directly
+ * handle heap limit reports themselves.</p>
+ *
+ * @param pssSize The size in bytes to set the limit at.
+ */
+ public void setWatchHeapLimit(long pssSize) {
+ try {
+ ActivityManagerNative.getDefault().setDumpHeapDebugLimit(null, 0, pssSize,
+ mContext.getPackageName());
+ } catch (RemoteException e) {
+ }
+ }
+
+ /**
+ * Action an app can implement to handle reports from {@link #setWatchHeapLimit(long)}.
+ * If your package has an activity handling this action, it will be launched with the
+ * heap data provided to it the same way as {@link Intent#ACTION_SEND}. Note that to
+ * match the activty must support this action and a MIME type of "*/*".
+ */
+ public static final String ACTION_REPORT_HEAP_LIMIT = "android.app.action.REPORT_HEAP_LIMIT";
+
+ /**
+ * Clear a heap watch limit previously set by {@link #setWatchHeapLimit(long)}.
+ */
+ public void clearWatchHeapLimit() {
+ try {
+ ActivityManagerNative.getDefault().setDumpHeapDebugLimit(null, 0, 0, null);
+ } catch (RemoteException e) {
+ }
+ }
+
+ /**
* @hide
*/
public void startLockTaskMode(int taskId) {
diff --git a/core/java/android/app/ActivityManagerNative.java b/core/java/android/app/ActivityManagerNative.java
index 1484af8..be7287f 100644
--- a/core/java/android/app/ActivityManagerNative.java
+++ b/core/java/android/app/ActivityManagerNative.java
@@ -2427,8 +2427,10 @@
case SET_DUMP_HEAP_DEBUG_LIMIT_TRANSACTION: {
data.enforceInterface(IActivityManager.descriptor);
String procName = data.readString();
+ int uid = data.readInt();
long maxMemSize = data.readLong();
- setDumpHeapDebugLimit(procName, maxMemSize);
+ String reportPackage = data.readString();
+ setDumpHeapDebugLimit(procName, uid, maxMemSize, reportPackage);
reply.writeNoException();
return true;
}
@@ -5644,12 +5646,15 @@
}
@Override
- public void setDumpHeapDebugLimit(String processName, long maxMemSize) throws RemoteException {
+ public void setDumpHeapDebugLimit(String processName, int uid, long maxMemSize,
+ String reportPackage) throws RemoteException {
Parcel data = Parcel.obtain();
Parcel reply = Parcel.obtain();
data.writeInterfaceToken(IActivityManager.descriptor);
data.writeString(processName);
+ data.writeInt(uid);
data.writeLong(maxMemSize);
+ data.writeString(reportPackage);
mRemote.transact(SET_DUMP_HEAP_DEBUG_LIMIT_TRANSACTION, data, reply, 0);
reply.readException();
data.recycle();
diff --git a/core/java/android/app/AppOpsManager.java b/core/java/android/app/AppOpsManager.java
index 4bd2332..381c20c 100644
--- a/core/java/android/app/AppOpsManager.java
+++ b/core/java/android/app/AppOpsManager.java
@@ -208,8 +208,12 @@
public static final int OP_ACTIVATE_VPN = 47;
/** @hide Access the WallpaperManagerAPI to write wallpapers. */
public static final int OP_WRITE_WALLPAPER = 48;
+ /** @hide Received the assist structure from an app. */
+ public static final int OP_ASSIST_STRUCTURE = 49;
+ /** @hide Received a screenshot from assist. */
+ public static final int OP_ASSIST_SCREENSHOT = 50;
/** @hide */
- public static final int _NUM_OP = 49;
+ public static final int _NUM_OP = 51;
/** Access to coarse location information. */
public static final String OPSTR_COARSE_LOCATION =
@@ -288,6 +292,8 @@
OP_PROJECT_MEDIA,
OP_ACTIVATE_VPN,
OP_WRITE_WALLPAPER,
+ OP_ASSIST_STRUCTURE,
+ OP_ASSIST_SCREENSHOT,
};
/**
@@ -344,6 +350,8 @@
null,
OPSTR_ACTIVATE_VPN,
null,
+ null,
+ null,
};
/**
@@ -400,6 +408,8 @@
"PROJECT_MEDIA",
"ACTIVATE_VPN",
"WRITE_WALLPAPER",
+ "ASSIST_STRUCTURE",
+ "ASSIST_SCREENSHOT"
};
/**
@@ -456,6 +466,8 @@
null, // no permission for projecting media
null, // no permission for activating vpn
null, // no permission for supporting wallpaper
+ null, // no permission for receiving assist structure
+ null, // no permission for receiving assist screenshot
};
/**
@@ -513,6 +525,8 @@
null, //PROJECT_MEDIA
UserManager.DISALLOW_CONFIG_VPN, // ACTIVATE_VPN
UserManager.DISALLOW_WALLPAPER, // WRITE_WALLPAPER
+ null, // ASSIST_STRUCTURE
+ null, // ASSIST_SCREENSHOT
};
/**
@@ -569,6 +583,8 @@
false, //PROJECT_MEDIA
false, //ACTIVATE_VPN
false, //WALLPAPER
+ false, //ASSIST_STRUCTURE
+ false, //ASSIST_SCREENSHOT
};
/**
@@ -624,6 +640,8 @@
AppOpsManager.MODE_IGNORED, // OP_PROJECT_MEDIA
AppOpsManager.MODE_IGNORED, // OP_ACTIVATE_VPN
AppOpsManager.MODE_ALLOWED,
+ AppOpsManager.MODE_ALLOWED,
+ AppOpsManager.MODE_ALLOWED,
};
/**
@@ -683,6 +701,8 @@
false,
false,
false,
+ false,
+ false,
};
private static HashMap<String, Integer> sOpStrToOp = new HashMap<String, Integer>();
diff --git a/core/java/android/app/IActivityManager.java b/core/java/android/app/IActivityManager.java
index d794aa3..e20b0da 100644
--- a/core/java/android/app/IActivityManager.java
+++ b/core/java/android/app/IActivityManager.java
@@ -482,7 +482,8 @@
public void systemBackupRestored() throws RemoteException;
public void notifyCleartextNetwork(int uid, byte[] firstPacket) throws RemoteException;
- public void setDumpHeapDebugLimit(String processName, long maxMemSize) throws RemoteException;
+ public void setDumpHeapDebugLimit(String processName, int uid, long maxMemSize,
+ String reportPackage) throws RemoteException;
public void dumpHeapFinished(String path) throws RemoteException;
public void setVoiceKeepAwake(IVoiceInteractionSession session, boolean keepAwake)
diff --git a/core/java/android/app/VoiceInteractor.java b/core/java/android/app/VoiceInteractor.java
index da7bb05..7acf5f0 100644
--- a/core/java/android/app/VoiceInteractor.java
+++ b/core/java/android/app/VoiceInteractor.java
@@ -103,9 +103,9 @@
request = pullRequest((IVoiceInteractorRequest)args.arg1, true);
if (DEBUG) Log.d(TAG, "onCompleteVoice: req="
+ ((IVoiceInteractorRequest)args.arg1).asBinder() + "/" + request
- + " result=" + args.arg1);
+ + " result=" + args.arg2);
if (request != null) {
- ((CompleteVoiceRequest)request).onCompleteResult((Bundle) args.arg1);
+ ((CompleteVoiceRequest)request).onCompleteResult((Bundle) args.arg2);
request.clear();
}
break;
@@ -297,6 +297,7 @@
*/
public static final class Option implements Parcelable {
final CharSequence mLabel;
+ final int mIndex;
ArrayList<CharSequence> mSynonyms;
Bundle mExtras;
@@ -308,6 +309,21 @@
*/
public Option(CharSequence label) {
mLabel = label;
+ mIndex = -1;
+ }
+
+ /**
+ * Creates an option that a user can select with their voice by matching the label
+ * or one of several synonyms.
+ * @param label The label that will both be matched against what the user speaks
+ * and displayed visually.
+ * @param index The location of this option within the overall set of options.
+ * Can be used to help identify which the option when it is returned from the
+ * voice interactor.
+ */
+ public Option(CharSequence label, int index) {
+ mLabel = label;
+ mIndex = index;
}
/**
@@ -328,6 +344,14 @@
return mLabel;
}
+ /**
+ * Return the index that was supplied in the constructor.
+ * If the option was constructed without an index, -1 is returned.
+ */
+ public int getIndex() {
+ return mIndex;
+ }
+
public int countSynonyms() {
return mSynonyms != null ? mSynonyms.size() : 0;
}
@@ -356,6 +380,7 @@
Option(Parcel in) {
mLabel = in.readCharSequence();
+ mIndex = in.readInt();
mSynonyms = in.readCharSequenceList();
mExtras = in.readBundle();
}
@@ -368,6 +393,7 @@
@Override
public void writeToParcel(Parcel dest, int flags) {
dest.writeCharSequence(mLabel);
+ dest.writeInt(mIndex);
dest.writeCharSequenceList(mSynonyms);
dest.writeBundle(mExtras);
}
diff --git a/core/java/android/service/voice/VoiceInteractionSession.java b/core/java/android/service/voice/VoiceInteractionSession.java
index 4c31f80..20d7079 100644
--- a/core/java/android/service/voice/VoiceInteractionSession.java
+++ b/core/java/android/service/voice/VoiceInteractionSession.java
@@ -520,6 +520,10 @@
mCallbacks, true);
}
+ public Context getContext() {
+ return mContext;
+ }
+
Request newRequest(IVoiceInteractorCallback callback) {
synchronized (this) {
Request req = new Request(callback, this);
@@ -832,6 +836,12 @@
return false;
}
+ /**
+ * Called when the user presses the back button while focus is in the session UI. Note
+ * that this will only happen if the session UI has requested input focus in its window;
+ * otherwise, the back key will go to whatever window has focus and do whatever behavior
+ * it normally has there.
+ */
public void onBackPressed() {
hide();
}
diff --git a/core/java/com/android/internal/app/DumpHeapActivity.java b/core/java/com/android/internal/app/DumpHeapActivity.java
index 7e70b0c..0ce501e 100644
--- a/core/java/com/android/internal/app/DumpHeapActivity.java
+++ b/core/java/com/android/internal/app/DumpHeapActivity.java
@@ -17,13 +17,16 @@
package com.android.internal.app;
import android.app.Activity;
+import android.app.ActivityManager;
import android.app.AlertDialog;
+import android.content.ActivityNotFoundException;
import android.content.ClipData;
import android.content.DialogInterface;
import android.content.Intent;
import android.net.Uri;
import android.os.Bundle;
import android.util.DebugUtils;
+import android.util.Slog;
/**
* This activity is displayed when the system has collected a heap dump from
@@ -34,6 +37,8 @@
public static final String KEY_PROCESS = "process";
/** The size limit the process reached */
public static final String KEY_SIZE = "size";
+ /** Optional name of package to directly launch */
+ public static final String KEY_DIRECT_LAUNCH = "direct_launch";
// Broadcast action to determine when to delete the current dump heap data.
public static final String ACTION_DELETE_DUMPHEAP = "com.android.server.am.DELETE_DUMPHEAP";
@@ -54,6 +59,28 @@
mProcess = getIntent().getStringExtra(KEY_PROCESS);
mSize = getIntent().getLongExtra(KEY_SIZE, 0);
+
+ String directLaunch = getIntent().getStringExtra(KEY_DIRECT_LAUNCH);
+ if (directLaunch != null) {
+ Intent intent = new Intent(ActivityManager.ACTION_REPORT_HEAP_LIMIT);
+ intent.setPackage(directLaunch);
+ ClipData clip = ClipData.newUri(getContentResolver(), "Heap Dump", JAVA_URI);
+ intent.setClipData(clip);
+ intent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION);
+ intent.setType(clip.getDescription().getMimeType(0));
+ intent.putExtra(Intent.EXTRA_STREAM, JAVA_URI);
+ try {
+ startActivity(intent);
+ scheduleDelete();
+ mHandled = true;
+ finish();
+ return;
+ } catch (ActivityNotFoundException e) {
+ Slog.i("DumpHeapActivity", "Unable to direct launch to " + directLaunch
+ + ": " + e.getMessage());
+ }
+ }
+
AlertDialog.Builder b = new AlertDialog.Builder(this,
android.R.style.Theme_Material_Light_Dialog_Alert);
b.setTitle(com.android.internal.R.string.dump_heap_title);
@@ -71,9 +98,7 @@
@Override
public void onClick(DialogInterface dialog, int which) {
mHandled = true;
- Intent broadcast = new Intent(ACTION_DELETE_DUMPHEAP);
- broadcast.putExtra(EXTRA_DELAY_DELETE, true);
- sendBroadcast(broadcast);
+ scheduleDelete();
Intent intent = new Intent(Intent.ACTION_SEND);
ClipData clip = ClipData.newUri(getContentResolver(), "Heap Dump", JAVA_URI);
intent.setClipData(clip);
@@ -88,6 +113,12 @@
mDialog = b.show();
}
+ void scheduleDelete() {
+ Intent broadcast = new Intent(ACTION_DELETE_DUMPHEAP);
+ broadcast.putExtra(EXTRA_DELAY_DELETE, true);
+ sendBroadcast(broadcast);
+ }
+
@Override
protected void onStop() {
super.onStop();
diff --git a/services/core/java/com/android/server/am/ActivityManagerService.java b/services/core/java/com/android/server/am/ActivityManagerService.java
index cdaa5a3..8895a15 100644
--- a/services/core/java/com/android/server/am/ActivityManagerService.java
+++ b/services/core/java/com/android/server/am/ActivityManagerService.java
@@ -1124,7 +1124,7 @@
boolean mAutoStopProfiler = false;
int mProfileType = 0;
String mOpenGlTraceApp = null;
- final ArrayMap<String, Long> mMemWatchProcesses = new ArrayMap<>();
+ final ProcessMap<Pair<Long, String>> mMemWatchProcesses = new ProcessMap<>();
String mMemWatchDumpProcName;
String mMemWatchDumpFile;
int mMemWatchDumpPid;
@@ -1830,11 +1830,21 @@
final String procName;
final int uid;
final long memLimit;
+ final String reportPackage;
synchronized (ActivityManagerService.this) {
procName = mMemWatchDumpProcName;
uid = mMemWatchDumpUid;
- Long limit = mMemWatchProcesses.get(procName);
- memLimit = limit != null ? limit : 0;
+ Pair<Long, String> val = mMemWatchProcesses.get(procName, uid);
+ if (val == null) {
+ val = mMemWatchProcesses.get(procName, 0);
+ }
+ if (val != null) {
+ memLimit = val.first;
+ reportPackage = val.second;
+ } else {
+ memLimit = 0;
+ reportPackage = null;
+ }
}
if (procName == null) {
return;
@@ -1867,6 +1877,9 @@
intent.setClassName("android", DumpHeapActivity.class.getName());
intent.putExtra(DumpHeapActivity.KEY_PROCESS, procName);
intent.putExtra(DumpHeapActivity.KEY_SIZE, memLimit);
+ if (reportPackage != null) {
+ intent.putExtra(DumpHeapActivity.KEY_DIRECT_LAUNCH, reportPackage);
+ }
int userId = UserHandle.getUserId(uid);
notification.setLatestEventInfo(mContext, text,
mContext.getText(R.string.dump_heap_notification_detail),
@@ -2474,11 +2487,19 @@
final void setFocusedActivityLocked(ActivityRecord r, String reason) {
if (r != null && mFocusedActivity != r) {
if (DEBUG_FOCUS) Slog.d(TAG_FOCUS, "setFocusedActivityLocked: r=" + r);
+ ActivityRecord last = mFocusedActivity;
mFocusedActivity = r;
if (r.task != null && r.task.voiceInteractor != null) {
startRunningVoiceLocked(r.task.voiceSession, r.info.applicationInfo.uid);
} else {
finishRunningVoiceLocked();
+ if (last != null && last.task.voiceSession != null) {
+ // We had been in a voice interaction session, but now focused has
+ // move to something different. Just finish the session, we can't
+ // return to it and retain the proper state and synchronization with
+ // the voice interaction service.
+ finishVoiceTask(last.task.voiceSession);
+ }
}
if (mStackSupervisor.setFocusedStack(r, reason + " setFocusedActivity")) {
mWindowManager.setFocusedApp(r.appToken, true);
@@ -12856,16 +12877,28 @@
+ " mOrigWaitForDebugger=" + mOrigWaitForDebugger);
}
}
- if (mMemWatchProcesses.size() > 0) {
+ if (mMemWatchProcesses.getMap().size() > 0) {
pw.println(" Mem watch processes:");
- for (int i=0; i<mMemWatchProcesses.size(); i++) {
- if (needSep) {
- pw.println();
- needSep = false;
+ final ArrayMap<String, SparseArray<Pair<Long, String>>> procs
+ = mMemWatchProcesses.getMap();
+ for (int i=0; i<procs.size(); i++) {
+ final String proc = procs.keyAt(i);
+ final SparseArray<Pair<Long, String>> uids = procs.valueAt(i);
+ for (int j=0; j<uids.size(); j++) {
+ if (needSep) {
+ pw.println();
+ needSep = false;
+ }
+ StringBuilder sb = new StringBuilder();
+ sb.append(" ").append(proc).append('/');
+ UserHandle.formatUid(sb, uids.keyAt(j));
+ Pair<Long, String> val = uids.valueAt(i);
+ sb.append(": "); DebugUtils.sizeValueToString(val.first, sb);
+ if (val.second != null) {
+ sb.append(", report to ").append(val.second);
+ }
+ pw.println(sb.toString());
}
- pw.print(" "); pw.print(mMemWatchProcesses.keyAt(i));
- pw.print(": "); DebugUtils.printSizeValue(pw, mMemWatchProcesses.valueAt(i));
- pw.println();
}
pw.print(" mMemWatchDumpProcName="); pw.println(mMemWatchDumpProcName);
pw.print(" mMemWatchDumpFile="); pw.println(mMemWatchDumpFile);
@@ -17390,7 +17423,18 @@
proc.lastCachedPss = pss;
}
- Long check = mMemWatchProcesses.get(proc.processName);
+ final SparseArray<Pair<Long, String>> watchUids
+ = mMemWatchProcesses.getMap().get(proc.processName);
+ Long check = null;
+ if (watchUids != null) {
+ Pair<Long, String> val = watchUids.get(proc.uid);
+ if (val == null) {
+ val = watchUids.get(0);
+ }
+ if (val != null) {
+ check = val.first;
+ }
+ }
if (check != null) {
if ((pss * 1024) >= check && proc.thread != null && mMemWatchDumpProcName == null) {
boolean isDebuggable = "1".equals(SystemProperties.get(SYSTEM_DEBUGGABLE, "0"));
@@ -17426,7 +17470,8 @@
IApplicationThread thread = myProc.thread;
if (thread != null) {
try {
- if (DEBUG_PSS) Slog.d(TAG_PSS, "Requesting dump heap from "
+ if (true || DEBUG_PSS) Slog.d(TAG_PSS,
+ "Requesting dump heap from "
+ myProc + " to " + heapdumpFile);
thread.dumpHeap(true, heapdumpFile.toString(), fd);
} catch (RemoteException e) {
@@ -18661,15 +18706,38 @@
}
@Override
- public void setDumpHeapDebugLimit(String processName, long maxMemSize) {
- enforceCallingPermission(android.Manifest.permission.SET_DEBUG_APP,
- "setDumpHeapDebugLimit()");
+ public void setDumpHeapDebugLimit(String processName, int uid, long maxMemSize,
+ String reportPackage) {
+ if (processName != null) {
+ enforceCallingPermission(android.Manifest.permission.SET_DEBUG_APP,
+ "setDumpHeapDebugLimit()");
+ } else {
+ if (!Build.IS_DEBUGGABLE) {
+ throw new SecurityException("Not running a debuggable build");
+ }
+ synchronized (mPidsSelfLocked) {
+ ProcessRecord proc = mPidsSelfLocked.get(Binder.getCallingPid());
+ if (proc == null) {
+ throw new SecurityException("No process found for calling pid "
+ + Binder.getCallingPid());
+ }
+ processName = proc.processName;
+ uid = proc.uid;
+ if (reportPackage != null && !proc.pkgList.containsKey(reportPackage)) {
+ throw new SecurityException("Package " + reportPackage + " is not running in "
+ + proc);
+ }
+ }
+ }
synchronized (this) {
if (maxMemSize > 0) {
- mMemWatchProcesses.put(processName, maxMemSize);
- mHandler.sendEmptyMessage(POST_DUMP_HEAP_NOTIFICATION_MSG);
+ mMemWatchProcesses.put(processName, uid, new Pair(maxMemSize, reportPackage));
} else {
- mMemWatchProcesses.remove(processName);
+ if (uid != 0) {
+ mMemWatchProcesses.remove(processName, uid);
+ } else {
+ mMemWatchProcesses.getMap().remove(processName);
+ }
}
}
}
@@ -18687,7 +18755,7 @@
+ " does not match last path " + mMemWatchDumpFile);
return;
}
- if (DEBUG_PSS) Slog.d(TAG_PSS, "Dump heap finished for " + path);
+ if (true || DEBUG_PSS) Slog.d(TAG_PSS, "Dump heap finished for " + path);
mHandler.sendEmptyMessage(POST_DUMP_HEAP_NOTIFICATION_MSG);
}
}
diff --git a/services/voiceinteraction/java/com/android/server/voiceinteraction/VoiceInteractionManagerServiceImpl.java b/services/voiceinteraction/java/com/android/server/voiceinteraction/VoiceInteractionManagerServiceImpl.java
index 1aa0d0b..bca757b 100644
--- a/services/voiceinteraction/java/com/android/server/voiceinteraction/VoiceInteractionManagerServiceImpl.java
+++ b/services/voiceinteraction/java/com/android/server/voiceinteraction/VoiceInteractionManagerServiceImpl.java
@@ -145,7 +145,10 @@
}
public boolean hideSessionLocked(int callingPid, int callingUid) {
- return mActiveSession.hideLocked();
+ if (mActiveSession != null) {
+ return mActiveSession.hideLocked();
+ }
+ return false;
}
public boolean deliverNewSessionLocked(int callingPid, int callingUid, IBinder token,
@@ -165,6 +168,10 @@
Slog.w(TAG, "startVoiceActivity does not match active session");
return ActivityManager.START_CANCELED;
}
+ if (!mActiveSession.mShown) {
+ Slog.w(TAG, "startVoiceActivity not allowed on hidden session");
+ return ActivityManager.START_CANCELED;
+ }
intent = new Intent(intent);
intent.addCategory(Intent.CATEGORY_VOICE);
intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_MULTIPLE_TASK);
diff --git a/services/voiceinteraction/java/com/android/server/voiceinteraction/VoiceInteractionSessionConnection.java b/services/voiceinteraction/java/com/android/server/voiceinteraction/VoiceInteractionSessionConnection.java
index 73c7363..607df2d 100644
--- a/services/voiceinteraction/java/com/android/server/voiceinteraction/VoiceInteractionSessionConnection.java
+++ b/services/voiceinteraction/java/com/android/server/voiceinteraction/VoiceInteractionSessionConnection.java
@@ -18,6 +18,7 @@
import android.app.ActivityManager;
import android.app.ActivityManagerNative;
+import android.app.AppOpsManager;
import android.app.AssistContent;
import android.app.IActivityManager;
import android.content.ClipData;
@@ -62,6 +63,7 @@
final int mCallingUid;
final IActivityManager mAm;
final IWindowManager mIWindowManager;
+ final AppOpsManager mAppOps;
final IBinder mPermissionOwner;
boolean mShown;
Bundle mShowArgs;
@@ -148,6 +150,7 @@
mAm = ActivityManagerNative.getDefault();
mIWindowManager = IWindowManager.Stub.asInterface(
ServiceManager.getService(Context.WINDOW_SERVICE));
+ mAppOps = context.getSystemService(AppOpsManager.class);
IBinder permOwner = null;
try {
permOwner = mAm.newUriPermissionOwner("voicesession:"
@@ -159,7 +162,8 @@
mBindIntent = new Intent(VoiceInteractionService.SERVICE_INTERFACE);
mBindIntent.setComponent(mSessionComponentName);
mBound = mContext.bindServiceAsUser(mBindIntent, this,
- Context.BIND_AUTO_CREATE|Context.BIND_ALLOW_OOM_MANAGEMENT, new UserHandle(mUser));
+ Context.BIND_AUTO_CREATE|Context.BIND_WAIVE_PRIORITY
+ |Context.BIND_ALLOW_OOM_MANAGEMENT, new UserHandle(mUser));
if (mBound) {
try {
mIWindowManager.addWindowToken(mToken,
@@ -186,19 +190,31 @@
mShowFlags = flags;
mHaveAssistData = false;
if ((flags&VoiceInteractionService.START_WITH_ASSIST) != 0) {
- try {
- mAm.requestAssistContextExtras(ActivityManager.ASSIST_CONTEXT_FULL,
- mAssistReceiver);
- } catch (RemoteException e) {
+ if (mAppOps.noteOpNoThrow(AppOpsManager.OP_ASSIST_STRUCTURE, mCallingUid,
+ mSessionComponentName.getPackageName()) == AppOpsManager.MODE_ALLOWED) {
+ try {
+ mAm.requestAssistContextExtras(ActivityManager.ASSIST_CONTEXT_FULL,
+ mAssistReceiver);
+ } catch (RemoteException e) {
+ }
+ } else {
+ mHaveAssistData = true;
+ mAssistData = null;
}
} else {
mAssistData = null;
}
mHaveScreenshot = false;
if ((flags&VoiceInteractionService.START_WITH_SCREENSHOT) != 0) {
- try {
- mIWindowManager.requestAssistScreenshot(mScreenshotReceiver);
- } catch (RemoteException e) {
+ if (mAppOps.noteOpNoThrow(AppOpsManager.OP_ASSIST_SCREENSHOT, mCallingUid,
+ mSessionComponentName.getPackageName()) == AppOpsManager.MODE_ALLOWED) {
+ try {
+ mIWindowManager.requestAssistScreenshot(mScreenshotReceiver);
+ } catch (RemoteException e) {
+ }
+ } else {
+ mHaveScreenshot = true;
+ mScreenshot = null;
}
} else {
mScreenshot = null;
@@ -335,6 +351,12 @@
mUser);
} catch (RemoteException e) {
}
+ if (mSession != null) {
+ try {
+ mAm.finishVoiceTask(mSession);
+ } catch (RemoteException e) {
+ }
+ }
}
if (mFullyBound) {
mContext.unbindService(mFullConnection);
diff --git a/tests/VoiceInteraction/res/layout/test_interaction.xml b/tests/VoiceInteraction/res/layout/test_interaction.xml
index 8c8151d..6209bd08 100644
--- a/tests/VoiceInteraction/res/layout/test_interaction.xml
+++ b/tests/VoiceInteraction/res/layout/test_interaction.xml
@@ -34,32 +34,49 @@
android:textAppearance="?android:attr/textAppearanceMedium"
/>
- <Button android:id="@+id/complete"
- android:layout_width="wrap_content"
+ <LinearLayout android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginTop="16dp"
- android:text="@string/completeVoice"
+ android:orientation="horizontal">
+
+ <Button android:id="@+id/complete"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:text="@string/completeVoice"
+ />
+
+ <Button android:id="@+id/abort"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:text="@string/abortVoice"
+ />
+
+ <Button android:id="@+id/pick"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:text="@string/pickVoice"
+ />
+
+ </LinearLayout>
+
+ <LinearLayout android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:layout_marginTop="16dp"
+ android:layout_marginBottom="16dp"
+ android:orientation="horizontal">
+
+ <Button android:id="@+id/cancel"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:text="@string/cancelVoice"
/>
- <Button android:id="@+id/pick"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:layout_marginTop="16dp"
- android:text="@string/pickVoice"
+ <Button android:id="@+id/jump"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:text="@string/jumpOut"
/>
- <Button android:id="@+id/abort"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:layout_marginTop="16dp"
- android:text="@string/abortVoice"
- />
-
- <Button android:id="@+id/cancel"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:layout_marginTop="16dp"
- android:text="@string/cancelVoice"
- />
+ </LinearLayout>
</LinearLayout>
diff --git a/tests/VoiceInteraction/res/values/strings.xml b/tests/VoiceInteraction/res/values/strings.xml
index 942c931..6289929 100644
--- a/tests/VoiceInteraction/res/values/strings.xml
+++ b/tests/VoiceInteraction/res/values/strings.xml
@@ -25,5 +25,6 @@
<string name="completeVoice">Complete Voice</string>
<string name="pickVoice">Pick Voice</string>
<string name="cancelVoice">Cancel</string>
+ <string name="jumpOut">Jump out</string>
</resources>
diff --git a/tests/VoiceInteraction/src/com/android/test/voiceinteraction/MainInteractionSession.java b/tests/VoiceInteraction/src/com/android/test/voiceinteraction/MainInteractionSession.java
index ec727c4..3c5c201 100644
--- a/tests/VoiceInteraction/src/com/android/test/voiceinteraction/MainInteractionSession.java
+++ b/tests/VoiceInteraction/src/com/android/test/voiceinteraction/MainInteractionSession.java
@@ -16,6 +16,7 @@
package com.android.test.voiceinteraction;
+import android.app.ActivityManager;
import android.app.AssistContent;
import android.app.AssistStructure;
import android.app.VoiceInteractor;
@@ -69,6 +70,8 @@
@Override
public void onCreate(Bundle args, int startFlags) {
super.onCreate(args);
+ ActivityManager am = getContext().getSystemService(ActivityManager.class);
+ am.setWatchHeapLimit(40*1024*1024);
}
@Override
diff --git a/tests/VoiceInteraction/src/com/android/test/voiceinteraction/TestInteractionActivity.java b/tests/VoiceInteraction/src/com/android/test/voiceinteraction/TestInteractionActivity.java
index e195c30..9d24c59 100644
--- a/tests/VoiceInteraction/src/com/android/test/voiceinteraction/TestInteractionActivity.java
+++ b/tests/VoiceInteraction/src/com/android/test/voiceinteraction/TestInteractionActivity.java
@@ -19,6 +19,7 @@
import android.app.Activity;
import android.app.VoiceInteractor;
import android.content.ComponentName;
+import android.content.Intent;
import android.os.Bundle;
import android.service.voice.VoiceInteractionService;
import android.util.Log;
@@ -37,6 +38,7 @@
Button mAbortButton;
Button mCompleteButton;
Button mPickButton;
+ Button mJumpOutButton;
Button mCancelButton;
@Override
@@ -64,6 +66,8 @@
mCompleteButton.setOnClickListener(this);
mPickButton = (Button)findViewById(R.id.pick);
mPickButton.setOnClickListener(this);
+ mJumpOutButton = (Button)findViewById(R.id.jump);
+ mJumpOutButton.setOnClickListener(this);
mCancelButton = (Button)findViewById(R.id.cancel);
mCancelButton.setOnClickListener(this);
@@ -165,6 +169,13 @@
}
};
mInteractor.submitRequest(req);
+ } else if (v == mJumpOutButton) {
+ Log.i(TAG, "Jump out");
+ Intent intent = new Intent(Intent.ACTION_MAIN);
+ intent.addCategory(Intent.CATEGORY_LAUNCHER);
+ intent.setComponent(new ComponentName(this, VoiceInteractionMain.class));
+ intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
+ startActivity(intent);
} else if (v == mCancelButton && mCurrentRequest != null) {
Log.i(TAG, "Cancel request");
mCurrentRequest.cancel();