Merge changes I61178def,I536e8934
* changes:
Flip feature flag to enable StrictMode defaults.
Narrower StrictMode defaults.
diff --git a/core/java/android/app/slice/Slice.java b/core/java/android/app/slice/Slice.java
index f6b6b86..1ce89d2 100644
--- a/core/java/android/app/slice/Slice.java
+++ b/core/java/android/app/slice/Slice.java
@@ -21,6 +21,7 @@
import android.annotation.StringDef;
import android.app.PendingIntent;
import android.app.RemoteInput;
+import android.app.slice.widget.SliceView;
import android.content.ContentResolver;
import android.content.IContentProvider;
import android.graphics.drawable.Icon;
@@ -54,7 +55,12 @@
public @interface SliceHint{ }
/**
- * Hint that this content is a title of other content in the slice.
+ * Hint that this content is a title of other content in the slice. This can also indicate that
+ * the content should be used in the shortcut representation of the slice (icon, label, action),
+ * normally this should be indicated by adding the hint on the action containing that content.
+ *
+ * @see SliceView#MODE_SHORTCUT
+ * @see SliceItem#TYPE_ACTION
*/
public static final String HINT_TITLE = "title";
/**
@@ -100,6 +106,13 @@
*/
public static final String HINT_NO_TINT = "no_tint";
/**
+ * Hint to indicate that this content should not be shown in the {@link SliceView#MODE_SMALL}
+ * and {@link SliceView#MODE_LARGE} modes of SliceView. This content may be used to populate
+ * the {@link SliceView#MODE_SHORTCUT} format of the slice.
+ * @hide
+ */
+ public static final String HINT_HIDDEN = "hidden";
+ /**
* Hint to indicate that this slice is incomplete and an update will be sent once
* loading is complete. Slices which contain HINT_PARTIAL will not be cached by the
* OS and should not be cached by apps.
diff --git a/core/java/android/app/slice/widget/GridView.java b/core/java/android/app/slice/widget/GridView.java
index 67a3c67..793abc0 100644
--- a/core/java/android/app/slice/widget/GridView.java
+++ b/core/java/android/app/slice/widget/GridView.java
@@ -126,6 +126,9 @@
* Returns true if this item is just an image.
*/
private boolean addItem(SliceItem item) {
+ if (item.hasHint(Slice.HINT_HIDDEN)) {
+ return false;
+ }
if (item.getType() == SliceItem.TYPE_IMAGE) {
ImageView v = new ImageView(getContext());
v.setImageIcon(item.getIcon());
@@ -145,6 +148,9 @@
items.addAll(item.getSlice().getItems());
}
items.forEach(i -> {
+ if (i.hasHint(Slice.HINT_HIDDEN)) {
+ return;
+ }
Context context = getContext();
switch (i.getType()) {
case SliceItem.TYPE_TEXT:
diff --git a/core/java/android/app/slice/widget/LargeTemplateView.java b/core/java/android/app/slice/widget/LargeTemplateView.java
index f45b2a8..788f6fb 100644
--- a/core/java/android/app/slice/widget/LargeTemplateView.java
+++ b/core/java/android/app/slice/widget/LargeTemplateView.java
@@ -85,9 +85,14 @@
addList(slice, items);
} else {
slice.getItems().forEach(item -> {
- if (item.hasHint(Slice.HINT_ACTIONS)) {
+ if (item.hasHint(Slice.HINT_HIDDEN)) {
+ // If it's hidden we don't show it
+ return;
+ } else if (item.hasHint(Slice.HINT_ACTIONS)) {
+ // Action groups don't show in lists
return;
} else if (item.getType() == SliceItem.TYPE_COLOR) {
+ // A color is not a list item
return;
} else if (item.getType() == SliceItem.TYPE_SLICE
&& item.hasHint(Slice.HINT_LIST)) {
@@ -108,8 +113,12 @@
private void addList(Slice slice, List<SliceItem> items) {
List<SliceItem> sliceItems = slice.getItems();
- sliceItems.forEach(i -> i.addHint(Slice.HINT_LIST_ITEM));
- items.addAll(sliceItems);
+ sliceItems.forEach(i -> {
+ if (!i.hasHint(Slice.HINT_HIDDEN) && i.getType() != SliceItem.TYPE_COLOR) {
+ i.addHint(Slice.HINT_LIST_ITEM);
+ items.add(i);
+ }
+ });
}
/**
diff --git a/core/java/android/app/slice/widget/ShortcutView.java b/core/java/android/app/slice/widget/ShortcutView.java
index 0bca8ce..0b7ad0d 100644
--- a/core/java/android/app/slice/widget/ShortcutView.java
+++ b/core/java/android/app/slice/widget/ShortcutView.java
@@ -24,13 +24,20 @@
import android.app.slice.widget.SliceView.SliceModeView;
import android.content.Context;
import android.content.Intent;
+import android.content.pm.ApplicationInfo;
+import android.content.pm.PackageManager;
+import android.content.pm.ProviderInfo;
+import android.content.res.Resources;
import android.graphics.Color;
+import android.graphics.drawable.Drawable;
import android.graphics.drawable.ShapeDrawable;
import android.graphics.drawable.shapes.OvalShape;
import android.net.Uri;
import com.android.internal.R;
+import java.util.List;
+
/**
* @hide
*/
@@ -38,27 +45,26 @@
private static final String TAG = "ShortcutView";
- private PendingIntent mAction;
private Uri mUri;
+ private PendingIntent mAction;
+ private SliceItem mLabel;
+ private SliceItem mIcon;
+
private int mLargeIconSize;
private int mSmallIconSize;
public ShortcutView(Context context) {
super(context);
- mSmallIconSize = getContext().getResources().getDimensionPixelSize(R.dimen.slice_icon_size);
+ final Resources res = getResources();
+ mSmallIconSize = res.getDimensionPixelSize(R.dimen.slice_icon_size);
+ mLargeIconSize = res.getDimensionPixelSize(R.dimen.slice_shortcut_size);
}
@Override
public void setSlice(Slice slice) {
removeAllViews();
- SliceItem sliceItem = SliceQuery.find(slice, SliceItem.TYPE_ACTION);
- SliceItem iconItem = SliceQuery.getPrimaryIcon(slice);
- SliceItem textItem = sliceItem != null
- ? SliceQuery.find(sliceItem, SliceItem.TYPE_TEXT)
- : SliceQuery.find(slice, SliceItem.TYPE_TEXT);
- SliceItem colorItem = sliceItem != null
- ? SliceQuery.find(sliceItem, SliceItem.TYPE_COLOR)
- : SliceQuery.find(slice, SliceItem.TYPE_COLOR);
+ determineShortcutItems(getContext(), slice);
+ SliceItem colorItem = SliceQuery.find(slice, SliceItem.TYPE_COLOR);
if (colorItem == null) {
colorItem = SliceQuery.find(slice, SliceItem.TYPE_COLOR);
}
@@ -67,13 +73,11 @@
ShapeDrawable circle = new ShapeDrawable(new OvalShape());
circle.setTint(color);
setBackground(circle);
- if (iconItem != null) {
- final boolean isLarge = iconItem.hasHint(Slice.HINT_LARGE);
+ if (mIcon != null) {
+ final boolean isLarge = mIcon.hasHint(Slice.HINT_LARGE);
final int iconSize = isLarge ? mLargeIconSize : mSmallIconSize;
- SliceViewUtil.createCircledIcon(getContext(), color, iconSize, iconItem.getIcon(),
+ SliceViewUtil.createCircledIcon(getContext(), color, iconSize, mIcon.getIcon(),
isLarge, this /* parent */);
- mAction = sliceItem != null ? sliceItem.getAction()
- : null;
mUri = slice.getUri();
setClickable(true);
} else {
@@ -103,4 +107,69 @@
}
return true;
}
+
+ /**
+ * Looks at the slice and determines which items are best to use to compose the shortcut.
+ */
+ private void determineShortcutItems(Context context, Slice slice) {
+ List<String> h = slice.getHints();
+ SliceItem sliceItem = new SliceItem(slice, SliceItem.TYPE_SLICE,
+ h.toArray(new String[h.size()]));
+ SliceItem titleItem = SliceQuery.find(slice, SliceItem.TYPE_ACTION,
+ Slice.HINT_TITLE, null);
+
+ if (titleItem != null) {
+ // Preferred case: hinted action containing hinted image and text
+ mAction = titleItem.getAction();
+ mIcon = SliceQuery.find(titleItem.getSlice(), SliceItem.TYPE_IMAGE, Slice.HINT_TITLE,
+ null);
+ mLabel = SliceQuery.find(titleItem.getSlice(), SliceItem.TYPE_TEXT, Slice.HINT_TITLE,
+ null);
+ } else {
+ // No hinted action; just use the first one
+ SliceItem actionItem = SliceQuery.find(sliceItem, SliceItem.TYPE_ACTION, (String) null,
+ null);
+ mAction = (actionItem != null) ? actionItem.getAction() : null;
+ }
+ // First fallback: any hinted image and text
+ if (mIcon == null) {
+ mIcon = SliceQuery.find(sliceItem, SliceItem.TYPE_IMAGE, Slice.HINT_TITLE,
+ null);
+ }
+ if (mLabel == null) {
+ mLabel = SliceQuery.find(sliceItem, SliceItem.TYPE_TEXT, Slice.HINT_TITLE,
+ null);
+ }
+ // Second fallback: first image and text
+ if (mIcon == null) {
+ mIcon = SliceQuery.find(sliceItem, SliceItem.TYPE_IMAGE, (String) null,
+ null);
+ }
+ if (mLabel == null) {
+ mLabel = SliceQuery.find(sliceItem, SliceItem.TYPE_TEXT, (String) null,
+ null);
+ }
+ // Final fallback: use app info
+ if (mIcon == null || mLabel == null || mAction == null) {
+ PackageManager pm = context.getPackageManager();
+ ProviderInfo providerInfo = pm.resolveContentProvider(
+ slice.getUri().getAuthority(), 0);
+ ApplicationInfo appInfo = providerInfo.applicationInfo;
+ if (appInfo != null) {
+ if (mIcon == null) {
+ Drawable icon = appInfo.loadDefaultIcon(pm);
+ mIcon = new SliceItem(SliceViewUtil.createIconFromDrawable(icon),
+ SliceItem.TYPE_IMAGE, new String[] {Slice.HINT_LARGE});
+ }
+ if (mLabel == null) {
+ mLabel = new SliceItem(pm.getApplicationLabel(appInfo),
+ SliceItem.TYPE_TEXT, null);
+ }
+ if (mAction == null) {
+ mAction = PendingIntent.getActivity(context, 0,
+ pm.getLaunchIntentForPackage(appInfo.packageName), 0);
+ }
+ }
+ }
+ }
}
diff --git a/core/java/android/app/slice/widget/SliceView.java b/core/java/android/app/slice/widget/SliceView.java
index 5bafbc0..c583562 100644
--- a/core/java/android/app/slice/widget/SliceView.java
+++ b/core/java/android/app/slice/widget/SliceView.java
@@ -115,7 +115,9 @@
*/
public static final String MODE_LARGE = "SLICE_LARGE";
/**
- * Mode indicating this slice should be presented as an icon.
+ * Mode indicating this slice should be presented as an icon. A shortcut requires an intent,
+ * icon, and label. This can be indicated by using {@link Slice#HINT_TITLE} on an action in a
+ * slice.
*/
public static final String MODE_SHORTCUT = "SLICE_ICON";
diff --git a/core/java/android/app/slice/widget/SliceViewUtil.java b/core/java/android/app/slice/widget/SliceViewUtil.java
index 0366998..1cf0055 100644
--- a/core/java/android/app/slice/widget/SliceViewUtil.java
+++ b/core/java/android/app/slice/widget/SliceViewUtil.java
@@ -28,6 +28,7 @@
import android.graphics.PorterDuff.Mode;
import android.graphics.PorterDuffXfermode;
import android.graphics.Rect;
+import android.graphics.drawable.BitmapDrawable;
import android.graphics.drawable.Drawable;
import android.graphics.drawable.Icon;
import android.view.Gravity;
@@ -141,6 +142,21 @@
/**
* @hide
*/
+ public static Icon createIconFromDrawable(Drawable d) {
+ if (d instanceof BitmapDrawable) {
+ return Icon.createWithBitmap(((BitmapDrawable) d).getBitmap());
+ }
+ Bitmap b = Bitmap.createBitmap(d.getIntrinsicWidth(), d.getIntrinsicHeight(),
+ Bitmap.Config.ARGB_8888);
+ Canvas canvas = new Canvas(b);
+ d.setBounds(0, 0, canvas.getWidth(), canvas.getHeight());
+ d.draw(canvas);
+ return Icon.createWithBitmap(b);
+ }
+
+ /**
+ * @hide
+ */
public static void createCircledIcon(Context context, int color, int iconSize, Icon icon,
boolean isLarge, ViewGroup parent) {
ImageView v = new ImageView(context);
diff --git a/proto/src/metrics_constants.proto b/proto/src/metrics_constants.proto
index d138184..a3c927a 100644
--- a/proto/src/metrics_constants.proto
+++ b/proto/src/metrics_constants.proto
@@ -4671,6 +4671,11 @@
// OS: P
DIALOG_SIM_STATUS = 1246;
+ // OPEN: Settings > System > About Phone > Android Version
+ // CATEGORY: SETTINGS
+ // OS: P
+ DIALOG_FIRMWARE_VERSION = 1247;
+
// Add new aosp constants above this line.
// END OF AOSP CONSTANTS
}
diff --git a/services/core/java/com/android/server/BatteryService.java b/services/core/java/com/android/server/BatteryService.java
index ad30897..1b61866 100644
--- a/services/core/java/com/android/server/BatteryService.java
+++ b/services/core/java/com/android/server/BatteryService.java
@@ -56,6 +56,7 @@
import android.os.RemoteException;
import android.os.ServiceManager;
import android.os.SystemClock;
+import android.os.Trace;
import android.os.UEventObserver;
import android.os.UserHandle;
import android.provider.Settings;
@@ -246,6 +247,7 @@
}
private void registerHealthCallback() {
+ traceBegin("HealthInitWrapper");
mHealthServiceWrapper = new HealthServiceWrapper();
mHealthHalCallback = new HealthHalCallback();
// IHealth is lazily retrieved.
@@ -259,8 +261,11 @@
} catch (NoSuchElementException ex) {
Slog.e(TAG, "health: cannot register callback. (no supported health HAL service)");
throw ex;
+ } finally {
+ traceEnd();
}
+ traceBegin("HealthInitWaitUpdate");
// init register for new service notifications, and IServiceManager should return the
// existing service in a near future. Wait for this.update() to instantiate
// the initial mHealthInfo.
@@ -280,6 +285,7 @@
Slog.i(TAG, "health: Waited " + (SystemClock.uptimeMillis() - beforeWait)
+ "ms and received the update.");
+ traceEnd();
}
private void updateBatteryWarningLevelLocked() {
@@ -375,6 +381,7 @@
}
private void update(HealthInfo info) {
+ traceBegin("HealthInfoUpdate");
synchronized (mLock) {
if (!mUpdatesStopped) {
mHealthInfo = info;
@@ -385,6 +392,7 @@
copy(mLastHealthInfo, info);
}
}
+ traceEnd();
}
private static void copy(HealthInfo dst, HealthInfo src) {
@@ -932,6 +940,14 @@
proto.flush();
}
+ private static void traceBegin(String name) {
+ Trace.traceBegin(Trace.TRACE_TAG_SYSTEM_SERVER, name);
+ }
+
+ private static void traceEnd() {
+ Trace.traceEnd(Trace.TRACE_TAG_SYSTEM_SERVER);
+ }
+
private final class Led {
private final Light mBatteryLight;
@@ -997,6 +1013,7 @@
String instance) {
if (newService == null) return;
+ traceBegin("HealthUnregisterCallback");
try {
if (oldService != null) {
int r = oldService.unregisterCallback(this);
@@ -1008,8 +1025,11 @@
} catch (RemoteException ex) {
Slog.w(TAG, "health: cannot unregister previous callback (transaction error): "
+ ex.getMessage());
+ } finally {
+ traceEnd();
}
+ traceBegin("HealthRegisterCallback");
try {
int r = newService.registerCallback(this);
if (r != Result.SUCCESS) {
@@ -1022,6 +1042,8 @@
} catch (RemoteException ex) {
Slog.e(TAG, "health: cannot register callback (transaction error): "
+ ex.getMessage());
+ } finally {
+ traceEnd();
}
}
}
@@ -1054,53 +1076,63 @@
Slog.e(TAG, "health: must not call unregisterListener on battery properties");
}
public int getProperty(int id, final BatteryProperty prop) throws RemoteException {
- IHealth service = mHealthServiceWrapper.getLastService();
- if (service == null) throw new RemoteException("no health service");
- final MutableInt outResult = new MutableInt(Result.NOT_SUPPORTED);
- switch(id) {
- case BatteryManager.BATTERY_PROPERTY_CHARGE_COUNTER:
- service.getChargeCounter((int result, int value) -> {
- outResult.value = result;
- if (result == Result.SUCCESS) prop.setLong(value);
- });
- break;
- case BatteryManager.BATTERY_PROPERTY_CURRENT_NOW:
- service.getCurrentNow((int result, int value) -> {
- outResult.value = result;
- if (result == Result.SUCCESS) prop.setLong(value);
- });
- break;
- case BatteryManager.BATTERY_PROPERTY_CURRENT_AVERAGE:
- service.getCurrentAverage((int result, int value) -> {
- outResult.value = result;
- if (result == Result.SUCCESS) prop.setLong(value);
- });
- break;
- case BatteryManager.BATTERY_PROPERTY_CAPACITY:
- service.getCapacity((int result, int value) -> {
- outResult.value = result;
- if (result == Result.SUCCESS) prop.setLong(value);
- });
- break;
- case BatteryManager.BATTERY_PROPERTY_STATUS:
- service.getChargeStatus((int result, int value) -> {
- outResult.value = result;
- if (result == Result.SUCCESS) prop.setLong(value);
- });
- break;
- case BatteryManager.BATTERY_PROPERTY_ENERGY_COUNTER:
- service.getEnergyCounter((int result, long value) -> {
- outResult.value = result;
- if (result == Result.SUCCESS) prop.setLong(value);
- });
- break;
+ traceBegin("HealthGetProperty");
+ try {
+ IHealth service = mHealthServiceWrapper.getLastService();
+ if (service == null) throw new RemoteException("no health service");
+ final MutableInt outResult = new MutableInt(Result.NOT_SUPPORTED);
+ switch(id) {
+ case BatteryManager.BATTERY_PROPERTY_CHARGE_COUNTER:
+ service.getChargeCounter((int result, int value) -> {
+ outResult.value = result;
+ if (result == Result.SUCCESS) prop.setLong(value);
+ });
+ break;
+ case BatteryManager.BATTERY_PROPERTY_CURRENT_NOW:
+ service.getCurrentNow((int result, int value) -> {
+ outResult.value = result;
+ if (result == Result.SUCCESS) prop.setLong(value);
+ });
+ break;
+ case BatteryManager.BATTERY_PROPERTY_CURRENT_AVERAGE:
+ service.getCurrentAverage((int result, int value) -> {
+ outResult.value = result;
+ if (result == Result.SUCCESS) prop.setLong(value);
+ });
+ break;
+ case BatteryManager.BATTERY_PROPERTY_CAPACITY:
+ service.getCapacity((int result, int value) -> {
+ outResult.value = result;
+ if (result == Result.SUCCESS) prop.setLong(value);
+ });
+ break;
+ case BatteryManager.BATTERY_PROPERTY_STATUS:
+ service.getChargeStatus((int result, int value) -> {
+ outResult.value = result;
+ if (result == Result.SUCCESS) prop.setLong(value);
+ });
+ break;
+ case BatteryManager.BATTERY_PROPERTY_ENERGY_COUNTER:
+ service.getEnergyCounter((int result, long value) -> {
+ outResult.value = result;
+ if (result == Result.SUCCESS) prop.setLong(value);
+ });
+ break;
+ }
+ return outResult.value;
+ } finally {
+ traceEnd();
}
- return outResult.value;
}
public void scheduleUpdate() throws RemoteException {
- IHealth service = mHealthServiceWrapper.getLastService();
- if (service == null) throw new RemoteException("no health service");
- service.update();
+ traceBegin("HealthScheduleUpdate");
+ try {
+ IHealth service = mHealthServiceWrapper.getLastService();
+ if (service == null) throw new RemoteException("no health service");
+ service.update();
+ } finally {
+ traceEnd();
+ }
}
}
@@ -1203,15 +1235,27 @@
if (callback == null || managerSupplier == null || healthSupplier == null)
throw new NullPointerException();
+ IServiceManager manager;
+
mCallback = callback;
mHealthSupplier = healthSupplier;
- IServiceManager manager = managerSupplier.get();
+ traceBegin("HealthInitGetManager");
+ try {
+ manager = managerSupplier.get();
+ } finally {
+ traceEnd();
+ }
for (String name : sAllInstances) {
- if (manager.getTransport(IHealth.kInterfaceName, name) !=
- IServiceManager.Transport.EMPTY) {
- mInstanceName = name;
- break;
+ traceBegin("HealthInitGetTransport_" + name);
+ try {
+ if (manager.getTransport(IHealth.kInterfaceName, name) !=
+ IServiceManager.Transport.EMPTY) {
+ mInstanceName = name;
+ break;
+ }
+ } finally {
+ traceEnd();
}
}
@@ -1221,7 +1265,12 @@
sAllInstances.toString()));
}
- manager.registerForNotifications(IHealth.kInterfaceName, mInstanceName, mNotification);
+ traceBegin("HealthInitRegisterNotification");
+ try {
+ manager.registerForNotifications(IHealth.kInterfaceName, mInstanceName, mNotification);
+ } finally {
+ traceEnd();
+ }
Slog.i(TAG, "health: HealthServiceWrapper listening to instance " + mInstanceName);
}
diff --git a/services/core/java/com/android/server/am/ActivityManagerService.java b/services/core/java/com/android/server/am/ActivityManagerService.java
index fc4cb1a..1ee92b7 100644
--- a/services/core/java/com/android/server/am/ActivityManagerService.java
+++ b/services/core/java/com/android/server/am/ActivityManagerService.java
@@ -4741,7 +4741,9 @@
mWindowManager, appOpsManager, proxy, this,
OP_ASSIST_STRUCTURE, OP_NONE);
requester.requestAssistData(mStackSupervisor.getTopVisibleActivities(),
- true, false /* fetchScreenshots */, recentsUid, recentsPackage);
+ true /* fetchData */, false /* fetchScreenshots */,
+ true /* allowFetchData */, false /* alloweFetchScreenshots */,
+ recentsUid, recentsPackage);
}
final Intent intent = new Intent();
diff --git a/services/core/java/com/android/server/am/AssistDataReceiverProxy.java b/services/core/java/com/android/server/am/AssistDataReceiverProxy.java
index 8306731..f22fe37 100644
--- a/services/core/java/com/android/server/am/AssistDataReceiverProxy.java
+++ b/services/core/java/com/android/server/am/AssistDataReceiverProxy.java
@@ -37,17 +37,12 @@
private static final String TAG = TAG_WITH_CLASS_NAME ? "AssistDataReceiverProxy" : TAG_AM;
private String mCallerPackage;
- private boolean mBinderDied;
private IAssistDataReceiver mReceiver;
public AssistDataReceiverProxy(IAssistDataReceiver receiver, String callerPackage) {
- try {
- receiver.asBinder().linkToDeath(this, 0);
- } catch (RemoteException e) {
- Log.w(TAG, "Could not link to client death", e);
- }
mReceiver = receiver;
mCallerPackage = callerPackage;
+ linkToDeath();
}
@Override
@@ -58,7 +53,7 @@
@Override
public void onAssistDataReceivedLocked(Bundle data, int activityIndex, int activityCount) {
- if (!mBinderDied) {
+ if (mReceiver != null) {
try {
mReceiver.onHandleAssistData(data);
} catch (RemoteException e) {
@@ -70,7 +65,7 @@
@Override
public void onAssistScreenshotReceivedLocked(Bitmap screenshot) {
- if (!mBinderDied) {
+ if (mReceiver != null) {
try {
mReceiver.onHandleAssistScreenshot(screenshot);
} catch (RemoteException e) {
@@ -81,7 +76,27 @@
}
@Override
+ public void onAssistRequestCompleted() {
+ unlinkToDeath();
+ }
+
+ @Override
public void binderDied() {
- mBinderDied = true;
+ unlinkToDeath();
+ }
+
+ private void linkToDeath() {
+ try {
+ mReceiver.asBinder().linkToDeath(this, 0);
+ } catch (RemoteException e) {
+ Log.w(TAG, "Could not link to client death", e);
+ }
+ }
+
+ private void unlinkToDeath() {
+ if (mReceiver != null) {
+ mReceiver.asBinder().unlinkToDeath(this, 0);
+ }
+ mReceiver = null;
}
}
\ No newline at end of file
diff --git a/services/core/java/com/android/server/am/AssistDataRequester.java b/services/core/java/com/android/server/am/AssistDataRequester.java
index e32ff6e..a8f829f 100644
--- a/services/core/java/com/android/server/am/AssistDataRequester.java
+++ b/services/core/java/com/android/server/am/AssistDataRequester.java
@@ -94,6 +94,17 @@
*/
@GuardedBy("mCallbacksLock")
void onAssistScreenshotReceivedLocked(Bitmap screenshot);
+
+ /**
+ * Called when there is no more pending assist data or screenshots for the last request.
+ * If the request was canceled, then this callback will not be made. In addition, the
+ * callback will be made with the {@param mCallbacksLock} held, and only if
+ * {@link #canHandleReceivedAssistDataLocked()} is true.
+ */
+ @GuardedBy("mCallbacksLock")
+ default void onAssistRequestCompleted() {
+ // Do nothing
+ }
}
/**
@@ -131,15 +142,21 @@
* @param fetchScreenshot whether or not to fetch the screenshot, only applies if fetchData is
* true, the caller is allowed to fetch the assist data, and the current activity allows
* assist data to be fetched from it
+ * @param allowFetchData to be joined with other checks, determines whether or not the requester
+ * is allowed to fetch the assist data
+ * @param allowFetchScreenshot to be joined with other checks, determines whether or not the
+ * requester is allowed to fetch the assist screenshot
*/
- public void requestAssistData(List<IBinder> activityTokens, boolean fetchData,
- boolean fetchScreenshot, int callingUid, String callingPackage) {
- // TODO: Better handle the cancel case if a request can be reused
- // TODO: Known issue, if the assist data is not allowed on the current activity, then no
- // assist data is requested for any of the other activities
+ public void requestAssistData(List<IBinder> activityTokens, final boolean fetchData,
+ final boolean fetchScreenshot, boolean allowFetchData, boolean allowFetchScreenshot,
+ int callingUid, String callingPackage) {
+ // TODO(b/34090158): Known issue, if the assist data is not allowed on the current activity,
+ // then no assist data is requested for any of the other activities
// Early exit if there are no activity to fetch for
if (activityTokens.isEmpty()) {
+ // No activities, just dispatch request-complete
+ tryDispatchRequestComplete();
return;
}
@@ -150,8 +167,8 @@
} catch (RemoteException e) {
// Should never happen
}
- fetchData &= isAssistDataAllowed;
- fetchScreenshot &= fetchData && isAssistDataAllowed
+ allowFetchData &= isAssistDataAllowed;
+ allowFetchScreenshot &= fetchData && isAssistDataAllowed
&& (mRequestScreenshotAppOps != OP_NONE);
mCanceled = false;
@@ -162,7 +179,7 @@
if (fetchData) {
if (mAppOpsManager.checkOpNoThrow(mRequestStructureAppOps, callingUid, callingPackage)
- == MODE_ALLOWED) {
+ == MODE_ALLOWED && allowFetchData) {
final int numActivities = activityTokens.size();
for (int i = 0; i < numActivities; i++) {
IBinder topActivity = activityTokens.get(i);
@@ -177,8 +194,12 @@
mPendingDataCount++;
} else if (i == 0) {
// Wasn't allowed... given that, let's not do the screenshot either.
- dispatchAssistDataReceived(null);
- fetchScreenshot = false;
+ if (mCallbacks.canHandleReceivedAssistDataLocked()) {
+ dispatchAssistDataReceived(null);
+ } else {
+ mAssistData.add(null);
+ }
+ allowFetchScreenshot = false;
break;
}
} catch (RemoteException e) {
@@ -187,14 +208,18 @@
}
} else {
// Wasn't allowed... given that, let's not do the screenshot either.
- dispatchAssistDataReceived(null);
- fetchScreenshot = false;
+ if (mCallbacks.canHandleReceivedAssistDataLocked()) {
+ dispatchAssistDataReceived(null);
+ } else {
+ mAssistData.add(null);
+ }
+ allowFetchScreenshot = false;
}
}
if (fetchScreenshot) {
if (mAppOpsManager.checkOpNoThrow(mRequestScreenshotAppOps, callingUid, callingPackage)
- == MODE_ALLOWED) {
+ == MODE_ALLOWED && allowFetchScreenshot) {
try {
MetricsLogger.count(mContext, "assist_with_screen", 1);
mPendingScreenshotCount++;
@@ -203,24 +228,38 @@
// Can't happen
}
} else {
- dispatchAssistScreenshotReceived(null);
+ if (mCallbacks.canHandleReceivedAssistDataLocked()) {
+ dispatchAssistScreenshotReceived(null);
+ } else {
+ mAssistScreenshot.add(null);
+ }
}
}
+ // For the cases where we dispatch null data/screenshot due to permissions, just dispatch
+ // request-complete after those are made
+ tryDispatchRequestComplete();
}
/**
* This call should only be made when the callbacks are capable of handling the received assist
- * data.
+ * data. The owner is also responsible for locking before calling this method.
*/
public void processPendingAssistData() {
+ flushPendingAssistData();
+ tryDispatchRequestComplete();
+ }
+
+ private void flushPendingAssistData() {
final int dataCount = mAssistData.size();
for (int i = 0; i < dataCount; i++) {
dispatchAssistDataReceived(mAssistData.get(i));
}
+ mAssistData.clear();
final int screenshotsCount = mAssistScreenshot.size();
for (int i = 0; i < screenshotsCount; i++) {
dispatchAssistScreenshotReceived(mAssistScreenshot.get(i));
}
+ mAssistScreenshot.clear();
}
public int getPendingDataCount() {
@@ -254,8 +293,9 @@
if (mCallbacks.canHandleReceivedAssistDataLocked()) {
// Process any pending data and dispatch the new data as well
- processPendingAssistData();
+ flushPendingAssistData();
dispatchAssistDataReceived(data);
+ tryDispatchRequestComplete();
} else {
// Queue up the data for processing later
mAssistData.add(data);
@@ -273,8 +313,9 @@
if (mCallbacks.canHandleReceivedAssistDataLocked()) {
// Process any pending data and dispatch the new data as well
- processPendingAssistData();
+ flushPendingAssistData();
dispatchAssistScreenshotReceived(screenshot);
+ tryDispatchRequestComplete();
} else {
// Queue up the data for processing later
mAssistScreenshot.add(screenshot);
@@ -298,6 +339,13 @@
mCallbacks.onAssistScreenshotReceivedLocked(screenshot);
}
+ private void tryDispatchRequestComplete() {
+ if (mPendingDataCount == 0 && mPendingScreenshotCount == 0 &&
+ mAssistData.isEmpty() && mAssistScreenshot.isEmpty()) {
+ mCallbacks.onAssistRequestCompleted();
+ }
+ }
+
public void dump(String prefix, PrintWriter pw) {
pw.print(prefix); pw.print("mPendingDataCount="); pw.println(mPendingDataCount);
pw.print(prefix); pw.print("mAssistData="); pw.println(mAssistData);
diff --git a/services/core/java/com/android/server/locksettings/SyntheticPasswordCrypto.java b/services/core/java/com/android/server/locksettings/SyntheticPasswordCrypto.java
index b7bca1fb..ef94000 100644
--- a/services/core/java/com/android/server/locksettings/SyntheticPasswordCrypto.java
+++ b/services/core/java/com/android/server/locksettings/SyntheticPasswordCrypto.java
@@ -112,7 +112,7 @@
}
}
- public static byte[] decryptBlob(String keyAlias, byte[] blob, byte[] applicationId) {
+ public static byte[] decryptBlobV1(String keyAlias, byte[] blob, byte[] applicationId) {
try {
KeyStore keyStore = KeyStore.getInstance("AndroidKeyStore");
keyStore.load(null);
@@ -120,6 +120,20 @@
SecretKey decryptionKey = (SecretKey) keyStore.getKey(keyAlias, null);
byte[] intermediate = decrypt(applicationId, APPLICATION_ID_PERSONALIZATION, blob);
return decrypt(decryptionKey, intermediate);
+ } catch (Exception e) {
+ e.printStackTrace();
+ throw new RuntimeException("Failed to decrypt blob", e);
+ }
+ }
+
+ public static byte[] decryptBlob(String keyAlias, byte[] blob, byte[] applicationId) {
+ try {
+ KeyStore keyStore = KeyStore.getInstance("AndroidKeyStore");
+ keyStore.load(null);
+
+ SecretKey decryptionKey = (SecretKey) keyStore.getKey(keyAlias, null);
+ byte[] intermediate = decrypt(decryptionKey, blob);
+ return decrypt(applicationId, APPLICATION_ID_PERSONALIZATION, intermediate);
} catch (CertificateException | IOException | BadPaddingException
| IllegalBlockSizeException
| KeyStoreException | NoSuchPaddingException | NoSuchAlgorithmException
@@ -150,9 +164,8 @@
keyStore.setEntry(keyAlias,
new KeyStore.SecretKeyEntry(secretKey),
builder.build());
- byte[] intermediate = encrypt(secretKey, data);
- return encrypt(applicationId, APPLICATION_ID_PERSONALIZATION, intermediate);
-
+ byte[] intermediate = encrypt(applicationId, APPLICATION_ID_PERSONALIZATION, data);
+ return encrypt(secretKey, intermediate);
} catch (CertificateException | IOException | BadPaddingException
| IllegalBlockSizeException
| KeyStoreException | NoSuchPaddingException | NoSuchAlgorithmException
diff --git a/services/core/java/com/android/server/locksettings/SyntheticPasswordManager.java b/services/core/java/com/android/server/locksettings/SyntheticPasswordManager.java
index 9440f17..ca6c9e7 100644
--- a/services/core/java/com/android/server/locksettings/SyntheticPasswordManager.java
+++ b/services/core/java/com/android/server/locksettings/SyntheticPasswordManager.java
@@ -101,7 +101,8 @@
private static final byte WEAVER_VERSION = 1;
private static final int INVALID_WEAVER_SLOT = -1;
- private static final byte SYNTHETIC_PASSWORD_VERSION = 1;
+ private static final byte SYNTHETIC_PASSWORD_VERSION_V1 = 1;
+ private static final byte SYNTHETIC_PASSWORD_VERSION = 2;
private static final byte SYNTHETIC_PASSWORD_PASSWORD_BASED = 0;
private static final byte SYNTHETIC_PASSWORD_TOKEN_BASED = 1;
@@ -792,6 +793,7 @@
byte[] pwdToken = computePasswordToken(credential, pwd);
final byte[] applicationId;
+ final long sid;
int weaverSlot = loadWeaverSlot(handle, userId);
if (weaverSlot != INVALID_WEAVER_SLOT) {
// Weaver based user password
@@ -804,6 +806,7 @@
if (result.gkResponse.getResponseCode() != VerifyCredentialResponse.RESPONSE_OK) {
return result;
}
+ sid = GateKeeper.INVALID_SECURE_USER_ID;
applicationId = transformUnderWeaverSecret(pwdToken, result.gkResponse.getPayload());
} else {
byte[] gkPwdToken = passwordTokenToGkInput(pwdToken);
@@ -836,12 +839,13 @@
result.gkResponse = VerifyCredentialResponse.ERROR;
return result;
}
+ sid = sidFromPasswordHandle(pwd.passwordHandle);
applicationId = transformUnderSecdiscardable(pwdToken,
loadSecdiscardable(handle, userId));
}
result.authToken = unwrapSyntheticPasswordBlob(handle, SYNTHETIC_PASSWORD_PASSWORD_BASED,
- applicationId, userId);
+ applicationId, sid, userId);
// Perform verifyChallenge to refresh auth tokens for GK if user password exists.
result.gkResponse = verifyChallenge(gatekeeper, result.authToken, 0L, userId);
@@ -877,7 +881,7 @@
}
byte[] applicationId = transformUnderSecdiscardable(token, secdiscardable);
result.authToken = unwrapSyntheticPasswordBlob(handle, SYNTHETIC_PASSWORD_TOKEN_BASED,
- applicationId, userId);
+ applicationId, 0L, userId);
if (result.authToken != null) {
result.gkResponse = verifyChallenge(gatekeeper, result.authToken, 0L, userId);
if (result.gkResponse == null) {
@@ -892,19 +896,26 @@
}
private AuthenticationToken unwrapSyntheticPasswordBlob(long handle, byte type,
- byte[] applicationId, int userId) {
+ byte[] applicationId, long sid, int userId) {
byte[] blob = loadState(SP_BLOB_NAME, handle, userId);
if (blob == null) {
return null;
}
- if (blob[0] != SYNTHETIC_PASSWORD_VERSION) {
+ final byte version = blob[0];
+ if (version != SYNTHETIC_PASSWORD_VERSION && version != SYNTHETIC_PASSWORD_VERSION_V1) {
throw new RuntimeException("Unknown blob version");
}
if (blob[1] != type) {
throw new RuntimeException("Invalid blob type");
}
- byte[] secret = decryptSPBlob(getHandleName(handle),
+ final byte[] secret;
+ if (version == SYNTHETIC_PASSWORD_VERSION_V1) {
+ secret = SyntheticPasswordCrypto.decryptBlobV1(getHandleName(handle),
+ Arrays.copyOfRange(blob, 2, blob.length), applicationId);
+ } else {
+ secret = decryptSPBlob(getHandleName(handle),
Arrays.copyOfRange(blob, 2, blob.length), applicationId);
+ }
if (secret == null) {
Log.e(TAG, "Fail to decrypt SP for user " + userId);
return null;
@@ -919,6 +930,10 @@
} else {
result.syntheticPassword = new String(secret);
}
+ if (version == SYNTHETIC_PASSWORD_VERSION_V1) {
+ Log.i(TAG, "Upgrade v1 SP blob for user " + userId + ", type = " + type);
+ createSyntheticPasswordBlob(handle, type, result, applicationId, sid, userId);
+ }
return result;
}
diff --git a/services/core/java/com/android/server/wm/PinnedStackController.java b/services/core/java/com/android/server/wm/PinnedStackController.java
index 365366a..d8726bf 100644
--- a/services/core/java/com/android/server/wm/PinnedStackController.java
+++ b/services/core/java/com/android/server/wm/PinnedStackController.java
@@ -149,6 +149,9 @@
@Override
public void binderDied() {
// Clean up the state if the listener dies
+ if (mPinnedStackListener != null) {
+ mPinnedStackListener.asBinder().unlinkToDeath(mPinnedStackListenerDeathHandler, 0);
+ }
mPinnedStackListener = null;
}
}
diff --git a/services/tests/servicestests/src/com/android/server/am/AssistDataRequesterTest.java b/services/tests/servicestests/src/com/android/server/am/AssistDataRequesterTest.java
index bec46db..ce88d84 100644
--- a/services/tests/servicestests/src/com/android/server/am/AssistDataRequesterTest.java
+++ b/services/tests/servicestests/src/com/android/server/am/AssistDataRequesterTest.java
@@ -22,6 +22,7 @@
import static android.app.AppOpsManager.OP_ASSIST_STRUCTURE;
import static android.graphics.Bitmap.Config.ARGB_8888;
+import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertTrue;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.anyBoolean;
@@ -74,6 +75,8 @@
private static final boolean CALLER_ASSIST_SCREENSHOT_ALLOWED = true;
private static final boolean FETCH_DATA = true;
private static final boolean FETCH_SCREENSHOTS = true;
+ private static final boolean ALLOW_FETCH_DATA = true;
+ private static final boolean ALLOW_FETCH_SCREENSHOTS = true;
private static final int TEST_UID = 0;
private static final String TEST_PACKAGE = "";
@@ -128,8 +131,7 @@
mHandler.post(() -> {
try {
mGate.await(10, TimeUnit.SECONDS);
- mDataRequester.onHandleAssistScreenshot(Bitmap.createBitmap(1, 1,
- ARGB_8888));
+ mDataRequester.onHandleAssistScreenshot(Bitmap.createBitmap(1, 1, ARGB_8888));
} catch (InterruptedException e) {
Log.e(TAG, "Failed to wait", e);
}
@@ -153,7 +155,7 @@
CALLER_ASSIST_SCREENSHOT_ALLOWED);
mDataRequester.requestAssistData(createActivityList(5), FETCH_DATA, FETCH_SCREENSHOTS,
- TEST_UID, TEST_PACKAGE);
+ ALLOW_FETCH_DATA, ALLOW_FETCH_SCREENSHOTS, TEST_UID, TEST_PACKAGE);
assertReceivedDataCount(5, 5, 1, 1);
}
@@ -163,18 +165,18 @@
CALLER_ASSIST_SCREENSHOT_ALLOWED);
mDataRequester.requestAssistData(createActivityList(0), FETCH_DATA, FETCH_SCREENSHOTS,
- TEST_UID, TEST_PACKAGE);
+ ALLOW_FETCH_DATA, ALLOW_FETCH_SCREENSHOTS, TEST_UID, TEST_PACKAGE);
assertReceivedDataCount(0, 0, 0, 0);
}
@Test
- public void testCurrentAppDisallow_expectNoCallbacks() throws Exception {
+ public void testCurrentAppDisallow_expectNullCallbacks() throws Exception {
setupMocks(!CURRENT_ACTIVITY_ASSIST_ALLOWED, CALLER_ASSIST_STRUCTURE_ALLOWED,
CALLER_ASSIST_SCREENSHOT_ALLOWED);
mDataRequester.requestAssistData(createActivityList(5), FETCH_DATA, FETCH_SCREENSHOTS,
- TEST_UID, TEST_PACKAGE);
- assertReceivedDataCount(0, 0, 0, 0);
+ ALLOW_FETCH_DATA, ALLOW_FETCH_SCREENSHOTS, TEST_UID, TEST_PACKAGE);
+ assertReceivedDataCount(0, 1, 0, 1);
}
@Test
@@ -184,7 +186,7 @@
mCallbacks.canHandleReceivedData = false;
mDataRequester.requestAssistData(createActivityList(5), FETCH_DATA, FETCH_SCREENSHOTS,
- TEST_UID, TEST_PACKAGE);
+ ALLOW_FETCH_DATA, ALLOW_FETCH_SCREENSHOTS, TEST_UID, TEST_PACKAGE);
assertTrue(mDataRequester.getPendingDataCount() == 5);
assertTrue(mDataRequester.getPendingScreenshotCount() == 1);
mGate.countDown();
@@ -195,21 +197,32 @@
assertTrue(mDataRequester.getPendingScreenshotCount() == 0);
assertTrue(mCallbacks.receivedData.isEmpty());
assertTrue(mCallbacks.receivedScreenshots.isEmpty());
+ assertFalse(mCallbacks.requestCompleted);
mCallbacks.canHandleReceivedData = true;
mDataRequester.processPendingAssistData();
+ // Since we are posting the callback for the request-complete, flush the handler as well
+ mGate.countDown();
+ waitForIdle(mHandler);
assertTrue(mCallbacks.receivedData.size() == 5);
assertTrue(mCallbacks.receivedScreenshots.size() == 1);
+ assertTrue(mCallbacks.requestCompleted);
+
+ // Clear the state and ensure that we only process pending data once
+ mCallbacks.reset();
+ mDataRequester.processPendingAssistData();
+ assertTrue(mCallbacks.receivedData.isEmpty());
+ assertTrue(mCallbacks.receivedScreenshots.isEmpty());
}
@Test
- public void testNoFetchData_expectNoCallbacks() throws Exception {
+ public void testNoFetchData_expectNoDataCallbacks() throws Exception {
setupMocks(CURRENT_ACTIVITY_ASSIST_ALLOWED, CALLER_ASSIST_STRUCTURE_ALLOWED,
CALLER_ASSIST_SCREENSHOT_ALLOWED);
mDataRequester.requestAssistData(createActivityList(5), !FETCH_DATA, FETCH_SCREENSHOTS,
- TEST_UID, TEST_PACKAGE);
- assertReceivedDataCount(0, 0, 0, 0);
+ ALLOW_FETCH_DATA, ALLOW_FETCH_SCREENSHOTS, TEST_UID, TEST_PACKAGE);
+ assertReceivedDataCount(0, 0, 0, 1);
}
@Test
@@ -218,9 +231,9 @@
CALLER_ASSIST_SCREENSHOT_ALLOWED);
mDataRequester.requestAssistData(createActivityList(5), FETCH_DATA, FETCH_SCREENSHOTS,
- TEST_UID, TEST_PACKAGE);
+ ALLOW_FETCH_DATA, ALLOW_FETCH_SCREENSHOTS, TEST_UID, TEST_PACKAGE);
// Expect a single null data when the appops is denied
- assertReceivedDataCount(0, 1, 0, 0);
+ assertReceivedDataCount(0, 1, 0, 1);
}
@Test
@@ -231,9 +244,9 @@
anyBoolean(), anyBoolean());
mDataRequester.requestAssistData(createActivityList(5), FETCH_DATA, FETCH_SCREENSHOTS,
- TEST_UID, TEST_PACKAGE);
+ ALLOW_FETCH_DATA, ALLOW_FETCH_SCREENSHOTS, TEST_UID, TEST_PACKAGE);
// Expect a single null data when requestAssistContextExtras() fails
- assertReceivedDataCount(0, 1, 0, 0);
+ assertReceivedDataCount(0, 1, 0, 1);
}
@Test
@@ -242,7 +255,7 @@
CALLER_ASSIST_SCREENSHOT_ALLOWED);
mDataRequester.requestAssistData(createActivityList(5), FETCH_DATA, !FETCH_SCREENSHOTS,
- TEST_UID, TEST_PACKAGE);
+ ALLOW_FETCH_DATA, ALLOW_FETCH_SCREENSHOTS, TEST_UID, TEST_PACKAGE);
assertReceivedDataCount(5, 5, 0, 0);
}
@@ -252,11 +265,35 @@
!CALLER_ASSIST_SCREENSHOT_ALLOWED);
mDataRequester.requestAssistData(createActivityList(5), FETCH_DATA, FETCH_SCREENSHOTS,
- TEST_UID, TEST_PACKAGE);
+ ALLOW_FETCH_DATA, ALLOW_FETCH_SCREENSHOTS, TEST_UID, TEST_PACKAGE);
// Expect a single null screenshot when the appops is denied
assertReceivedDataCount(5, 5, 0, 1);
}
+ @Test
+ public void testCanNotHandleReceivedData_expectNoCallbacks() throws Exception {
+ setupMocks(CURRENT_ACTIVITY_ASSIST_ALLOWED, !CALLER_ASSIST_STRUCTURE_ALLOWED,
+ !CALLER_ASSIST_SCREENSHOT_ALLOWED);
+
+ mCallbacks.canHandleReceivedData = false;
+ mDataRequester.requestAssistData(createActivityList(5), FETCH_DATA, FETCH_SCREENSHOTS,
+ ALLOW_FETCH_DATA, ALLOW_FETCH_SCREENSHOTS, TEST_UID, TEST_PACKAGE);
+ mGate.countDown();
+ waitForIdle(mHandler);
+ assertTrue(mCallbacks.receivedData.isEmpty());
+ assertTrue(mCallbacks.receivedScreenshots.isEmpty());
+ }
+
+ @Test
+ public void testRequestDataNoneAllowed_expectNullCallbacks() throws Exception {
+ setupMocks(CURRENT_ACTIVITY_ASSIST_ALLOWED, CALLER_ASSIST_STRUCTURE_ALLOWED,
+ CALLER_ASSIST_SCREENSHOT_ALLOWED);
+
+ mDataRequester.requestAssistData(createActivityList(5), FETCH_DATA, FETCH_SCREENSHOTS,
+ !ALLOW_FETCH_DATA, !ALLOW_FETCH_SCREENSHOTS, TEST_UID, TEST_PACKAGE);
+ assertReceivedDataCount(0, 1, 0, 1);
+ }
+
private void assertReceivedDataCount(int numPendingData, int numReceivedData,
int numPendingScreenshots, int numReceivedScreenshots) throws Exception {
assertTrue("Expected " + numPendingData + " pending data, got "
@@ -265,6 +302,7 @@
assertTrue("Expected " + numPendingScreenshots + " pending screenshots, got "
+ mDataRequester.getPendingScreenshotCount(),
mDataRequester.getPendingScreenshotCount() == numPendingScreenshots);
+ assertFalse("Expected request NOT completed", mCallbacks.requestCompleted);
mGate.countDown();
waitForIdle(mHandler);
assertTrue("Expected " + numReceivedData + " data, received "
@@ -273,6 +311,7 @@
assertTrue("Expected " + numReceivedScreenshots + " screenshots, received "
+ mCallbacks.receivedScreenshots.size(),
mCallbacks.receivedScreenshots.size() == numReceivedScreenshots);
+ assertTrue("Expected request completed", mCallbacks.requestCompleted);
}
private List<IBinder> createActivityList(int size) {
@@ -292,12 +331,19 @@
latch.await(2, TimeUnit.SECONDS);
}
- private static class Callbacks implements AssistDataRequesterCallbacks {
+ private class Callbacks implements AssistDataRequesterCallbacks {
boolean canHandleReceivedData = true;
+ boolean requestCompleted = false;
ArrayList<Bundle> receivedData = new ArrayList<>();
ArrayList<Bitmap> receivedScreenshots = new ArrayList<>();
+ void reset() {
+ canHandleReceivedData = true;
+ receivedData.clear();
+ receivedScreenshots.clear();
+ }
+
@Override
public boolean canHandleReceivedAssistDataLocked() {
return canHandleReceivedData;
@@ -312,5 +358,17 @@
public void onAssistScreenshotReceivedLocked(Bitmap screenshot) {
receivedScreenshots.add(screenshot);
}
+
+ @Override
+ public void onAssistRequestCompleted() {
+ mHandler.post(() -> {
+ try {
+ mGate.await(10, TimeUnit.SECONDS);
+ requestCompleted = true;
+ } catch (InterruptedException e) {
+ Log.e(TAG, "Failed to wait", e);
+ }
+ });
+ }
}
}
\ No newline at end of file
diff --git a/services/voiceinteraction/java/com/android/server/voiceinteraction/VoiceInteractionSessionConnection.java b/services/voiceinteraction/java/com/android/server/voiceinteraction/VoiceInteractionSessionConnection.java
index 925219d..e0d9c73 100644
--- a/services/voiceinteraction/java/com/android/server/voiceinteraction/VoiceInteractionSessionConnection.java
+++ b/services/voiceinteraction/java/com/android/server/voiceinteraction/VoiceInteractionSessionConnection.java
@@ -198,7 +198,10 @@
mShowArgs = args;
mShowFlags = flags;
+ disabledContext |= getUserDisabledShowContextLocked();
mAssistDataRequester.requestAssistData(topActivities,
+ (flags & VoiceInteractionSession.SHOW_WITH_ASSIST) != 0,
+ (flags & VoiceInteractionSession.SHOW_WITH_SCREENSHOT) != 0,
(disabledContext & VoiceInteractionSession.SHOW_WITH_ASSIST) == 0,
(disabledContext & VoiceInteractionSession.SHOW_WITH_SCREENSHOT) == 0,
mCallingUid, mSessionComponentName.getPackageName());
@@ -215,6 +218,7 @@
mShowFlags = 0;
} catch (RemoteException e) {
}
+ mAssistDataRequester.processPendingAssistData();
} else if (showCallback != null) {
mPendingShowCallbacks.add(showCallback);
}
@@ -237,11 +241,16 @@
@Override
public void onAssistDataReceivedLocked(Bundle data, int activityIndex, int activityCount) {
+ // Return early if we have no session
+ if (mSession == null) {
+ return;
+ }
+
if (data == null) {
try {
mSession.handleAssist(null, null, null, 0, 0);
} catch (RemoteException e) {
- // Can't happen
+ // Ignore
}
} else {
final Bundle assistData = data.getBundle(ASSIST_KEY_DATA);
@@ -267,17 +276,22 @@
mSession.handleAssist(assistData, structure, content, activityIndex,
activityCount);
} catch (RemoteException e) {
- // Can't happen
+ // Ignore
}
}
}
@Override
public void onAssistScreenshotReceivedLocked(Bitmap screenshot) {
+ // Return early if we have no session
+ if (mSession == null) {
+ return;
+ }
+
try {
mSession.handleScreenshot(screenshot);
} catch (RemoteException e) {
- // Can't happen
+ // Ignore
}
}