Merge "Add "Locked" suffix to IMMS methods"
diff --git a/apct-tests/perftests/utils/src/android/perftests/utils/TestPackageInstaller.java b/apct-tests/perftests/utils/src/android/perftests/utils/TestPackageInstaller.java
index 530dc9d..15a65ce 100644
--- a/apct-tests/perftests/utils/src/android/perftests/utils/TestPackageInstaller.java
+++ b/apct-tests/perftests/utils/src/android/perftests/utils/TestPackageInstaller.java
@@ -130,7 +130,8 @@
IntentSender getIntentSender(int sessionId) {
String action = BROADCAST_ACTION + "." + sessionId;
IntentFilter filter = new IntentFilter(action);
- mContext.registerReceiver(this, filter);
+ mContext.registerReceiver(this, filter,
+ Context.RECEIVER_EXPORTED_UNAUDITED);
Intent intent = new Intent(action);
PendingIntent pending = PendingIntent.getBroadcast(mContext, sessionId, intent,
diff --git a/core/api/test-current.txt b/core/api/test-current.txt
index fad681f..b37c938 100644
--- a/core/api/test-current.txt
+++ b/core/api/test-current.txt
@@ -2086,6 +2086,7 @@
field public static final String NAMESPACE_CONSTRAIN_DISPLAY_APIS = "constrain_display_apis";
field public static final String NAMESPACE_DEVICE_IDLE = "device_idle";
field public static final String NAMESPACE_JOB_SCHEDULER = "jobscheduler";
+ field public static final String NAMESPACE_SELECTION_TOOLBAR = "selection_toolbar";
}
public final class Settings {
diff --git a/core/java/android/bluetooth/BluetoothUuid.java b/core/java/android/bluetooth/BluetoothUuid.java
index bb537dd..2a8ff51 100644
--- a/core/java/android/bluetooth/BluetoothUuid.java
+++ b/core/java/android/bluetooth/BluetoothUuid.java
@@ -189,7 +189,7 @@
@NonNull
@SystemApi
public static final ParcelUuid CAP =
- ParcelUuid.fromString("00008FE0-0000-1000-8000-00805F9B34FB");
+ ParcelUuid.fromString("00001853-0000-1000-8000-00805F9B34FB");
/** @hide */
@NonNull
@SystemApi
diff --git a/core/java/android/companion/AssociationInfo.java b/core/java/android/companion/AssociationInfo.java
index 3f02aa2..78d5137 100644
--- a/core/java/android/companion/AssociationInfo.java
+++ b/core/java/android/companion/AssociationInfo.java
@@ -197,6 +197,20 @@
return macAddress.equals(mDeviceMacAddress);
}
+ /** @hide */
+ public @NonNull String toShortString() {
+ final StringBuilder sb = new StringBuilder();
+ sb.append("id=").append(mId);
+ if (mDeviceMacAddress != null) {
+ sb.append(", addr=").append(getDeviceMacAddressAsString());
+ }
+ if (mSelfManaged) {
+ sb.append(", self-managed");
+ }
+ sb.append(", pkg=u").append(mUserId).append('/').append(mPackageName);
+ return sb.toString();
+ }
+
@Override
public String toString() {
return "Association{"
diff --git a/core/java/android/content/Context.java b/core/java/android/content/Context.java
index 117f305..120a0a6 100644
--- a/core/java/android/content/Context.java
+++ b/core/java/android/content/Context.java
@@ -3615,9 +3615,8 @@
* {@link #BIND_ADJUST_WITH_ACTIVITY}.
* @return {@code true} if the system is in the process of bringing up a
* service that your client has permission to bind to; {@code false}
- * if the system couldn't find the service. If this value is {@code true}, you
- * should later call {@link #unbindService} to release the
- * connection.
+ * if the system couldn't find the service. You should call {@link #unbindService}
+ * to release the connection even if this method returned {@code false}.
*
* @throws SecurityException if the client does not have the required permission to bind.
*/
diff --git a/core/java/android/hardware/SystemSensorManager.java b/core/java/android/hardware/SystemSensorManager.java
index 282f1d3..3a8513b 100644
--- a/core/java/android/hardware/SystemSensorManager.java
+++ b/core/java/android/hardware/SystemSensorManager.java
@@ -464,7 +464,8 @@
IntentFilter filter = new IntentFilter("dynamic_sensor_change");
filter.addAction(Intent.ACTION_DYNAMIC_SENSOR_CHANGED);
- mContext.registerReceiver(mDynamicSensorBroadcastReceiver, filter);
+ mContext.registerReceiver(mDynamicSensorBroadcastReceiver, filter,
+ Context.RECEIVER_NOT_EXPORTED);
}
}
diff --git a/core/java/android/inputmethodservice/InputMethodService.java b/core/java/android/inputmethodservice/InputMethodService.java
index 22b444e..afaa085 100644
--- a/core/java/android/inputmethodservice/InputMethodService.java
+++ b/core/java/android/inputmethodservice/InputMethodService.java
@@ -51,7 +51,6 @@
import static android.view.ViewGroup.LayoutParams.WRAP_CONTENT;
import static android.view.WindowInsets.Type.navigationBars;
import static android.view.WindowInsets.Type.statusBars;
-import static android.view.WindowManager.LayoutParams.FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS;
import static java.lang.annotation.RetentionPolicy.SOURCE;
@@ -1340,28 +1339,43 @@
mInflater = (LayoutInflater)getSystemService(
Context.LAYOUT_INFLATER_SERVICE);
Trace.traceBegin(TRACE_TAG_WINDOW_MANAGER, "IMS.initSoftInputWindow");
- mWindow = new SoftInputWindow(this, "InputMethod", mTheme, null, null, mDispatcherState,
- WindowManager.LayoutParams.TYPE_INPUT_METHOD, Gravity.BOTTOM, false);
- mWindow.getWindow().getAttributes().setFitInsetsTypes(statusBars() | navigationBars());
- mWindow.getWindow().getAttributes().setFitInsetsSides(Side.all() & ~Side.BOTTOM);
- mWindow.getWindow().getAttributes().receiveInsetsIgnoringZOrder = true;
+ mWindow = new SoftInputWindow(this, mTheme, mDispatcherState);
- // Automotive devices may request the navigation bar to be hidden when the IME shows up
- // (controlled via config_automotiveHideNavBarForKeyboard) in order to maximize the visible
- // screen real estate. When this happens, the IME window should animate from the bottom of
- // the screen to reduce the jank that happens from the lack of synchronization between the
- // bottom system window and the IME window.
- if (mIsAutomotive && mAutomotiveHideNavBarForKeyboard) {
- mWindow.getWindow().setDecorFitsSystemWindows(false);
+ {
+ final Window window = mWindow.getWindow();
+ {
+ final WindowManager.LayoutParams lp = window.getAttributes();
+ lp.setTitle("InputMethod");
+ lp.type = WindowManager.LayoutParams.TYPE_INPUT_METHOD;
+ lp.width = WindowManager.LayoutParams.MATCH_PARENT;
+ lp.height = WindowManager.LayoutParams.WRAP_CONTENT;
+ lp.gravity = Gravity.BOTTOM;
+ lp.setFitInsetsTypes(statusBars() | navigationBars());
+ lp.setFitInsetsSides(Side.all() & ~Side.BOTTOM);
+ lp.receiveInsetsIgnoringZOrder = true;
+ window.setAttributes(lp);
+ }
+
+ // For ColorView in DecorView to work, FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS needs to be set
+ // by default (but IME developers can opt this out later if they want a new behavior).
+ final int windowFlags = WindowManager.LayoutParams.FLAG_LAYOUT_IN_SCREEN
+ | WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE
+ | WindowManager.LayoutParams.FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS;
+ final int windowFlagsMask = windowFlags
+ | WindowManager.LayoutParams.FLAG_DIM_BEHIND; // to be unset
+ window.setFlags(windowFlags, windowFlagsMask);
+
+ // Automotive devices may request the navigation bar to be hidden when the IME shows up
+ // (controlled via config_automotiveHideNavBarForKeyboard) in order to maximize the
+ // visible screen real estate. When this happens, the IME window should animate from the
+ // bottom of the screen to reduce the jank that happens from the lack of synchronization
+ // between the bottom system window and the IME window.
+ if (mIsAutomotive && mAutomotiveHideNavBarForKeyboard) {
+ window.setDecorFitsSystemWindows(false);
+ }
}
- // For ColorView in DecorView to work, FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS needs to be set
- // by default (but IME developers can opt this out later if they want a new behavior).
- mWindow.getWindow().setFlags(
- FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS, FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS);
-
initViews();
- mWindow.getWindow().setLayout(MATCH_PARENT, WRAP_CONTENT);
Trace.traceEnd(TRACE_TAG_WINDOW_MANAGER);
mInlineSuggestionSessionController = new InlineSuggestionSessionController(
diff --git a/core/java/android/inputmethodservice/SoftInputWindow.java b/core/java/android/inputmethodservice/SoftInputWindow.java
index c2286d1..66288d6 100644
--- a/core/java/android/inputmethodservice/SoftInputWindow.java
+++ b/core/java/android/inputmethodservice/SoftInputWindow.java
@@ -42,23 +42,15 @@
import java.lang.annotation.Retention;
/**
- * A SoftInputWindow is a Dialog that is intended to be used for a top-level input
- * method window. It will be displayed along the edge of the screen, moving
- * the application user interface away from it so that the focused item is
- * always visible.
- * @hide
+ * A {@link SoftInputWindow} is a {@link Dialog} that is intended to be used for a top-level input
+ * method window. It will be displayed along the edge of the screen, moving the application user
+ * interface away from it so that the focused item is always visible.
*/
-public final class SoftInputWindow extends Dialog {
+final class SoftInputWindow extends Dialog {
private static final boolean DEBUG = false;
private static final String TAG = "SoftInputWindow";
- private final String mName;
- private final Callback mCallback;
- private final KeyEvent.Callback mKeyEventCallback;
private final KeyEvent.DispatcherState mDispatcherState;
- private final int mWindowType;
- private final int mGravity;
- private final boolean mTakesFocus;
private final Rect mBounds = new Rect();
@Retention(SOURCE)
@@ -93,22 +85,12 @@
private int mWindowState = WindowState.TOKEN_PENDING;
/**
- * Used to provide callbacks.
- */
- public interface Callback {
- /**
- * Used to be notified when {@link Dialog#onBackPressed()} gets called.
- */
- void onBackPressed();
- }
-
- /**
* Set {@link IBinder} window token to the window.
*
* <p>This method can be called only once.</p>
* @param token {@link IBinder} token to be associated with the window.
*/
- public void setToken(IBinder token) {
+ void setToken(IBinder token) {
switch (mWindowState) {
case WindowState.TOKEN_PENDING:
// Normal scenario. Nothing to worry about.
@@ -152,18 +134,9 @@
* using styles. This theme is applied on top of the current theme in
* <var>context</var>. If 0, the default dialog theme will be used.
*/
- public SoftInputWindow(Context context, String name, int theme, Callback callback,
- KeyEvent.Callback keyEventCallback, KeyEvent.DispatcherState dispatcherState,
- int windowType, int gravity, boolean takesFocus) {
+ SoftInputWindow(Context context, int theme, KeyEvent.DispatcherState dispatcherState) {
super(context, theme);
- mName = name;
- mCallback = callback;
- mKeyEventCallback = keyEventCallback;
mDispatcherState = dispatcherState;
- mWindowType = windowType;
- mGravity = gravity;
- mTakesFocus = takesFocus;
- initDockWindow();
}
@Override
@@ -188,83 +161,6 @@
}
}
- private void updateWidthHeight(WindowManager.LayoutParams lp) {
- if (lp.gravity == Gravity.TOP || lp.gravity == Gravity.BOTTOM) {
- lp.width = WindowManager.LayoutParams.MATCH_PARENT;
- lp.height = WindowManager.LayoutParams.WRAP_CONTENT;
- } else {
- lp.width = WindowManager.LayoutParams.WRAP_CONTENT;
- lp.height = WindowManager.LayoutParams.MATCH_PARENT;
- }
- }
-
- @Override
- public boolean onKeyDown(int keyCode, KeyEvent event) {
- if (mKeyEventCallback != null && mKeyEventCallback.onKeyDown(keyCode, event)) {
- return true;
- }
- return super.onKeyDown(keyCode, event);
- }
-
- @Override
- public boolean onKeyLongPress(int keyCode, KeyEvent event) {
- if (mKeyEventCallback != null && mKeyEventCallback.onKeyLongPress(keyCode, event)) {
- return true;
- }
- return super.onKeyLongPress(keyCode, event);
- }
-
- @Override
- public boolean onKeyUp(int keyCode, KeyEvent event) {
- if (mKeyEventCallback != null && mKeyEventCallback.onKeyUp(keyCode, event)) {
- return true;
- }
- return super.onKeyUp(keyCode, event);
- }
-
- @Override
- public boolean onKeyMultiple(int keyCode, int count, KeyEvent event) {
- if (mKeyEventCallback != null && mKeyEventCallback.onKeyMultiple(keyCode, count, event)) {
- return true;
- }
- return super.onKeyMultiple(keyCode, count, event);
- }
-
- @Override
- public void onBackPressed() {
- if (mCallback != null) {
- mCallback.onBackPressed();
- } else {
- super.onBackPressed();
- }
- }
-
- private void initDockWindow() {
- WindowManager.LayoutParams lp = getWindow().getAttributes();
-
- lp.type = mWindowType;
- lp.setTitle(mName);
-
- lp.gravity = mGravity;
- updateWidthHeight(lp);
-
- getWindow().setAttributes(lp);
-
- int windowSetFlags = WindowManager.LayoutParams.FLAG_LAYOUT_IN_SCREEN;
- int windowModFlags = WindowManager.LayoutParams.FLAG_LAYOUT_IN_SCREEN
- | WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE
- | WindowManager.LayoutParams.FLAG_DIM_BEHIND;
-
- if (!mTakesFocus) {
- windowSetFlags |= WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE;
- } else {
- windowSetFlags |= WindowManager.LayoutParams.FLAG_NOT_TOUCH_MODAL;
- windowModFlags |= WindowManager.LayoutParams.FLAG_NOT_TOUCH_MODAL;
- }
-
- getWindow().setFlags(windowSetFlags, windowModFlags);
- }
-
@Override
public void show() {
switch (mWindowState) {
@@ -372,10 +268,11 @@
void dumpDebug(ProtoOutputStream proto, long fieldId) {
final long token = proto.start(fieldId);
- proto.write(NAME, mName);
- proto.write(WINDOW_TYPE, mWindowType);
- proto.write(GRAVITY, mGravity);
- proto.write(TAKES_FOCUS, mTakesFocus);
+ // TODO(b/192412909): Deprecate the following 4 entries, as they are all constant.
+ proto.write(NAME, "InputMethod");
+ proto.write(WINDOW_TYPE, WindowManager.LayoutParams.TYPE_INPUT_METHOD);
+ proto.write(GRAVITY, Gravity.BOTTOM);
+ proto.write(TAKES_FOCUS, false);
mBounds.dumpDebug(proto, BOUNDS);
proto.write(WINDOW_STATE, mWindowState);
proto.end(token);
diff --git a/core/java/android/net/NetworkPolicy.java b/core/java/android/net/NetworkPolicy.java
index a9c52f1..f4b427959f 100644
--- a/core/java/android/net/NetworkPolicy.java
+++ b/core/java/android/net/NetworkPolicy.java
@@ -369,11 +369,13 @@
try {
final NetworkTemplate.Builder builder = new NetworkTemplate.Builder(matchRule)
- .setWifiNetworkKey(wifiNetworkKey)
.setMeteredness(metered);
if (subscriberId != null) {
builder.setSubscriberIds(Set.of(subscriberId));
}
+ if (wifiNetworkKey != null) {
+ builder.setWifiNetworkKeys(Set.of(wifiNetworkKey));
+ }
return builder.build();
} catch (IllegalArgumentException e) {
throw new BackupUtils.BadVersionException(
@@ -393,7 +395,7 @@
case MATCH_MOBILE:
return !template.getSubscriberIds().isEmpty();
case MATCH_WIFI:
- if (Objects.equals(template.getWifiNetworkKey(), null)
+ if (template.getWifiNetworkKeys().isEmpty()
&& template.getSubscriberIds().isEmpty()) {
return false;
}
diff --git a/core/java/android/permission/PermissionManager.java b/core/java/android/permission/PermissionManager.java
index e2f5908..17f57a8 100644
--- a/core/java/android/permission/PermissionManager.java
+++ b/core/java/android/permission/PermissionManager.java
@@ -218,7 +218,8 @@
/**
* Checks whether a given data access chain described by the given {@link AttributionSource}
* has a given permission. Call this method if you are the datasource which would not blame you
- * for access to the data since you are the data.
+ * for access to the data since you are the data. Use this API if you are the datasource of the
+ * protected state.
*
* <strong>NOTE:</strong> Use this method only for permission checks at the
* point where you will deliver the permission protected data to clients.
diff --git a/core/java/android/provider/DeviceConfig.java b/core/java/android/provider/DeviceConfig.java
index 22b9578..6349cde 100644
--- a/core/java/android/provider/DeviceConfig.java
+++ b/core/java/android/provider/DeviceConfig.java
@@ -564,6 +564,14 @@
public static final String NAMESPACE_WINDOW_MANAGER_NATIVE_BOOT = "window_manager_native_boot";
/**
+ * Definitions for selection toolbar related functions.
+ *
+ * @hide
+ */
+ @TestApi
+ public static final String NAMESPACE_SELECTION_TOOLBAR = "selection_toolbar";
+
+ /**
* List of namespaces which can be read without READ_DEVICE_CONFIG permission
*
* @hide
@@ -571,7 +579,7 @@
@NonNull
private static final List<String> PUBLIC_NAMESPACES =
Arrays.asList(NAMESPACE_TEXTCLASSIFIER, NAMESPACE_RUNTIME, NAMESPACE_STATSD_JAVA,
- NAMESPACE_STATSD_JAVA_BOOT);
+ NAMESPACE_STATSD_JAVA_BOOT, NAMESPACE_SELECTION_TOOLBAR);
/**
* Privacy related properties definitions.
*
diff --git a/core/java/android/provider/Settings.java b/core/java/android/provider/Settings.java
index f5777ed..190b8f6 100644
--- a/core/java/android/provider/Settings.java
+++ b/core/java/android/provider/Settings.java
@@ -6495,6 +6495,27 @@
public static final String ALLOW_MOCK_LOCATION = "mock_location";
/**
+ * This is used by Bluetooth Manager to store adapter name
+ * @hide
+ */
+ @Readable(maxTargetSdk = Build.VERSION_CODES.S)
+ public static final String BLUETOOTH_NAME = "bluetooth_name";
+
+ /**
+ * This is used by Bluetooth Manager to store adapter address
+ * @hide
+ */
+ @Readable(maxTargetSdk = Build.VERSION_CODES.S)
+ public static final String BLUETOOTH_ADDRESS = "bluetooth_address";
+
+ /**
+ * This is used by Bluetooth Manager to store whether adapter address is valid
+ * @hide
+ */
+ @Readable(maxTargetSdk = Build.VERSION_CODES.S)
+ public static final String BLUETOOTH_ADDR_VALID = "bluetooth_addr_valid";
+
+ /**
* Setting to indicate that on device captions are enabled.
*
* @hide
@@ -17037,6 +17058,15 @@
*/
public static final String CLOCKWORK_SYSUI_MAIN_ACTIVITY =
"clockwork_sysui_main_activity";
+
+ /**
+ * Setting to disable power button long press launching Assistant. It's boolean, i.e.
+ * enabled = 1, disabled = 0. By default, this setting is enabled.
+ *
+ * @hide
+ */
+ public static final String CLOCKWORK_LONG_PRESS_TO_ASSISTANT_ENABLED =
+ "clockwork_long_press_to_assistant_enabled";
}
}
diff --git a/core/java/android/view/DisplayCutout.java b/core/java/android/view/DisplayCutout.java
index c1a5636..ae323226 100644
--- a/core/java/android/view/DisplayCutout.java
+++ b/core/java/android/view/DisplayCutout.java
@@ -585,8 +585,10 @@
* Returns a list of {@code Rect}s, each of which is the bounding rectangle for a non-functional
* area on the display.
*
- * There will be at most one non-functional area per short edge of the device, and none on
- * the long edges.
+ * There will be at most one non-functional area per edge of the device.
+ *
+ * <p>Note that there is no bounding rectangle for waterfall cutout since it just represents the
+ * curved areas of the display but not the non-functional areas.</p>
*
* @return a list of bounding {@code Rect}s, one for each display cutout area. No empty Rect is
* returned.
@@ -607,8 +609,10 @@
* functional area on the display. Ordinal value of BoundPosition is used as an index of
* the array.
*
- * There will be at most one non-functional area per short edge of the device, and none on
- * the long edges.
+ * There will be at most one non-functional area per edge of the device.
+ *
+ * <p>Note that there is no bounding rectangle for waterfall cutout since it just represents the
+ * curved areas of the display but not the non-functional areas.</p>
*
* @return an array of bounding {@code Rect}s, one for each display cutout area. This might
* contain ZERO_RECT, which means there is no cutout area at the position.
diff --git a/core/java/android/view/SurfaceControl.java b/core/java/android/view/SurfaceControl.java
index 3b52709..ab33fea 100644
--- a/core/java/android/view/SurfaceControl.java
+++ b/core/java/android/view/SurfaceControl.java
@@ -515,6 +515,15 @@
public static final int ENABLE_BACKPRESSURE = 0x00000100;
/**
+ * Buffers from this SurfaceControl should be considered display decorations.
+ *
+ * If the hardware has optimizations for display decorations (e.g. rounded corners, camera
+ * cutouts, etc), it should use them for this layer.
+ * @hide
+ */
+ public static final int DISPLAY_DECORATION = 0x00000200;
+
+ /**
* Surface creation flag: Creates a surface where color components are interpreted
* as "non pre-multiplied" by their alpha channel. Of course this flag is
* meaningless for surfaces without an alpha channel. By default
@@ -3266,6 +3275,21 @@
}
/**
+ * Sets whether the surface should take advantage of display decoration optimizations.
+ * @hide
+ */
+ public Transaction setDisplayDecoration(SurfaceControl sc, boolean displayDecoration) {
+ checkPreconditions(sc);
+ if (displayDecoration) {
+ nativeSetFlags(mNativeObject, sc.mNativeObject, DISPLAY_DECORATION,
+ DISPLAY_DECORATION);
+ } else {
+ nativeSetFlags(mNativeObject, sc.mNativeObject, 0, DISPLAY_DECORATION);
+ }
+ return this;
+ }
+
+ /**
* Indicates whether the surface must be considered opaque, even if its pixel format is
* set to translucent. This can be useful if an application needs full RGBA 8888 support
* for instance but will still draw every pixel opaque.
diff --git a/core/java/android/view/ViewRootImpl.java b/core/java/android/view/ViewRootImpl.java
index 1fdbf0e2..748e551 100644
--- a/core/java/android/view/ViewRootImpl.java
+++ b/core/java/android/view/ViewRootImpl.java
@@ -493,6 +493,9 @@
protected final ViewFrameInfo mViewFrameInfo = new ViewFrameInfo();
private final InputEventAssigner mInputEventAssigner = new InputEventAssigner();
+ // Whether to draw this surface as DISPLAY_DECORATION.
+ boolean mDisplayDecorationCached = false;
+
/**
* Update the Choreographer's FrameInfo object with the timing information for the current
* ViewRootImpl instance. Erase the data in the current ViewFrameInfo to prepare for the next
@@ -1970,26 +1973,29 @@
return mBoundsLayer;
}
- Surface getOrCreateBLASTSurface() {
+ void updateBlastSurfaceIfNeeded() {
if (!mSurfaceControl.isValid()) {
- return null;
+ return;
}
- Surface ret = null;
- if (mBlastBufferQueue == null) {
- mBlastBufferQueue = new BLASTBufferQueue(mTag, mSurfaceControl,
- mSurfaceSize.x, mSurfaceSize.y,
- mWindowAttributes.format);
- // We only return the Surface the first time, as otherwise
- // it hasn't changed and there is no need to update.
- ret = mBlastBufferQueue.createSurface();
- } else {
+ if (mBlastBufferQueue != null && mBlastBufferQueue.isSameSurfaceControl(mSurfaceControl)) {
mBlastBufferQueue.update(mSurfaceControl,
mSurfaceSize.x, mSurfaceSize.y,
mWindowAttributes.format);
+ return;
}
- return ret;
+ // If the SurfaceControl has been updated, destroy and recreate the BBQ to reset the BQ and
+ // BBQ states.
+ if (mBlastBufferQueue != null) {
+ mBlastBufferQueue.destroy();
+ }
+ mBlastBufferQueue = new BLASTBufferQueue(mTag, mSurfaceControl,
+ mSurfaceSize.x, mSurfaceSize.y, mWindowAttributes.format);
+ Surface blastSurface = mBlastBufferQueue.createSurface();
+ // Only call transferFrom if the surface has changed to prevent inc the generation ID and
+ // causing EGL resources to be recreated.
+ mSurface.transferFrom(blastSurface);
}
private void setBoundsLayerCrop(Transaction t) {
@@ -2853,6 +2859,12 @@
if (mSurfaceControl.isValid()) {
updateOpacity(mWindowAttributes, dragResizing,
surfaceControlChanged /*forceUpdate */);
+ // No need to updateDisplayDecoration if it's a new SurfaceControl and
+ // mDisplayDecorationCached is false, since that's the default for a new
+ // SurfaceControl.
+ if (surfaceControlChanged && mDisplayDecorationCached) {
+ updateDisplayDecoration();
+ }
}
if (DEBUG_LAYOUT) Log.v(mTag, "relayout: frame=" + frame.toShortString()
@@ -7884,13 +7896,7 @@
if (!useBLAST()) {
mSurface.copyFrom(mSurfaceControl);
} else {
- final Surface blastSurface = getOrCreateBLASTSurface();
- // If blastSurface == null that means it hasn't changed since the last time we
- // called. In this situation, avoid calling transferFrom as we would then
- // inc the generation ID and cause EGL resources to be recreated.
- if (blastSurface != null) {
- mSurface.transferFrom(blastSurface);
- }
+ updateBlastSurfaceIfNeeded();
}
if (mAttachInfo.mThreadedRenderer != null) {
mAttachInfo.mThreadedRenderer.setSurfaceControl(mSurfaceControl);
@@ -10440,6 +10446,23 @@
}
/**
+ * @hide
+ */
+ public void setDisplayDecoration(boolean displayDecoration) {
+ if (displayDecoration == mDisplayDecorationCached) return;
+
+ mDisplayDecorationCached = displayDecoration;
+
+ if (mSurfaceControl.isValid()) {
+ updateDisplayDecoration();
+ }
+ }
+
+ private void updateDisplayDecoration() {
+ mTransaction.setDisplayDecoration(mSurfaceControl, mDisplayDecorationCached).apply();
+ }
+
+ /**
* Sends a list of blur regions to SurfaceFlinger, tagged with a frame.
*
* @param regionCopy List of regions
diff --git a/core/java/android/view/WindowManager.java b/core/java/android/view/WindowManager.java
index aa6cb83..cd9f3eb6 100644
--- a/core/java/android/view/WindowManager.java
+++ b/core/java/android/view/WindowManager.java
@@ -105,6 +105,7 @@
import android.os.Build;
import android.os.Bundle;
import android.os.IBinder;
+import android.os.IInputConstants;
import android.os.Parcel;
import android.os.Parcelable;
import android.text.TextUtils;
@@ -120,6 +121,7 @@
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
+import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.Objects;
@@ -2755,7 +2757,7 @@
*
* <p>Applications that target {@link android.os.Build.VERSION_CODES#P} and later, this flag
* is ignored unless there is a focused view that returns {@code true} from
- * {@link View#isInEditMode()} when the window is focused.</p>
+ * {@link View#onCheckIsTextEditor()} when the window is focused.</p>
*/
public static final int SOFT_INPUT_STATE_VISIBLE = 4;
@@ -2765,7 +2767,7 @@
*
* <p>Applications that target {@link android.os.Build.VERSION_CODES#P} and later, this flag
* is ignored unless there is a focused view that returns {@code true} from
- * {@link View#isInEditMode()} when the window is focused.</p>
+ * {@link View#onCheckIsTextEditor()} when the window is focused.</p>
*/
public static final int SOFT_INPUT_STATE_ALWAYS_VISIBLE = 5;
@@ -3325,21 +3327,13 @@
public static final int LAYOUT_IN_DISPLAY_CUTOUT_MODE_ALWAYS = 3;
/**
- * When this window has focus, disable touch pad pointer gesture processing.
- * The window will receive raw position updates from the touch pad instead
- * of pointer movements and synthetic touch events.
- *
- * @hide
- */
- public static final int INPUT_FEATURE_DISABLE_POINTER_GESTURES = 0x00000001;
-
- /**
* Does not construct an input channel for this window. The channel will therefore
* be incapable of receiving input.
*
* @hide
*/
- public static final int INPUT_FEATURE_NO_INPUT_CHANNEL = 0x00000002;
+ public static final int INPUT_FEATURE_NO_INPUT_CHANNEL =
+ IInputConstants.InputFeature.NO_INPUT_CHANNEL;
/**
* When this window has focus, does not call user activity for all input events so
@@ -3352,7 +3346,8 @@
* @hide
*/
@UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
- public static final int INPUT_FEATURE_DISABLE_USER_ACTIVITY = 0x00000004;
+ public static final int INPUT_FEATURE_DISABLE_USER_ACTIVITY =
+ IInputConstants.InputFeature.DISABLE_USER_ACTIVITY;
/**
* An input spy window. This window will receive all pointer events within its touchable
@@ -3361,7 +3356,25 @@
* event's coordinates.
* @hide
*/
- public static final int INPUT_FEATURE_SPY = 0x00000020;
+ public static final int INPUT_FEATURE_SPY =
+ IInputConstants.InputFeature.SPY;
+
+ /**
+ * When used with the window flag {@link #FLAG_NOT_TOUCHABLE}, this window will continue
+ * to receive events from a stylus device within its touchable region. All other pointer
+ * events, such as from a mouse or touchscreen, will be dispatched to the windows behind it.
+ *
+ * This input feature has no effect when the window flag {@link #FLAG_NOT_TOUCHABLE} is
+ * not set.
+ *
+ * The window must be a trusted overlay to use this input feature.
+ *
+ * @see #FLAG_NOT_TOUCHABLE
+ *
+ * @hide
+ */
+ public static final int INPUT_FEATURE_INTERCEPTS_STYLUS =
+ IInputConstants.InputFeature.INTERCEPTS_STYLUS;
/**
* An internal annotation for flags that can be specified to {@link #inputFeatures}.
@@ -3370,18 +3383,20 @@
*/
@Retention(RetentionPolicy.SOURCE)
@IntDef(flag = true, prefix = { "INPUT_FEATURE_" }, value = {
- INPUT_FEATURE_DISABLE_POINTER_GESTURES,
INPUT_FEATURE_NO_INPUT_CHANNEL,
INPUT_FEATURE_DISABLE_USER_ACTIVITY,
+ INPUT_FEATURE_SPY,
+ INPUT_FEATURE_INTERCEPTS_STYLUS,
})
public @interface InputFeatureFlags {}
/**
* Control special features of the input subsystem.
*
- * @see #INPUT_FEATURE_DISABLE_POINTER_GESTURES
* @see #INPUT_FEATURE_NO_INPUT_CHANNEL
* @see #INPUT_FEATURE_DISABLE_USER_ACTIVITY
+ * @see #INPUT_FEATURE_SPY
+ * @see #INPUT_FEATURE_INTERCEPTS_STYLUS
* @hide
*/
@InputFeatureFlags
@@ -4485,7 +4500,7 @@
sb.append(hasSystemUiListeners);
}
if (inputFeatures != 0) {
- sb.append(" if=").append(inputFeatureToString(inputFeatures));
+ sb.append(" if=").append(inputFeaturesToString(inputFeatures));
}
if (userActivityTimeout >= 0) {
sb.append(" userActivityTimeout=").append(userActivityTimeout);
@@ -4787,17 +4802,28 @@
}
}
- private static String inputFeatureToString(int inputFeature) {
- switch (inputFeature) {
- case INPUT_FEATURE_DISABLE_POINTER_GESTURES:
- return "DISABLE_POINTER_GESTURES";
- case INPUT_FEATURE_NO_INPUT_CHANNEL:
- return "NO_INPUT_CHANNEL";
- case INPUT_FEATURE_DISABLE_USER_ACTIVITY:
- return "DISABLE_USER_ACTIVITY";
- default:
- return Integer.toString(inputFeature);
+ private static String inputFeaturesToString(int inputFeatures) {
+ final List<String> features = new ArrayList<>();
+ if ((inputFeatures & INPUT_FEATURE_NO_INPUT_CHANNEL) != 0) {
+ inputFeatures &= ~INPUT_FEATURE_NO_INPUT_CHANNEL;
+ features.add("INPUT_FEATURE_NO_INPUT_CHANNEL");
}
+ if ((inputFeatures & INPUT_FEATURE_DISABLE_USER_ACTIVITY) != 0) {
+ inputFeatures &= ~INPUT_FEATURE_DISABLE_USER_ACTIVITY;
+ features.add("INPUT_FEATURE_DISABLE_USER_ACTIVITY");
+ }
+ if ((inputFeatures & INPUT_FEATURE_SPY) != 0) {
+ inputFeatures &= ~INPUT_FEATURE_SPY;
+ features.add("INPUT_FEATURE_SPY");
+ }
+ if ((inputFeatures & INPUT_FEATURE_INTERCEPTS_STYLUS) != 0) {
+ inputFeatures &= ~INPUT_FEATURE_INTERCEPTS_STYLUS;
+ features.add("INPUT_FEATURE_INTERCEPTS_STYLUS");
+ }
+ if (inputFeatures != 0) {
+ features.add(Integer.toHexString(inputFeatures));
+ }
+ return String.join(" | ", features);
}
/**
diff --git a/core/java/com/android/internal/policy/IKeyguardService.aidl b/core/java/com/android/internal/policy/IKeyguardService.aidl
index 76aa7a0..36b7ee5 100644
--- a/core/java/com/android/internal/policy/IKeyguardService.aidl
+++ b/core/java/com/android/internal/policy/IKeyguardService.aidl
@@ -15,6 +15,7 @@
*/
package com.android.internal.policy;
+import android.content.Intent;
import com.android.internal.policy.IKeyguardDrawnCallback;
import com.android.internal.policy.IKeyguardDismissCallback;
import com.android.internal.policy.IKeyguardStateCallback;
@@ -113,7 +114,20 @@
/**
* Notifies the Keyguard that the power key was pressed while locked and launched Home rather
- * than putting the device to sleep or waking up.
+ * than putting the device to sleep or waking up. Note that it's called only if the device is
+ * interactive.
*/
void onShortPowerPressedGoHome();
+
+ /**
+ * Notifies the Keyguard that it needs to bring up a bouncer and then launch the intent as soon
+ * as user unlocks the watch.
+ */
+ void dismissKeyguardToLaunch(in Intent intentToLaunch);
+
+ /**
+ * Notifies the Keyguard that a key was pressed while locked so the Keyguard can handle it.
+ * Note that it's called only if the device is interactive.
+ */
+ void onSystemKeyPressed(int keycode);
}
diff --git a/core/jni/android_graphics_BLASTBufferQueue.cpp b/core/jni/android_graphics_BLASTBufferQueue.cpp
index 78e5adc..55f1369 100644
--- a/core/jni/android_graphics_BLASTBufferQueue.cpp
+++ b/core/jni/android_graphics_BLASTBufferQueue.cpp
@@ -37,16 +37,6 @@
return reinterpret_cast<jlong>(queue.get());
}
-static jlong nativeCreateAndUpdate(JNIEnv* env, jclass clazz, jstring jName, jlong surfaceControl,
- jlong width, jlong height, jint format) {
- ScopedUtfChars name(env, jName);
- sp<BLASTBufferQueue> queue =
- new BLASTBufferQueue(name.c_str(), reinterpret_cast<SurfaceControl*>(surfaceControl),
- width, height, format);
- queue->incStrong((void*)nativeCreate);
- return reinterpret_cast<jlong>(queue.get());
-}
-
static void nativeDestroy(JNIEnv* env, jclass clazz, jlong ptr) {
sp<BLASTBufferQueue> queue = reinterpret_cast<BLASTBufferQueue*>(ptr);
queue->decStrong((void*)nativeCreate);
@@ -91,11 +81,15 @@
queue->applyPendingTransactions(frameNum);
}
+static bool nativeIsSameSurfaceControl(JNIEnv* env, jclass clazz, jlong ptr, jlong surfaceControl) {
+ sp<BLASTBufferQueue> queue = reinterpret_cast<BLASTBufferQueue*>(ptr);
+ return queue->isSameSurfaceControl(reinterpret_cast<SurfaceControl*>(surfaceControl));
+}
+
static const JNINativeMethod gMethods[] = {
/* name, signature, funcPtr */
// clang-format off
{"nativeCreate", "(Ljava/lang/String;)J", (void*)nativeCreate},
- {"nativeCreateAndUpdate", "(Ljava/lang/String;JJJI)J", (void*)nativeCreateAndUpdate},
{"nativeGetSurface", "(JZ)Landroid/view/Surface;", (void*)nativeGetSurface},
{"nativeDestroy", "(J)V", (void*)nativeDestroy},
{"nativeSetSyncTransaction", "(JJZ)V", (void*)nativeSetSyncTransaction},
@@ -103,6 +97,7 @@
{"nativeMergeWithNextTransaction", "(JJJ)V", (void*)nativeMergeWithNextTransaction},
{"nativeGetLastAcquiredFrameNum", "(J)J", (void*)nativeGetLastAcquiredFrameNum},
{"nativeApplyPendingTransactions", "(JJ)V", (void*)nativeApplyPendingTransactions},
+ {"nativeIsSameSurfaceControl", "(JJ)Z", (void*)nativeIsSameSurfaceControl},
// clang-format on
};
diff --git a/core/tests/coretests/src/android/net/NetworkPolicyTest.kt b/core/tests/coretests/src/android/net/NetworkPolicyTest.kt
index 121caef..3c8f90c 100644
--- a/core/tests/coretests/src/android/net/NetworkPolicyTest.kt
+++ b/core/tests/coretests/src/android/net/NetworkPolicyTest.kt
@@ -32,14 +32,14 @@
import kotlin.test.assertTrue
private const val TEST_IMSI1 = "TESTIMSI1"
-private const val TEST_SSID1 = "TESTISSID1"
+private const val TEST_WIFI_NETWORK_KEY1 = "TESTKEY1"
@RunWith(AndroidJUnit4::class)
class NetworkPolicyTest {
@Test
fun testTemplateBackupRestore() {
assertPolicyBackupRestore(createTestPolicyForTemplate(
- NetworkTemplate.buildTemplateWifi(TEST_SSID1)))
+ NetworkTemplate.buildTemplateWifi(TEST_WIFI_NETWORK_KEY1)))
assertPolicyBackupRestore(createTestPolicyForTemplate(
NetworkTemplate.buildTemplateMobileAll(TEST_IMSI1)))
assertPolicyBackupRestore(createTestPolicyForTemplate(
@@ -79,6 +79,6 @@
// Verify wifi template can be persistable if the Wifi Network Key is supplied.
assertTrue(NetworkPolicy.isTemplatePersistable(NetworkTemplate.Builder(MATCH_WIFI)
- .setWifiNetworkKey(TEST_SSID1).build()))
+ .setWifiNetworkKeys(setOf(TEST_WIFI_NETWORK_KEY1)).build()))
}
-}
\ No newline at end of file
+}
diff --git a/graphics/java/android/graphics/BLASTBufferQueue.java b/graphics/java/android/graphics/BLASTBufferQueue.java
index 8d3eadb..a9e730d 100644
--- a/graphics/java/android/graphics/BLASTBufferQueue.java
+++ b/graphics/java/android/graphics/BLASTBufferQueue.java
@@ -27,8 +27,6 @@
// Note: This field is accessed by native code.
public long mNativeObject; // BLASTBufferQueue*
- private static native long nativeCreateAndUpdate(String name, long surfaceControl, long width,
- long height, int format);
private static native long nativeCreate(String name);
private static native void nativeDestroy(long ptr);
private static native Surface nativeGetSurface(long ptr, boolean includeSurfaceControlHandle);
@@ -40,11 +38,13 @@
long frameNumber);
private static native long nativeGetLastAcquiredFrameNum(long ptr);
private static native void nativeApplyPendingTransactions(long ptr, long frameNumber);
+ private static native boolean nativeIsSameSurfaceControl(long ptr, long surfaceControlPtr);
/** Create a new connection with the surface flinger. */
public BLASTBufferQueue(String name, SurfaceControl sc, int width, int height,
@PixelFormat.Format int format) {
- mNativeObject = nativeCreateAndUpdate(name, sc.mNativeObject, width, height, format);
+ this(name);
+ update(sc, width, height, format);
}
public BLASTBufferQueue(String name) {
@@ -152,4 +152,11 @@
public long getLastAcquiredFrameNum() {
return nativeGetLastAcquiredFrameNum(mNativeObject);
}
+
+ /**
+ * @return True if the associated SurfaceControl has the same handle as {@param sc}.
+ */
+ public boolean isSameSurfaceControl(SurfaceControl sc) {
+ return nativeIsSameSurfaceControl(mNativeObject, sc.mNativeObject);
+ }
}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/dagger/WMShellModule.java b/libs/WindowManager/Shell/src/com/android/wm/shell/dagger/WMShellModule.java
index d70857a4..4f01dc6 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/dagger/WMShellModule.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/dagger/WMShellModule.java
@@ -309,10 +309,11 @@
PipAnimationController pipAnimationController, PipBoundsAlgorithm pipBoundsAlgorithm,
PipBoundsState pipBoundsState, PipTransitionState pipTransitionState,
PhonePipMenuController pipMenuController,
- PipSurfaceTransactionHelper pipSurfaceTransactionHelper) {
+ PipSurfaceTransactionHelper pipSurfaceTransactionHelper,
+ Optional<SplitScreenController> splitScreenOptional) {
return new PipTransition(context, pipBoundsState, pipTransitionState, pipMenuController,
pipBoundsAlgorithm, pipAnimationController, transitions, shellTaskOrganizer,
- pipSurfaceTransactionHelper);
+ pipSurfaceTransactionHelper, splitScreenOptional);
}
@WMSingleton
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipTaskOrganizer.java b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipTaskOrganizer.java
index 667d166..a8d4d1c 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipTaskOrganizer.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipTaskOrganizer.java
@@ -25,6 +25,8 @@
import static com.android.wm.shell.ShellTaskOrganizer.TASK_LISTENER_TYPE_PIP;
import static com.android.wm.shell.ShellTaskOrganizer.taskListenerTypeToString;
+import static com.android.wm.shell.common.split.SplitScreenConstants.SPLIT_POSITION_BOTTOM_OR_RIGHT;
+import static com.android.wm.shell.common.split.SplitScreenConstants.SPLIT_POSITION_TOP_OR_LEFT;
import static com.android.wm.shell.pip.PipAnimationController.ANIM_TYPE_ALPHA;
import static com.android.wm.shell.pip.PipAnimationController.ANIM_TYPE_BOUNDS;
import static com.android.wm.shell.pip.PipAnimationController.FRACTION_START;
@@ -40,6 +42,10 @@
import static com.android.wm.shell.pip.PipAnimationController.isInPipDirection;
import static com.android.wm.shell.pip.PipAnimationController.isOutPipDirection;
import static com.android.wm.shell.pip.PipAnimationController.isRemovePipDirection;
+import static com.android.wm.shell.transition.Transitions.ENABLE_SHELL_TRANSITIONS;
+import static com.android.wm.shell.transition.Transitions.TRANSIT_EXIT_PIP;
+import static com.android.wm.shell.transition.Transitions.TRANSIT_EXIT_PIP_TO_SPLIT;
+import static com.android.wm.shell.transition.Transitions.TRANSIT_REMOVE_PIP;
import android.animation.Animator;
import android.animation.AnimatorListenerAdapter;
@@ -394,6 +400,18 @@
mPipUiEventLoggerLogger.log(
PipUiEventLogger.PipUiEventEnum.PICTURE_IN_PICTURE_EXPAND_TO_FULLSCREEN);
final WindowContainerTransaction wct = new WindowContainerTransaction();
+
+ if (ENABLE_SHELL_TRANSITIONS) {
+ if (requestEnterSplit && mSplitScreenOptional.isPresent()) {
+ mSplitScreenOptional.get().prepareEnterSplitScreen(wct, mTaskInfo,
+ isPipTopLeft()
+ ? SPLIT_POSITION_TOP_OR_LEFT : SPLIT_POSITION_BOTTOM_OR_RIGHT);
+ mPipTransitionController.startExitTransition(
+ TRANSIT_EXIT_PIP_TO_SPLIT, wct, null /* destinationBounds */);
+ return;
+ }
+ }
+
final Rect destinationBounds = mPipBoundsState.getDisplayBounds();
final int direction = syncWithSplitScreenBounds(destinationBounds, requestEnterSplit)
? TRANSITION_DIRECTION_LEAVE_PIP_TO_SPLIT_SCREEN
@@ -414,7 +432,7 @@
mPipTransitionState.setTransitionState(PipTransitionState.EXITING_PIP);
if (Transitions.ENABLE_SHELL_TRANSITIONS) {
- mPipTransitionController.startTransition(destinationBounds, wct);
+ mPipTransitionController.startExitTransition(TRANSIT_EXIT_PIP, wct, destinationBounds);
return;
}
mSyncTransactionQueue.queue(wct);
@@ -479,7 +497,8 @@
wct.setBounds(mToken, null);
wct.setWindowingMode(mToken, WINDOWING_MODE_UNDEFINED);
wct.reorder(mToken, false);
- mPipTransitionController.startTransition(null, wct);
+ mPipTransitionController.startExitTransition(TRANSIT_REMOVE_PIP, wct,
+ null /* destinationBounds */);
return;
}
@@ -1280,7 +1299,8 @@
public void applyFinishBoundsResize(@NonNull WindowContainerTransaction wct,
@PipAnimationController.TransitionDirection int direction, boolean wasPipTopLeft) {
if (direction == TRANSITION_DIRECTION_LEAVE_PIP_TO_SPLIT_SCREEN) {
- mSplitScreenOptional.get().enterSplitScreen(mTaskInfo.taskId, wasPipTopLeft, wct);
+ mSplitScreenOptional.ifPresent(splitScreenController ->
+ splitScreenController.enterSplitScreen(mTaskInfo.taskId, wasPipTopLeft, wct));
} else {
mTaskOrganizer.applyTransaction(wct);
}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipTransition.java b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipTransition.java
index c909652f..e440feb 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipTransition.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipTransition.java
@@ -30,8 +30,9 @@
import static com.android.wm.shell.pip.PipAnimationController.TRANSITION_DIRECTION_TO_PIP;
import static com.android.wm.shell.pip.PipAnimationController.isInPipDirection;
import static com.android.wm.shell.pip.PipAnimationController.isOutPipDirection;
-import static com.android.wm.shell.transition.Transitions.TRANSIT_EXIT_PIP;
+import static com.android.wm.shell.transition.Transitions.TRANSIT_EXIT_PIP_TO_SPLIT;
import static com.android.wm.shell.transition.Transitions.TRANSIT_REMOVE_PIP;
+import static com.android.wm.shell.transition.Transitions.isOpeningType;
import android.app.ActivityManager;
import android.app.TaskInfo;
@@ -51,8 +52,11 @@
import com.android.wm.shell.R;
import com.android.wm.shell.ShellTaskOrganizer;
+import com.android.wm.shell.splitscreen.SplitScreenController;
import com.android.wm.shell.transition.Transitions;
+import java.util.Optional;
+
/**
* Implementation of transitions for PiP on phone. Responsible for enter (alpha, bounds) and
* exit animation.
@@ -64,6 +68,7 @@
private final PipTransitionState mPipTransitionState;
private final int mEnterExitAnimationDuration;
private final PipSurfaceTransactionHelper mSurfaceTransactionHelper;
+ private final Optional<SplitScreenController> mSplitScreenOptional;
private @PipAnimationController.AnimationType int mOneShotAnimationType = ANIM_TYPE_BOUNDS;
private Transitions.TransitionFinishCallback mFinishCallback;
private Rect mExitDestinationBounds = new Rect();
@@ -77,13 +82,15 @@
PipAnimationController pipAnimationController,
Transitions transitions,
@NonNull ShellTaskOrganizer shellTaskOrganizer,
- PipSurfaceTransactionHelper pipSurfaceTransactionHelper) {
+ PipSurfaceTransactionHelper pipSurfaceTransactionHelper,
+ Optional<SplitScreenController> splitScreenOptional) {
super(pipBoundsState, pipMenuController, pipBoundsAlgorithm,
pipAnimationController, transitions, shellTaskOrganizer);
mPipTransitionState = pipTransitionState;
mEnterExitAnimationDuration = context.getResources()
.getInteger(R.integer.config_pipResizeAnimationDuration);
mSurfaceTransactionHelper = pipSurfaceTransactionHelper;
+ mSplitScreenOptional = splitScreenOptional;
}
@Override
@@ -101,13 +108,12 @@
}
@Override
- public void startTransition(Rect destinationBounds, WindowContainerTransaction out) {
+ public void startExitTransition(int type, WindowContainerTransaction out,
+ @Nullable Rect destinationBounds) {
if (destinationBounds != null) {
mExitDestinationBounds.set(destinationBounds);
- mExitTransition = mTransitions.startTransition(TRANSIT_EXIT_PIP, out, this);
- } else {
- mTransitions.startTransition(TRANSIT_REMOVE_PIP, out, this);
}
+ mExitTransition = mTransitions.startTransition(type, out, this);
}
@Override
@@ -116,9 +122,15 @@
@android.annotation.NonNull SurfaceControl.Transaction startTransaction,
@android.annotation.NonNull SurfaceControl.Transaction finishTransaction,
@android.annotation.NonNull Transitions.TransitionFinishCallback finishCallback) {
-
- if (mExitTransition == transition || info.getType() == TRANSIT_EXIT_PIP) {
+ final int type = info.getType();
+ if (mExitTransition == transition) {
mExitTransition = null;
+
+ if (type == TRANSIT_EXIT_PIP_TO_SPLIT) {
+ return startExitToSplitAnimation(
+ info, startTransaction, finishTransaction, finishCallback);
+ }
+
if (info.getChanges().size() == 1) {
if (mFinishCallback != null) {
mFinishCallback.onTransitionFinished(null, null);
@@ -138,7 +150,7 @@
}
}
- if (info.getType() == TRANSIT_REMOVE_PIP) {
+ if (type == TRANSIT_REMOVE_PIP) {
if (mFinishCallback != null) {
mFinishCallback.onTransitionFinished(null /* wct */, null /* callback */);
mFinishCallback = null;
@@ -154,7 +166,7 @@
// We only support TRANSIT_PIP type (from RootWindowContainer) or TRANSIT_OPEN (from apps
// that enter PiP instantly on opening, mostly from CTS/Flicker tests)
- if (info.getType() != TRANSIT_PIP && info.getType() != TRANSIT_OPEN) {
+ if (type != TRANSIT_PIP && type != TRANSIT_OPEN) {
// In case the PIP window is part of rotation transition, reset the bounds and rounded
// corner.
for (int i = info.getChanges().size() - 1; i >= 0; --i) {
@@ -368,6 +380,40 @@
return true;
}
+ private boolean startExitToSplitAnimation(TransitionInfo info,
+ SurfaceControl.Transaction startTransaction,
+ SurfaceControl.Transaction finishTransaction,
+ Transitions.TransitionFinishCallback finishCallback) {
+ final int changeSize = info.getChanges().size();
+ if (changeSize < 4) {
+ throw new RuntimeException(
+ "Got an exit-pip-to-split transition with unexpected change-list");
+ }
+ for (int i = changeSize - 1; i >= 0; i--) {
+ final TransitionInfo.Change change = info.getChanges().get(i);
+ final int mode = change.getMode();
+
+ if (mode == TRANSIT_CHANGE && change.getParent() != null) {
+ // TODO: perform resize/expand animation for reparented child task.
+ continue;
+ }
+
+ if (isOpeningType(mode) && change.getParent() == null) {
+ final SurfaceControl leash = change.getLeash();
+ final Rect endBounds = change.getEndAbsBounds();
+ startTransaction
+ .show(leash)
+ .setAlpha(leash, 1f)
+ .setPosition(leash, endBounds.left, endBounds.top)
+ .setWindowCrop(leash, endBounds.width(), endBounds.height());
+ }
+ }
+ mSplitScreenOptional.get().finishEnterSplitScreen(startTransaction);
+ startTransaction.apply();
+ finishCallback.onTransitionFinished(null, null);
+ return true;
+ }
+
private void finishResizeForMenu(Rect destinationBounds) {
mPipMenuController.movePipMenu(null, null, destinationBounds);
mPipMenuController.updateMenuBounds(destinationBounds);
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipTransitionController.java b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipTransitionController.java
index 376f329..1c8b9bc 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipTransitionController.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipTransitionController.java
@@ -20,6 +20,7 @@
import static com.android.wm.shell.pip.PipAnimationController.TRANSITION_DIRECTION_REMOVE_STACK;
+import android.annotation.Nullable;
import android.app.PictureInPictureParams;
import android.app.TaskInfo;
import android.content.ComponentName;
@@ -98,9 +99,10 @@
}
/**
- * Called when the Shell wants to starts a transition/animation.
+ * Called when the Shell wants to start an exit Pip transition/animation.
*/
- public void startTransition(Rect destinationBounds, WindowContainerTransaction out) {
+ public void startExitTransition(int type, WindowContainerTransaction out,
+ @Nullable Rect destinationBounds) {
// Default implementation does nothing.
}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/recents/RecentTasksController.java b/libs/WindowManager/Shell/src/com/android/wm/shell/recents/RecentTasksController.java
index 3de59b4..7decb54 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/recents/RecentTasksController.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/recents/RecentTasksController.java
@@ -292,6 +292,7 @@
* Invalidates this instance, preventing future calls from updating the controller.
*/
void invalidate() {
+ Slog.d("b/206648922", "invalidating controller: " + mController);
mController = null;
}
@@ -317,7 +318,8 @@
(controller) -> out[0] = controller.getRecentTasks(maxNum, flags, userId)
.toArray(new GroupedRecentTaskInfo[0]),
true /* blocking */);
- Slog.d("b/206648922", "getRecentTasks(" + maxNum + "): " + out[0]);
+ Slog.d("b/206648922", "getRecentTasks(" + maxNum + "): " + out[0]
+ + " mController=" + mController);
return out[0];
}
}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/SplitScreenController.java b/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/SplitScreenController.java
index fd1d0f9..6921448 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/SplitScreenController.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/SplitScreenController.java
@@ -239,6 +239,15 @@
enterSplitScreen(taskId, leftOrTop, new WindowContainerTransaction());
}
+ public void prepareEnterSplitScreen(WindowContainerTransaction wct,
+ ActivityManager.RunningTaskInfo taskInfo, int startPosition) {
+ mStageCoordinator.prepareEnterSplitScreen(wct, taskInfo, startPosition);
+ }
+
+ public void finishEnterSplitScreen(SurfaceControl.Transaction t) {
+ mStageCoordinator.finishEnterSplitScreen(t);
+ }
+
public void enterSplitScreen(int taskId, boolean leftOrTop, WindowContainerTransaction wct) {
final int stageType = isSplitScreenVisible() ? STAGE_TYPE_UNDEFINED : STAGE_TYPE_SIDE;
final int stagePosition =
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/SplitScreenTransitions.java b/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/SplitScreenTransitions.java
index 54d8ece..0aa8d7e 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/SplitScreenTransitions.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/SplitScreenTransitions.java
@@ -28,7 +28,8 @@
import static com.android.wm.shell.splitscreen.SplitScreenController.exitReasonToString;
import static com.android.wm.shell.transition.Transitions.TRANSIT_SPLIT_DISMISS;
import static com.android.wm.shell.transition.Transitions.TRANSIT_SPLIT_DISMISS_SNAP;
-import static com.android.wm.shell.transition.Transitions.isOpeningType;
+import static com.android.wm.shell.transition.Transitions.TRANSIT_SPLIT_SCREEN_OPEN_TO_SIDE;
+import static com.android.wm.shell.transition.Transitions.TRANSIT_SPLIT_SCREEN_PAIR_OPEN;
import android.animation.Animator;
import android.animation.AnimatorListenerAdapter;
@@ -148,7 +149,7 @@
t.setWindowCrop(leash, change.getEndAbsBounds().width(),
change.getEndAbsBounds().height());
}
- boolean isOpening = isOpeningType(info.getType());
+ boolean isOpening = isOpeningTransition(info);
if (isOpening && (mode == TRANSIT_OPEN || mode == TRANSIT_TO_FRONT)) {
// fade in
startExampleAnimation(leash, true /* show */);
@@ -305,6 +306,12 @@
mTransitions.getAnimExecutor().execute(va::start);
}
+ private boolean isOpeningTransition(TransitionInfo info) {
+ return Transitions.isOpeningType(info.getType())
+ || info.getType() == TRANSIT_SPLIT_SCREEN_OPEN_TO_SIDE
+ || info.getType() == TRANSIT_SPLIT_SCREEN_PAIR_OPEN;
+ }
+
/** Bundled information of dismiss transition. */
static class DismissTransition {
IBinder mTransition;
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/StageCoordinator.java b/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/StageCoordinator.java
index e2bba76..4c28be0 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/StageCoordinator.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/StageCoordinator.java
@@ -46,6 +46,7 @@
import static com.android.wm.shell.splitscreen.SplitScreenController.exitReasonToString;
import static com.android.wm.shell.splitscreen.SplitScreenTransitions.FLAG_IS_DIVIDER_BAR;
import static com.android.wm.shell.transition.Transitions.ENABLE_SHELL_TRANSITIONS;
+import static com.android.wm.shell.transition.Transitions.TRANSIT_SPLIT_SCREEN_OPEN_TO_SIDE;
import static com.android.wm.shell.transition.Transitions.TRANSIT_SPLIT_SCREEN_PAIR_OPEN;
import static com.android.wm.shell.transition.Transitions.isClosingType;
import static com.android.wm.shell.transition.Transitions.isOpeningType;
@@ -323,7 +324,14 @@
if (!evictWct.isEmpty()) {
wct.merge(evictWct, true /* transfer */);
}
- mTaskOrganizer.applyTransaction(wct);
+
+ if (ENABLE_SHELL_TRANSITIONS) {
+ prepareEnterSplitScreen(wct);
+ mSplitTransitions.startEnterTransition(TRANSIT_SPLIT_SCREEN_OPEN_TO_SIDE,
+ wct, null, this);
+ } else {
+ mTaskOrganizer.applyTransaction(wct);
+ }
return true;
}
@@ -720,12 +728,47 @@
* an existing WindowContainerTransaction (rather than applying immediately). This is intended
* to be used when exiting split might be bundled with other window operations.
*/
- void prepareExitSplitScreen(@StageType int stageToTop,
+ private void prepareExitSplitScreen(@StageType int stageToTop,
@NonNull WindowContainerTransaction wct) {
+ if (!mMainStage.isActive()) return;
mSideStage.removeAllTasks(wct, stageToTop == STAGE_TYPE_SIDE);
mMainStage.deactivate(wct, stageToTop == STAGE_TYPE_MAIN);
}
+ private void prepareEnterSplitScreen(WindowContainerTransaction wct) {
+ prepareEnterSplitScreen(wct, null /* taskInfo */, SPLIT_POSITION_UNDEFINED);
+ }
+
+ /**
+ * Prepare transaction to active split screen. If there's a task indicated, the task will be put
+ * into side stage.
+ */
+ void prepareEnterSplitScreen(WindowContainerTransaction wct,
+ @Nullable ActivityManager.RunningTaskInfo taskInfo, @SplitPosition int startPosition) {
+ if (mMainStage.isActive()) return;
+
+ if (taskInfo != null) {
+ setSideStagePosition(startPosition, wct);
+ mSideStage.addTask(taskInfo, wct);
+ }
+ mMainStage.activate(getMainStageBounds(), wct, true /* includingTopTask */);
+ mSideStage.moveToTop(getSideStageBounds(), wct);
+ }
+
+ void finishEnterSplitScreen(SurfaceControl.Transaction t) {
+ mSplitLayout.init();
+ setDividerVisibility(true, t);
+ setSplitsVisible(true);
+ mShouldUpdateRecents = true;
+ updateRecentTasksSplitPair();
+ if (!mLogger.hasStartedSession()) {
+ mLogger.logEnter(mSplitLayout.getDividerPositionAsFraction(),
+ getMainStagePosition(), mMainStage.getTopChildTaskUid(),
+ getSideStagePosition(), mSideStage.getTopChildTaskUid(),
+ mSplitLayout.isLandscape());
+ }
+ }
+
void getStageBounds(Rect outTopOrLeftBounds, Rect outBottomOrRightBounds) {
outTopOrLeftBounds.set(mSplitLayout.getBounds1());
outBottomOrRightBounds.set(mSplitLayout.getBounds2());
@@ -932,8 +975,7 @@
final WindowContainerTransaction wct = new WindowContainerTransaction();
mSplitLayout.init();
// Make sure the main stage is active.
- mMainStage.activate(getMainStageBounds(), wct, true /* reparent */);
- mSideStage.moveToTop(getSideStageBounds(), wct);
+ prepareEnterSplitScreen(wct);
mSyncQueue.queue(wct);
mSyncQueue.runInSync(t -> {
updateSurfaceBounds(mSplitLayout, t);
@@ -1237,8 +1279,7 @@
if (isOpening && getStageOfTask(triggerTask) != null) {
// One task is appearing into split, prepare to enter split screen.
out = new WindowContainerTransaction();
- mMainStage.activate(getMainStageBounds(), out, true /* includingTopTask */);
- mSideStage.moveToTop(getSideStageBounds(), out);
+ prepareEnterSplitScreen(out);
mSplitTransitions.mPendingEnter = transition;
}
}
@@ -1250,18 +1291,7 @@
// Once the pending enter transition got merged, make sure to bring divider bar visible and
// clear the pending transition from cache to prevent mess-up the following state.
if (transition == mSplitTransitions.mPendingEnter) {
- mSplitLayout.init();
- setDividerVisibility(true, null /* transaction */);
- setSplitsVisible(true);
- mShouldUpdateRecents = true;
- updateRecentTasksSplitPair();
-
- if (!mLogger.hasStartedSession()) {
- mLogger.logEnter(mSplitLayout.getDividerPositionAsFraction(),
- getMainStagePosition(), mMainStage.getTopChildTaskUid(),
- getSideStagePosition(), mSideStage.getTopChildTaskUid(),
- mSplitLayout.isLandscape());
- }
+ finishEnterSplitScreen(null);
mSplitTransitions.mPendingEnter = null;
}
}
@@ -1349,47 +1379,40 @@
sideChild = change;
}
}
- if (mainChild == null || sideChild == null) {
- throw new IllegalStateException("Launched 2 tasks in split, but didn't receive"
- + " 2 tasks in transition. Possibly one of them failed to launch");
- // TODO: fallback logic. Probably start a new transition to exit split before
- // applying anything here. Ideally consolidate with transition-merging.
+
+ // TODO: fallback logic. Probably start a new transition to exit split before applying
+ // anything here. Ideally consolidate with transition-merging.
+ if (info.getType() == TRANSIT_SPLIT_SCREEN_OPEN_TO_SIDE) {
+ if (mainChild == null && sideChild == null) {
+ throw new IllegalStateException("Launched a task in split, but didn't receive any"
+ + " task in transition.");
+ }
+ } else {
+ if (mainChild == null || sideChild == null) {
+ throw new IllegalStateException("Launched 2 tasks in split, but didn't receive"
+ + " 2 tasks in transition. Possibly one of them failed to launch");
+ }
}
- // Update local states (before animating).
- mSplitLayout.init();
- setDividerVisibility(true, t);
- setSplitsVisible(true);
-
- addDividerBarToTransition(info, t, true /* show */);
-
// Make some noise if things aren't totally expected. These states shouldn't effect
// transitions locally, but remotes (like Launcher) may get confused if they were
// depending on listener callbacks. This can happen because task-organizer callbacks
// aren't serialized with transition callbacks.
// TODO(b/184679596): Find a way to either include task-org information in
// the transition, or synchronize task-org callbacks.
- if (!mMainStage.containsTask(mainChild.getTaskInfo().taskId)) {
+ if (mainChild != null && !mMainStage.containsTask(mainChild.getTaskInfo().taskId)) {
Log.w(TAG, "Expected onTaskAppeared on " + mMainStage
+ " to have been called with " + mainChild.getTaskInfo().taskId
+ " before startAnimation().");
}
- if (!mSideStage.containsTask(sideChild.getTaskInfo().taskId)) {
+ if (sideChild != null && !mSideStage.containsTask(sideChild.getTaskInfo().taskId)) {
Log.w(TAG, "Expected onTaskAppeared on " + mSideStage
+ " to have been called with " + sideChild.getTaskInfo().taskId
+ " before startAnimation().");
}
- mShouldUpdateRecents = true;
- updateRecentTasksSplitPair();
-
- if (!mLogger.hasStartedSession()) {
- mLogger.logEnter(mSplitLayout.getDividerPositionAsFraction(),
- getMainStagePosition(), mMainStage.getTopChildTaskUid(),
- getSideStagePosition(), mSideStage.getTopChildTaskUid(),
- mSplitLayout.isLandscape());
- }
-
+ finishEnterSplitScreen(t);
+ addDividerBarToTransition(info, t, true /* show */);
return true;
}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/StageTaskListener.java b/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/StageTaskListener.java
index 04e20db..83534c1 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/StageTaskListener.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/StageTaskListener.java
@@ -302,6 +302,11 @@
}
void addTask(ActivityManager.RunningTaskInfo task, WindowContainerTransaction wct) {
+ // Clear overridden bounds and windowing mode to make sure the child task can inherit
+ // windowing mode and bounds from split root.
+ wct.setWindowingMode(task.token, WINDOWING_MODE_UNDEFINED)
+ .setBounds(task.token, null);
+
wct.reparent(task.token, mRootTaskInfo.token, true /* onTop*/);
}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/transition/Transitions.java b/libs/WindowManager/Shell/src/com/android/wm/shell/transition/Transitions.java
index b8cbfd9..711510d 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/transition/Transitions.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/transition/Transitions.java
@@ -74,23 +74,25 @@
public static final boolean ENABLE_SHELL_TRANSITIONS =
SystemProperties.getBoolean("persist.debug.shell_transit", false);
- /** Transition type for dismissing split-screen via dragging the divider off the screen. */
- public static final int TRANSIT_SPLIT_DISMISS_SNAP = TRANSIT_FIRST_CUSTOM + 1;
-
- /** Transition type for launching 2 tasks simultaneously. */
- public static final int TRANSIT_SPLIT_SCREEN_PAIR_OPEN = TRANSIT_FIRST_CUSTOM + 2;
-
/** Transition type for exiting PIP via the Shell, via pressing the expand button. */
- public static final int TRANSIT_EXIT_PIP = TRANSIT_FIRST_CUSTOM + 3;
+ public static final int TRANSIT_EXIT_PIP = TRANSIT_FIRST_CUSTOM + 1;
+
+ public static final int TRANSIT_EXIT_PIP_TO_SPLIT = TRANSIT_FIRST_CUSTOM + 2;
/** Transition type for removing PIP via the Shell, either via Dismiss bubble or Close. */
- public static final int TRANSIT_REMOVE_PIP = TRANSIT_FIRST_CUSTOM + 4;
+ public static final int TRANSIT_REMOVE_PIP = TRANSIT_FIRST_CUSTOM + 3;
+
+ /** Transition type for launching 2 tasks simultaneously. */
+ public static final int TRANSIT_SPLIT_SCREEN_PAIR_OPEN = TRANSIT_FIRST_CUSTOM + 4;
/** Transition type for entering split by opening an app into side-stage. */
public static final int TRANSIT_SPLIT_SCREEN_OPEN_TO_SIDE = TRANSIT_FIRST_CUSTOM + 5;
+ /** Transition type for dismissing split-screen via dragging the divider off the screen. */
+ public static final int TRANSIT_SPLIT_DISMISS_SNAP = TRANSIT_FIRST_CUSTOM + 6;
+
/** Transition type for dismissing split-screen. */
- public static final int TRANSIT_SPLIT_DISMISS = TRANSIT_FIRST_CUSTOM + 6;
+ public static final int TRANSIT_SPLIT_DISMISS = TRANSIT_FIRST_CUSTOM + 7;
private final WindowOrganizer mOrganizer;
private final Context mContext;
diff --git a/media/java/android/media/tv/AdRequest.aidl b/media/java/android/media/tv/AdRequest.aidl
new file mode 100644
index 0000000..ebfd748
--- /dev/null
+++ b/media/java/android/media/tv/AdRequest.aidl
@@ -0,0 +1,19 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.media.tv;
+
+parcelable AdRequest;
diff --git a/media/java/android/media/tv/AdRequest.java b/media/java/android/media/tv/AdRequest.java
new file mode 100644
index 0000000..536baf2
--- /dev/null
+++ b/media/java/android/media/tv/AdRequest.java
@@ -0,0 +1,135 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.media.tv;
+
+import android.annotation.NonNull;
+import android.annotation.StringDef;
+import android.os.Bundle;
+import android.os.Parcel;
+import android.os.ParcelFileDescriptor;
+import android.os.Parcelable;
+
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+
+/** @hide */
+public final class AdRequest implements Parcelable {
+ @Retention(RetentionPolicy.SOURCE)
+ @StringDef(prefix = "REQUEST_TYPE_", value = {
+ REQUEST_TYPE_START,
+ REQUEST_TYPE_STOP
+ })
+ public @interface RequestType {}
+
+ public static final String REQUEST_TYPE_START = "START";
+ public static final String REQUEST_TYPE_STOP = "STOP";
+
+ public static final @NonNull Parcelable.Creator<AdRequest> CREATOR =
+ new Parcelable.Creator<AdRequest>() {
+ @Override
+ public AdRequest createFromParcel(Parcel source) {
+ return new AdRequest(source);
+ }
+
+ @Override
+ public AdRequest[] newArray(int size) {
+ return new AdRequest[size];
+ }
+ };
+
+ private final int mId;
+ private final @RequestType String mRequestType;
+ private final ParcelFileDescriptor mFileDescriptor;
+ private final long mStartTime;
+ private final long mStopTime;
+ private final long mEchoInterval;
+ private final String mMediaFileType;
+ private final Bundle mMetadata;
+
+ public AdRequest(int id, @RequestType String requestType, ParcelFileDescriptor fileDescriptor,
+ long startTime, long stopTime, long echoInterval, String mediaFileType,
+ Bundle metadata) {
+ mId = id;
+ mRequestType = requestType;
+ mFileDescriptor = fileDescriptor;
+ mStartTime = startTime;
+ mStopTime = stopTime;
+ mEchoInterval = echoInterval;
+ mMediaFileType = mediaFileType;
+ mMetadata = metadata;
+ }
+
+ private AdRequest(Parcel source) {
+ mId = source.readInt();
+ mRequestType = source.readString();
+ mFileDescriptor = source.readFileDescriptor();
+ mStartTime = source.readLong();
+ mStopTime = source.readLong();
+ mEchoInterval = source.readLong();
+ mMediaFileType = source.readString();
+ mMetadata = source.readBundle();
+ }
+
+ public int getId() {
+ return mId;
+ }
+
+ public @RequestType String getRequestType() {
+ return mRequestType;
+ }
+
+ public ParcelFileDescriptor getFileDescriptor() {
+ return mFileDescriptor;
+ }
+
+ public long getStartTime() {
+ return mStartTime;
+ }
+
+ public long getStopTime() {
+ return mStopTime;
+ }
+
+ public long getEchoInterval() {
+ return mEchoInterval;
+ }
+
+ public String getMediaFileType() {
+ return mMediaFileType;
+ }
+
+ public Bundle getMetadata() {
+ return mMetadata;
+ }
+
+ @Override
+ public int describeContents() {
+ return 0;
+ }
+
+ @Override
+ public void writeToParcel(@NonNull Parcel dest, int flags) {
+ dest.writeInt(mId);
+ dest.writeString(mRequestType);
+ mFileDescriptor.writeToParcel(dest, flags);
+ dest.writeLong(mStartTime);
+ dest.writeLong(mStopTime);
+ dest.writeLong(mEchoInterval);
+ dest.writeString(mMediaFileType);
+ dest.writeBundle(mMetadata);
+ }
+}
diff --git a/media/java/android/media/tv/AdResponse.aidl b/media/java/android/media/tv/AdResponse.aidl
new file mode 100644
index 0000000..9c09a0a
--- /dev/null
+++ b/media/java/android/media/tv/AdResponse.aidl
@@ -0,0 +1,19 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.media.tv;
+
+parcelable AdResponse;
diff --git a/media/java/android/media/tv/AdResponse.java b/media/java/android/media/tv/AdResponse.java
new file mode 100644
index 0000000..28cf5ac
--- /dev/null
+++ b/media/java/android/media/tv/AdResponse.java
@@ -0,0 +1,96 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.media.tv;
+
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.annotation.StringDef;
+import android.os.Parcel;
+import android.os.Parcelable;
+
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+
+/** @hide */
+public final class AdResponse implements Parcelable {
+ @Retention(RetentionPolicy.SOURCE)
+ @StringDef(prefix = "RESPONSE_TYPE_", value = {
+ RESPONSE_TYPE_PLAYING,
+ RESPONSE_TYPE_FINISHED,
+ RESPONSE_TYPE_STOPPED,
+ RESPONSE_TYPE_ERROR
+ })
+ public @interface ResponseType {}
+
+ public static final String RESPONSE_TYPE_PLAYING = "PLAYING";
+ public static final String RESPONSE_TYPE_FINISHED = "FINISHED";
+ public static final String RESPONSE_TYPE_STOPPED = "STOPPED";
+ public static final String RESPONSE_TYPE_ERROR = "ERROR";
+
+ public static final @NonNull Parcelable.Creator<AdResponse> CREATOR =
+ new Parcelable.Creator<AdResponse>() {
+ @Override
+ public AdResponse createFromParcel(Parcel source) {
+ return new AdResponse(source);
+ }
+
+ @Override
+ public AdResponse[] newArray(int size) {
+ return new AdResponse[size];
+ }
+ };
+
+ private final int mId;
+ private final @ResponseType String mResponseType;
+ private final Long mElapsedTime;
+
+ public AdResponse(int id, @ResponseType String responseType, @Nullable Long elapsedTime) {
+ mId = id;
+ mResponseType = responseType;
+ mElapsedTime = elapsedTime;
+ }
+
+ private AdResponse(Parcel source) {
+ mId = source.readInt();
+ mResponseType = source.readString();
+ mElapsedTime = (Long) source.readValue(Long.class.getClassLoader());
+ }
+
+ public int getId() {
+ return mId;
+ }
+
+ public @ResponseType String getResponseType() {
+ return mResponseType;
+ }
+
+ public Long getElapsedTime() {
+ return mElapsedTime;
+ }
+
+ @Override
+ public int describeContents() {
+ return 0;
+ }
+
+ @Override
+ public void writeToParcel(@NonNull Parcel dest, int flags) {
+ dest.writeInt(mId);
+ dest.writeString(mResponseType);
+ dest.writeValue(mElapsedTime);
+ }
+}
diff --git a/media/java/android/media/tv/ITvInputClient.aidl b/media/java/android/media/tv/ITvInputClient.aidl
index de13fc1..f4f55e4 100644
--- a/media/java/android/media/tv/ITvInputClient.aidl
+++ b/media/java/android/media/tv/ITvInputClient.aidl
@@ -17,6 +17,7 @@
package android.media.tv;
import android.content.ComponentName;
+import android.media.tv.AdResponse;
import android.media.tv.AitInfo;
import android.media.tv.BroadcastInfoResponse;
import android.media.tv.ITvInputSession;
@@ -54,4 +55,7 @@
// For broadcast info
void onBroadcastInfoResponse(in BroadcastInfoResponse response, int seq);
+
+ // For ad response
+ void onAdResponse(in AdResponse response, int seq);
}
diff --git a/media/java/android/media/tv/ITvInputManager.aidl b/media/java/android/media/tv/ITvInputManager.aidl
index a6e0217..d34e636 100644
--- a/media/java/android/media/tv/ITvInputManager.aidl
+++ b/media/java/android/media/tv/ITvInputManager.aidl
@@ -20,6 +20,7 @@
import android.content.Intent;
import android.graphics.Rect;
import android.media.PlaybackParams;
+import android.media.tv.AdRequest;
import android.media.tv.BroadcastInfoRequest;
import android.media.tv.DvbDeviceInfo;
import android.media.tv.ITvInputClient;
@@ -106,6 +107,9 @@
void requestBroadcastInfo(in IBinder sessionToken, in BroadcastInfoRequest request, int userId);
void removeBroadcastInfo(in IBinder sessionToken, int id, int userId);
+ // For ad request
+ void requestAd(in IBinder sessionToken, in AdRequest request, int userId);
+
// For TV input hardware binding
List<TvInputHardwareInfo> getHardwareList();
ITvInputHardware acquireTvInputHardware(int deviceId, in ITvInputHardwareCallback callback,
diff --git a/media/java/android/media/tv/ITvInputSession.aidl b/media/java/android/media/tv/ITvInputSession.aidl
index 025e6f1c..f427501 100644
--- a/media/java/android/media/tv/ITvInputSession.aidl
+++ b/media/java/android/media/tv/ITvInputSession.aidl
@@ -18,6 +18,7 @@
import android.graphics.Rect;
import android.media.PlaybackParams;
+import android.media.tv.AdRequest;
import android.media.tv.BroadcastInfoRequest;
import android.media.tv.TvTrackInfo;
import android.net.Uri;
@@ -67,4 +68,7 @@
// For broadcast info
void requestBroadcastInfo(in BroadcastInfoRequest request);
void removeBroadcastInfo(int id);
+
+ // For ad request
+ void requestAd(in AdRequest request);
}
diff --git a/media/java/android/media/tv/ITvInputSessionCallback.aidl b/media/java/android/media/tv/ITvInputSessionCallback.aidl
index 9a0aaa3..9830e78 100644
--- a/media/java/android/media/tv/ITvInputSessionCallback.aidl
+++ b/media/java/android/media/tv/ITvInputSessionCallback.aidl
@@ -16,6 +16,7 @@
package android.media.tv;
+import android.media.tv.AdResponse;
import android.media.tv.AitInfo;
import android.media.tv.BroadcastInfoResponse;
import android.media.tv.ITvInputSession;
@@ -51,4 +52,7 @@
// For broadcast info
void onBroadcastInfoResponse(in BroadcastInfoResponse response);
+
+ // For ad response
+ void onAdResponse(in AdResponse response);
}
diff --git a/media/java/android/media/tv/ITvInputSessionWrapper.java b/media/java/android/media/tv/ITvInputSessionWrapper.java
index ef6ef91..418ab2c 100644
--- a/media/java/android/media/tv/ITvInputSessionWrapper.java
+++ b/media/java/android/media/tv/ITvInputSessionWrapper.java
@@ -73,6 +73,7 @@
private static final int DO_REQUEST_BROADCAST_INFO = 24;
private static final int DO_REMOVE_BROADCAST_INFO = 25;
private static final int DO_SET_IAPP_NOTIFICATION_ENABLED = 26;
+ private static final int DO_REQUEST_AD = 27;
private final boolean mIsRecordingSession;
private final HandlerCaller mCaller;
@@ -249,6 +250,10 @@
mTvInputSessionImpl.setIAppNotificationEnabled((Boolean) msg.obj);
break;
}
+ case DO_REQUEST_AD: {
+ mTvInputSessionImpl.requestAd((AdRequest) msg.obj);
+ break;
+ }
default: {
Log.w(TAG, "Unhandled message code: " + msg.what);
break;
@@ -414,6 +419,11 @@
mCaller.executeOrSendMessage(mCaller.obtainMessageI(DO_REMOVE_BROADCAST_INFO, requestId));
}
+ @Override
+ public void requestAd(AdRequest request) {
+ mCaller.executeOrSendMessage(mCaller.obtainMessageO(DO_REQUEST_AD, request));
+ }
+
private final class TvInputEventReceiver extends InputEventReceiver {
public TvInputEventReceiver(InputChannel inputChannel, Looper looper) {
super(inputChannel, looper);
diff --git a/media/java/android/media/tv/TvInputManager.java b/media/java/android/media/tv/TvInputManager.java
index cde2f5a..ad86002 100644
--- a/media/java/android/media/tv/TvInputManager.java
+++ b/media/java/android/media/tv/TvInputManager.java
@@ -894,6 +894,19 @@
});
}
}
+
+ void postAdResponse(final AdResponse response) {
+ if (mSession.mIAppNotificationEnabled) {
+ mHandler.post(new Runnable() {
+ @Override
+ public void run() {
+ if (mSession.getIAppSession() != null) {
+ mSession.getIAppSession().notifyAdResponse(response);
+ }
+ }
+ });
+ }
+ }
}
/**
@@ -1317,6 +1330,18 @@
record.postBroadcastInfoResponse(response);
}
}
+
+ @Override
+ public void onAdResponse(AdResponse response, int seq) {
+ synchronized (mSessionCallbackRecordMap) {
+ SessionCallbackRecord record = mSessionCallbackRecordMap.get(seq);
+ if (record == null) {
+ Log.e(TAG, "Callback not found for seq " + seq);
+ return;
+ }
+ record.postAdResponse(response);
+ }
+ }
};
ITvInputManagerCallback managerCallback = new ITvInputManagerCallback.Stub() {
@Override
@@ -3033,6 +3058,18 @@
}
}
+ public void requestAd(AdRequest request) {
+ if (mToken == null) {
+ Log.w(TAG, "The session has been already released");
+ return;
+ }
+ try {
+ mService.requestAd(mToken, request, mUserId);
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
+ }
+ }
+
private final class InputEventHandler extends Handler {
public static final int MSG_SEND_INPUT_EVENT = 1;
public static final int MSG_TIMEOUT_INPUT_EVENT = 2;
diff --git a/media/java/android/media/tv/TvInputService.java b/media/java/android/media/tv/TvInputService.java
index ee33e5c..bd5a343 100755
--- a/media/java/android/media/tv/TvInputService.java
+++ b/media/java/android/media/tv/TvInputService.java
@@ -849,7 +849,7 @@
/**
* Notifies response for broadcast info.
*
- * @param response
+ * @param response broadcast info response.
* @hide
*/
public void notifyBroadcastInfoResponse(@NonNull final BroadcastInfoResponse response) {
@@ -869,6 +869,29 @@
});
}
+ /**
+ * Notifies response for advertisement.
+ *
+ * @param response advertisement response.
+ * @hide
+ */
+ public void notifyAdResponse(@NonNull final AdResponse response) {
+ executeOrPostRunnableOnMainThread(new Runnable() {
+ @MainThread
+ @Override
+ public void run() {
+ try {
+ if (DEBUG) Log.d(TAG, "notifyAdResponse");
+ if (mSessionCallback != null) {
+ mSessionCallback.onAdResponse(response);
+ }
+ } catch (RemoteException e) {
+ Log.w(TAG, "error in notifyAdResponse", e);
+ }
+ }
+ });
+ }
+
private void notifyTimeShiftStartPositionChanged(final long timeMs) {
executeOrPostRunnableOnMainThread(new Runnable() {
@MainThread
@@ -1038,6 +1061,8 @@
public abstract void onSetStreamVolume(@FloatRange(from = 0.0, to = 1.0) float volume);
/**
+ * called when broadcast info is requested.
+ *
* @param request broadcast info request
* @hide
*/
@@ -1051,6 +1076,15 @@
}
/**
+ * called when advertisement is requested.
+ *
+ * @param request advertisement request
+ * @hide
+ */
+ public void onRequestAd(@NonNull AdRequest request) {
+ }
+
+ /**
* Tunes to a given channel.
*
* <p>No video will be displayed until {@link #notifyVideoAvailable()} is called.
@@ -1663,6 +1697,10 @@
onRemoveBroadcastInfo(requestId);
}
+ void requestAd(AdRequest request) {
+ onRequestAd(request);
+ }
+
/**
* Takes care of dispatching incoming input events and tells whether the event was handled.
*/
diff --git a/media/java/android/media/tv/interactive/ITvIAppClient.aidl b/media/java/android/media/tv/interactive/ITvIAppClient.aidl
index 00c464f..892a800 100644
--- a/media/java/android/media/tv/interactive/ITvIAppClient.aidl
+++ b/media/java/android/media/tv/interactive/ITvIAppClient.aidl
@@ -17,6 +17,7 @@
package android.media.tv.interactive;
import android.graphics.Rect;
+import android.media.tv.AdRequest;
import android.media.tv.BroadcastInfoRequest;
import android.net.Uri;
import android.os.Bundle;
@@ -41,4 +42,5 @@
void onRequestCurrentChannelLcn(int seq);
void onRequestStreamVolume(int seq);
void onRequestTrackInfoList(int seq);
+ void onAdRequest(in AdRequest request, int Seq);
}
diff --git a/media/java/android/media/tv/interactive/ITvIAppManager.aidl b/media/java/android/media/tv/interactive/ITvIAppManager.aidl
index 15a2eace..23201fa 100644
--- a/media/java/android/media/tv/interactive/ITvIAppManager.aidl
+++ b/media/java/android/media/tv/interactive/ITvIAppManager.aidl
@@ -17,6 +17,7 @@
package android.media.tv.interactive;
import android.graphics.Rect;
+import android.media.tv.AdResponse;
import android.media.tv.BroadcastInfoResponse;
import android.media.tv.TvTrackInfo;
import android.media.tv.interactive.ITvIAppClient;
@@ -55,6 +56,7 @@
int userId);
void notifyBroadcastInfoResponse(in IBinder sessionToken, in BroadcastInfoResponse response,
int UserId);
+ void notifyAdResponse(in IBinder sessionToken, in AdResponse response, int UserId);
void createMediaView(in IBinder sessionToken, in IBinder windowToken, in Rect frame,
int userId);
@@ -63,4 +65,4 @@
void registerCallback(in ITvIAppManagerCallback callback, int userId);
void unregisterCallback(in ITvIAppManagerCallback callback, int userId);
-}
\ No newline at end of file
+}
diff --git a/media/java/android/media/tv/interactive/ITvIAppSession.aidl b/media/java/android/media/tv/interactive/ITvIAppSession.aidl
index 4a85fef..52f9a87 100644
--- a/media/java/android/media/tv/interactive/ITvIAppSession.aidl
+++ b/media/java/android/media/tv/interactive/ITvIAppSession.aidl
@@ -19,6 +19,7 @@
import android.graphics.Rect;
import android.media.tv.BroadcastInfoResponse;
import android.net.Uri;
+import android.media.tv.AdResponse;
import android.media.tv.BroadcastInfoResponse;
import android.media.tv.TvTrackInfo;
import android.os.Bundle;
@@ -44,8 +45,9 @@
void setSurface(in Surface surface);
void dispatchSurfaceChanged(int format, int width, int height);
void notifyBroadcastInfoResponse(in BroadcastInfoResponse response);
+ void notifyAdResponse(in AdResponse response);
void createMediaView(in IBinder windowToken, in Rect frame);
void relayoutMediaView(in Rect frame);
void removeMediaView();
-}
\ No newline at end of file
+}
diff --git a/media/java/android/media/tv/interactive/ITvIAppSessionCallback.aidl b/media/java/android/media/tv/interactive/ITvIAppSessionCallback.aidl
index b09f787..9b9e6af 100644
--- a/media/java/android/media/tv/interactive/ITvIAppSessionCallback.aidl
+++ b/media/java/android/media/tv/interactive/ITvIAppSessionCallback.aidl
@@ -17,9 +17,9 @@
package android.media.tv.interactive;
import android.graphics.Rect;
+import android.media.tv.AdRequest;
import android.media.tv.BroadcastInfoRequest;
import android.media.tv.interactive.ITvIAppSession;
-import android.media.tv.BroadcastInfoRequest;
import android.net.Uri;
import android.os.Bundle;
@@ -41,4 +41,5 @@
void onRequestCurrentChannelLcn();
void onRequestStreamVolume();
void onRequestTrackInfoList();
+ void onAdRequest(in AdRequest request);
}
diff --git a/media/java/android/media/tv/interactive/TvIAppInfo.java b/media/java/android/media/tv/interactive/TvIAppInfo.java
index 6d5e561..79d94cc 100644
--- a/media/java/android/media/tv/interactive/TvIAppInfo.java
+++ b/media/java/android/media/tv/interactive/TvIAppInfo.java
@@ -17,6 +17,7 @@
package android.media.tv.interactive;
import android.annotation.NonNull;
+import android.annotation.StringDef;
import android.content.ComponentName;
import android.content.Context;
import android.content.Intent;
@@ -35,6 +36,8 @@
import org.xmlpull.v1.XmlPullParserException;
import java.io.IOException;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
import java.util.ArrayList;
import java.util.List;
@@ -46,6 +49,21 @@
private static final boolean DEBUG = false;
private static final String TAG = "TvIAppInfo";
+ @Retention(RetentionPolicy.SOURCE)
+ @StringDef(prefix = { "INTERACTIVE_APP_TYPE_" }, value = {
+ INTERACTIVE_APP_TYPE_HBBTV,
+ INTERACTIVE_APP_TYPE_ATSC,
+ INTERACTIVE_APP_TYPE_GINGA,
+ })
+ @interface InteractiveAppType {}
+
+ /** HbbTV interactive app type */
+ public static final String INTERACTIVE_APP_TYPE_HBBTV = "hbbtv";
+ /** ATSC interactive app type */
+ public static final String INTERACTIVE_APP_TYPE_ATSC = "atsc";
+ /** Ginga interactive app type */
+ public static final String INTERACTIVE_APP_TYPE_GINGA = "ginga";
+
private final ResolveInfo mService;
private final String mId;
private List<String> mTypes = new ArrayList<>();
@@ -107,6 +125,13 @@
}
/**
+ * Gets supported interactive app types
+ */
+ public List<String> getSupportedTypes() {
+ return new ArrayList<>(mTypes);
+ }
+
+ /**
* A convenience builder for creating {@link TvIAppInfo} objects.
*/
public static final class Builder {
@@ -186,7 +211,7 @@
CharSequence[] types = sa.getTextArray(
com.android.internal.R.styleable.TvIAppService_supportedTypes);
for (CharSequence cs : types) {
- mTypes.add(cs.toString());
+ mTypes.add(cs.toString().toLowerCase());
}
sa.recycle();
diff --git a/media/java/android/media/tv/interactive/TvIAppManager.java b/media/java/android/media/tv/interactive/TvIAppManager.java
index 4c4dffb..d1fd1df 100644
--- a/media/java/android/media/tv/interactive/TvIAppManager.java
+++ b/media/java/android/media/tv/interactive/TvIAppManager.java
@@ -22,6 +22,8 @@
import android.annotation.SystemService;
import android.content.Context;
import android.graphics.Rect;
+import android.media.tv.AdRequest;
+import android.media.tv.AdResponse;
import android.media.tv.BroadcastInfoRequest;
import android.media.tv.BroadcastInfoResponse;
import android.media.tv.TvInputManager;
@@ -282,6 +284,18 @@
}
@Override
+ public void onAdRequest(AdRequest request, int seq) {
+ synchronized (mSessionCallbackRecordMap) {
+ SessionCallbackRecord record = mSessionCallbackRecordMap.get(seq);
+ if (record == null) {
+ Log.e(TAG, "Callback not found for seq " + seq);
+ return;
+ }
+ record.postAdRequest(request);
+ }
+ }
+
+ @Override
public void onRequestCurrentChannelUri(int seq) {
synchronized (mSessionCallbackRecordMap) {
SessionCallbackRecord record = mSessionCallbackRecordMap.get(seq);
@@ -945,6 +959,23 @@
}
/**
+ * Notifies of any advertisement response passed in from TIS.
+ *
+ * @param response response passed in from TIS.
+ */
+ public void notifyAdResponse(AdResponse response) {
+ if (mToken == null) {
+ Log.w(TAG, "The session has been already released");
+ return;
+ }
+ try {
+ mService.notifyAdResponse(mToken, response, mUserId);
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
+ }
+ }
+
+ /**
* Releases this session.
*/
public void release() {
@@ -1322,6 +1353,17 @@
});
}
+ void postAdRequest(final AdRequest request) {
+ mHandler.post(new Runnable() {
+ @Override
+ public void run() {
+ if (mSession.getInputSession() != null) {
+ mSession.getInputSession().requestAd(request);
+ }
+ }
+ });
+ }
+
void postSessionStateChanged(int state) {
mHandler.post(new Runnable() {
@Override
diff --git a/media/java/android/media/tv/interactive/TvIAppService.java b/media/java/android/media/tv/interactive/TvIAppService.java
index a0f8413..1480ff64 100644
--- a/media/java/android/media/tv/interactive/TvIAppService.java
+++ b/media/java/android/media/tv/interactive/TvIAppService.java
@@ -27,6 +27,8 @@
import android.content.Intent;
import android.graphics.PixelFormat;
import android.graphics.Rect;
+import android.media.tv.AdRequest;
+import android.media.tv.AdResponse;
import android.media.tv.BroadcastInfoRequest;
import android.media.tv.BroadcastInfoResponse;
import android.media.tv.TvTrackInfo;
@@ -434,6 +436,13 @@
}
/**
+ * Called when an advertisement response is received.
+ * @hide
+ */
+ public void onAdResponse(AdResponse response) {
+ }
+
+ /**
* TODO: JavaDoc of APIs related to input events.
* @hide
*/
@@ -703,6 +712,29 @@
});
}
+ /**
+ * requests an advertisement request to be processed by the related TV input.
+ * @param request advertisement request
+ */
+ public void requestAd(@NonNull final AdRequest request) {
+ executeOrPostRunnableOnMainThread(new Runnable() {
+ @MainThread
+ @Override
+ public void run() {
+ try {
+ if (DEBUG) {
+ Log.d(TAG, "requestAd (id=" + request.getId() + ")");
+ }
+ if (mSessionCallback != null) {
+ mSessionCallback.onAdRequest(request);
+ }
+ } catch (RemoteException e) {
+ Log.w(TAG, "error in requestAd", e);
+ }
+ }
+ });
+ }
+
void startIApp() {
onStartIApp();
}
@@ -784,6 +816,16 @@
}
/**
+ * Calls {@link #onAdResponse}.
+ */
+ void notifyAdResponse(AdResponse response) {
+ if (DEBUG) {
+ Log.d(TAG, "notifyAdResponse (requestId=" + response.getId() + ")");
+ }
+ onAdResponse(response);
+ }
+
+ /**
* Notifies when the session state is changed.
* @param state the current state.
*/
@@ -1142,6 +1184,11 @@
}
@Override
+ public void notifyAdResponse(AdResponse response) {
+ mSessionImpl.notifyAdResponse(response);
+ }
+
+ @Override
public void createMediaView(IBinder windowToken, Rect frame) {
mSessionImpl.createMediaView(windowToken, frame);
}
diff --git a/media/java/android/media/tv/interactive/TvIAppView.java b/media/java/android/media/tv/interactive/TvIAppView.java
index bc015d1..1ce14ae 100644
--- a/media/java/android/media/tv/interactive/TvIAppView.java
+++ b/media/java/android/media/tv/interactive/TvIAppView.java
@@ -113,6 +113,10 @@
this(context, null, 0);
}
+ public TvIAppView(@NonNull Context context, @Nullable AttributeSet attrs) {
+ this(context, attrs, 0);
+ }
+
public TvIAppView(@NonNull Context context, @Nullable AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
int sourceResId = Resources.getAttributeSetSourceResId(attrs);
diff --git a/packages/ConnectivityT/framework-t/src/android/app/usage/NetworkStatsManager.java b/packages/ConnectivityT/framework-t/src/android/app/usage/NetworkStatsManager.java
index 2b9560e..a316b8a 100644
--- a/packages/ConnectivityT/framework-t/src/android/app/usage/NetworkStatsManager.java
+++ b/packages/ConnectivityT/framework-t/src/android/app/usage/NetworkStatsManager.java
@@ -47,7 +47,6 @@
import android.os.RemoteException;
import android.telephony.TelephonyManager;
import android.text.TextUtils;
-import android.util.DataUnit;
import android.util.Log;
import com.android.internal.annotations.VisibleForTesting;
@@ -119,7 +118,7 @@
* is reached.
* @hide
*/
- public static final long MIN_THRESHOLD_BYTES = DataUnit.MEBIBYTES.toBytes(2);
+ public static final long MIN_THRESHOLD_BYTES = 2 * 1_048_576L; // 2MiB
private final Context mContext;
private final INetworkStatsService mService;
diff --git a/packages/ConnectivityT/framework-t/src/android/net/NetworkIdentity.java b/packages/ConnectivityT/framework-t/src/android/net/NetworkIdentity.java
index eb8f43e..8f1115e 100644
--- a/packages/ConnectivityT/framework-t/src/android/net/NetworkIdentity.java
+++ b/packages/ConnectivityT/framework-t/src/android/net/NetworkIdentity.java
@@ -21,7 +21,6 @@
import android.annotation.Nullable;
import android.content.Context;
import android.net.wifi.WifiInfo;
-import android.net.wifi.WifiManager;
import android.service.NetworkIdentityProto;
import android.telephony.Annotation.NetworkType;
import android.util.proto.ProtoOutputStream;
@@ -228,11 +227,11 @@
final int oemManaged = getOemBitfield(snapshot.getNetworkCapabilities());
if (legacyType == TYPE_WIFI) {
- networkId = snapshot.getNetworkCapabilities().getSsid();
- if (networkId == null) {
- final WifiManager wifi = context.getSystemService(WifiManager.class);
- final WifiInfo info = wifi.getConnectionInfo();
- networkId = info != null ? info.getSSID() : null;
+ final TransportInfo transportInfo = snapshot.getNetworkCapabilities()
+ .getTransportInfo();
+ if (transportInfo instanceof WifiInfo) {
+ final WifiInfo info = (WifiInfo) transportInfo;
+ networkId = info != null ? info.getCurrentNetworkKey() : null;
}
}
diff --git a/packages/ConnectivityT/framework-t/src/android/net/NetworkStatsAccess.java b/packages/ConnectivityT/framework-t/src/android/net/NetworkStatsAccess.java
index 591605d9..b64fbdb 100644
--- a/packages/ConnectivityT/framework-t/src/android/net/NetworkStatsAccess.java
+++ b/packages/ConnectivityT/framework-t/src/android/net/NetworkStatsAccess.java
@@ -17,6 +17,7 @@
package android.net;
import static android.Manifest.permission.READ_NETWORK_USAGE_HISTORY;
+import static android.content.pm.PackageManager.PERMISSION_GRANTED;
import static android.net.NetworkStats.UID_ALL;
import static android.net.TrafficStats.UID_REMOVED;
import static android.net.TrafficStats.UID_TETHERING;
@@ -106,7 +107,7 @@
/** Returns the {@link NetworkStatsAccess.Level} for the given caller. */
public static @NetworkStatsAccess.Level int checkAccessLevel(
- Context context, int callingUid, String callingPackage) {
+ Context context, int callingPid, int callingUid, String callingPackage) {
final DevicePolicyManager mDpm = context.getSystemService(DevicePolicyManager.class);
final TelephonyManager tm = (TelephonyManager)
context.getSystemService(Context.TELEPHONY_SERVICE);
@@ -123,8 +124,12 @@
final boolean isDeviceOwner = mDpm != null && mDpm.isDeviceOwnerApp(callingPackage);
final int appId = UserHandle.getAppId(callingUid);
+ final boolean isNetworkStack = context.checkPermission(
+ android.Manifest.permission.NETWORK_STACK, callingPid, callingUid)
+ == PERMISSION_GRANTED;
+
if (hasCarrierPrivileges || isDeviceOwner
- || appId == Process.SYSTEM_UID || appId == Process.NETWORK_STACK_UID) {
+ || appId == Process.SYSTEM_UID || isNetworkStack) {
// Carrier-privileged apps and device owners, and the system (including the
// network stack) can access data usage for all apps on the device.
return NetworkStatsAccess.Level.DEVICE;
@@ -155,6 +160,8 @@
*/
public static boolean isAccessibleToUser(int uid, int callerUid,
@NetworkStatsAccess.Level int accessLevel) {
+ final int userId = UserHandle.getUserHandleForUid(uid).getIdentifier();
+ final int callerUserId = UserHandle.getUserHandleForUid(callerUid).getIdentifier();
switch (accessLevel) {
case NetworkStatsAccess.Level.DEVICE:
// Device-level access - can access usage for any uid.
@@ -165,13 +172,13 @@
// anonymized uids
return uid == android.os.Process.SYSTEM_UID || uid == UID_REMOVED
|| uid == UID_TETHERING || uid == UID_ALL
- || UserHandle.getUserId(uid) == UserHandle.getUserId(callerUid);
+ || userId == callerUserId;
case NetworkStatsAccess.Level.USER:
// User-level access - can access usage for any app running in the same user, along
// with some special uids (system, removed, or tethering).
return uid == android.os.Process.SYSTEM_UID || uid == UID_REMOVED
|| uid == UID_TETHERING
- || UserHandle.getUserId(uid) == UserHandle.getUserId(callerUid);
+ || userId == callerUserId;
case NetworkStatsAccess.Level.DEFAULT:
default:
// Default access level - can only access one's own usage.
@@ -185,8 +192,8 @@
AppOpsManager appOps = (AppOpsManager) context.getSystemService(
Context.APP_OPS_SERVICE);
- final int mode = appOps.noteOp(AppOpsManager.OP_GET_USAGE_STATS,
- callingUid, callingPackage);
+ final int mode = appOps.noteOp(AppOpsManager.OPSTR_GET_USAGE_STATS,
+ callingUid, callingPackage, null /* attributionTag */, null /* message */);
if (mode == AppOpsManager.MODE_DEFAULT) {
// The default behavior here is to check if PackageManager has given the app
// permission.
diff --git a/packages/ConnectivityT/framework-t/src/android/net/NetworkTemplate.java b/packages/ConnectivityT/framework-t/src/android/net/NetworkTemplate.java
index 9fa777d..659ad06 100644
--- a/packages/ConnectivityT/framework-t/src/android/net/NetworkTemplate.java
+++ b/packages/ConnectivityT/framework-t/src/android/net/NetworkTemplate.java
@@ -35,7 +35,6 @@
import static android.net.NetworkStats.ROAMING_ALL;
import static android.net.NetworkStats.ROAMING_NO;
import static android.net.NetworkStats.ROAMING_YES;
-import static android.net.wifi.WifiInfo.sanitizeSsid;
import android.annotation.IntDef;
import android.annotation.NonNull;
@@ -57,6 +56,7 @@
import java.lang.annotation.RetentionPolicy;
import java.util.Arrays;
import java.util.Collection;
+import java.util.Comparator;
import java.util.HashSet;
import java.util.List;
import java.util.Objects;
@@ -213,11 +213,14 @@
public static NetworkTemplate buildTemplateMobileWithRatType(@Nullable String subscriberId,
@NetworkType int ratType, int metered) {
if (TextUtils.isEmpty(subscriberId)) {
- return new NetworkTemplate(MATCH_MOBILE_WILDCARD, null, null, null,
- metered, ROAMING_ALL, DEFAULT_NETWORK_ALL, ratType, OEM_MANAGED_ALL,
+ return new NetworkTemplate(MATCH_MOBILE_WILDCARD, null /* subscriberId */,
+ null /* matchSubscriberIds */,
+ new String[0] /* matchWifiNetworkKeys */, metered, ROAMING_ALL,
+ DEFAULT_NETWORK_ALL, ratType, OEM_MANAGED_ALL,
NetworkStatsUtils.SUBSCRIBER_ID_MATCH_RULE_EXACT);
}
- return new NetworkTemplate(MATCH_MOBILE, subscriberId, new String[]{subscriberId}, null,
+ return new NetworkTemplate(MATCH_MOBILE, subscriberId, new String[] { subscriberId },
+ new String[0] /* matchWifiNetworkKeys */,
metered, ROAMING_ALL, DEFAULT_NETWORK_ALL, ratType, OEM_MANAGED_ALL,
NetworkStatsUtils.SUBSCRIBER_ID_MATCH_RULE_EXACT);
}
@@ -235,7 +238,7 @@
/**
* Template to match all metered {@link ConnectivityManager#TYPE_WIFI} networks,
- * regardless of SSID.
+ * regardless of key of the wifi network.
*
* @hide
*/
@@ -255,33 +258,40 @@
/**
* Template to match {@link ConnectivityManager#TYPE_WIFI} networks with the
- * given SSID.
+ * given key of the wifi network.
*
+ * @param wifiNetworkKey key of the wifi network. see {@link WifiInfo#getCurrentNetworkKey()}
+ * to know details about the key.
* @hide
*/
- public static NetworkTemplate buildTemplateWifi(@NonNull String networkId) {
- Objects.requireNonNull(networkId);
+ public static NetworkTemplate buildTemplateWifi(@NonNull String wifiNetworkKey) {
+ Objects.requireNonNull(wifiNetworkKey);
return new NetworkTemplate(MATCH_WIFI, null /* subscriberId */,
new String[] { null } /* matchSubscriberIds */,
- networkId, METERED_ALL, ROAMING_ALL,
+ new String[] { wifiNetworkKey }, METERED_ALL, ROAMING_ALL,
DEFAULT_NETWORK_ALL, NETWORK_TYPE_ALL, OEM_MANAGED_ALL,
NetworkStatsUtils.SUBSCRIBER_ID_MATCH_RULE_ALL);
}
/**
- * Template to match all {@link ConnectivityManager#TYPE_WIFI} networks with the given SSID,
- * and IMSI.
+ * Template to match all {@link ConnectivityManager#TYPE_WIFI} networks with the given
+ * key of the wifi network and IMSI.
*
- * Call with {@link #WIFI_NETWORK_KEY_ALL} for {@code networkId} to get result regardless
- * of SSID.
+ * Call with {@link #WIFI_NETWORK_KEY_ALL} for {@code wifiNetworkKey} to get result regardless
+ * of key of the wifi network.
+ *
+ * @param wifiNetworkKey key of the wifi network. see {@link WifiInfo#getCurrentNetworkKey()}
+ * to know details about the key.
+ * @param subscriberId the IMSI associated to this wifi network.
*
* @hide
*/
- public static NetworkTemplate buildTemplateWifi(@Nullable String networkId,
+ public static NetworkTemplate buildTemplateWifi(@Nullable String wifiNetworkKey,
@Nullable String subscriberId) {
return new NetworkTemplate(MATCH_WIFI, subscriberId, new String[] { subscriberId },
- networkId, METERED_ALL, ROAMING_ALL,
- DEFAULT_NETWORK_ALL, NETWORK_TYPE_ALL, OEM_MANAGED_ALL,
+ wifiNetworkKey != null
+ ? new String[] { wifiNetworkKey } : new String[0],
+ METERED_ALL, ROAMING_ALL, DEFAULT_NETWORK_ALL, NETWORK_TYPE_ALL, OEM_MANAGED_ALL,
NetworkStatsUtils.SUBSCRIBER_ID_MATCH_RULE_EXACT);
}
@@ -324,7 +334,9 @@
public static NetworkTemplate buildTemplateCarrierMetered(@NonNull String subscriberId) {
Objects.requireNonNull(subscriberId);
return new NetworkTemplate(MATCH_CARRIER, subscriberId,
- new String[] { subscriberId }, null /* networkId */, METERED_YES, ROAMING_ALL,
+ new String[] { subscriberId },
+ new String[0] /* matchWifiNetworkKeys */,
+ METERED_YES, ROAMING_ALL,
DEFAULT_NETWORK_ALL, NETWORK_TYPE_ALL, OEM_MANAGED_ALL,
NetworkStatsUtils.SUBSCRIBER_ID_MATCH_RULE_EXACT);
}
@@ -342,8 +354,8 @@
*/
private final String[] mMatchSubscriberIds;
- // TODO: Change variable name to match the Api surface.
- private final String mNetworkId;
+ @NonNull
+ private final String[] mMatchWifiNetworkKeys;
// Matches for the NetworkStats constants METERED_*, ROAMING_* and DEFAULT_NETWORK_*.
private final int mMetered;
@@ -377,18 +389,19 @@
/** @hide */
// TODO: Deprecate this constructor, mark it @UnsupportedAppUsage(maxTargetSdk = S)
@UnsupportedAppUsage
- public NetworkTemplate(int matchRule, String subscriberId, String networkId) {
- this(matchRule, subscriberId, new String[] { subscriberId }, networkId);
+ public NetworkTemplate(int matchRule, String subscriberId, String wifiNetworkKey) {
+ this(matchRule, subscriberId, new String[] { subscriberId }, wifiNetworkKey);
}
/** @hide */
public NetworkTemplate(int matchRule, String subscriberId, String[] matchSubscriberIds,
- String networkId) {
+ String wifiNetworkKey) {
// Older versions used to only match MATCH_MOBILE and MATCH_MOBILE_WILDCARD templates
// to metered networks. It is now possible to match mobile with any meteredness, but
// in order to preserve backward compatibility of @UnsupportedAppUsage methods, this
//constructor passes METERED_YES for these types.
- this(matchRule, subscriberId, matchSubscriberIds, networkId,
+ this(matchRule, subscriberId, matchSubscriberIds,
+ wifiNetworkKey != null ? new String[] { wifiNetworkKey } : new String[0],
(matchRule == MATCH_MOBILE || matchRule == MATCH_MOBILE_WILDCARD) ? METERED_YES
: METERED_ALL , ROAMING_ALL, DEFAULT_NETWORK_ALL, NETWORK_TYPE_ALL,
OEM_MANAGED_ALL, NetworkStatsUtils.SUBSCRIBER_ID_MATCH_RULE_EXACT);
@@ -397,23 +410,25 @@
/** @hide */
// TODO: Remove it after updating all of the caller.
public NetworkTemplate(int matchRule, String subscriberId, String[] matchSubscriberIds,
- String networkId, int metered, int roaming, int defaultNetwork, int subType,
+ String wifiNetworkKey, int metered, int roaming, int defaultNetwork, int subType,
int oemManaged) {
- this(matchRule, subscriberId, matchSubscriberIds, networkId, metered, roaming,
- defaultNetwork, subType, oemManaged,
+ this(matchRule, subscriberId, matchSubscriberIds,
+ wifiNetworkKey != null ? new String[] { wifiNetworkKey } : new String[0],
+ metered, roaming, defaultNetwork, subType, oemManaged,
NetworkStatsUtils.SUBSCRIBER_ID_MATCH_RULE_EXACT);
}
/** @hide */
public NetworkTemplate(int matchRule, String subscriberId, String[] matchSubscriberIds,
- String networkId, int metered, int roaming, int defaultNetwork, int subType,
- int oemManaged, int subscriberIdMatchRule) {
+ String[] matchWifiNetworkKeys, int metered, int roaming,
+ int defaultNetwork, int subType, int oemManaged, int subscriberIdMatchRule) {
+ Objects.requireNonNull(matchWifiNetworkKeys);
mMatchRule = matchRule;
mSubscriberId = subscriberId;
// TODO: Check whether mMatchSubscriberIds = null or mMatchSubscriberIds = {null} when
// mSubscriberId is null
mMatchSubscriberIds = matchSubscriberIds;
- mNetworkId = networkId;
+ mMatchWifiNetworkKeys = matchWifiNetworkKeys;
mMetered = metered;
mRoaming = roaming;
mDefaultNetwork = defaultNetwork;
@@ -431,7 +446,7 @@
mMatchRule = in.readInt();
mSubscriberId = in.readString();
mMatchSubscriberIds = in.createStringArray();
- mNetworkId = in.readString();
+ mMatchWifiNetworkKeys = in.createStringArray();
mMetered = in.readInt();
mRoaming = in.readInt();
mDefaultNetwork = in.readInt();
@@ -445,7 +460,7 @@
dest.writeInt(mMatchRule);
dest.writeString(mSubscriberId);
dest.writeStringArray(mMatchSubscriberIds);
- dest.writeString(mNetworkId);
+ dest.writeStringArray(mMatchWifiNetworkKeys);
dest.writeInt(mMetered);
dest.writeInt(mRoaming);
dest.writeInt(mDefaultNetwork);
@@ -471,9 +486,7 @@
builder.append(", matchSubscriberIds=").append(
Arrays.toString(NetworkIdentityUtils.scrubSubscriberIds(mMatchSubscriberIds)));
}
- if (mNetworkId != null) {
- builder.append(", networkId=").append(mNetworkId);
- }
+ builder.append(", matchWifiNetworkKeys=").append(Arrays.toString(mMatchWifiNetworkKeys));
if (mMetered != METERED_ALL) {
builder.append(", metered=").append(NetworkStats.meteredToString(mMetered));
}
@@ -497,8 +510,8 @@
@Override
public int hashCode() {
- return Objects.hash(mMatchRule, mSubscriberId, mNetworkId, mMetered, mRoaming,
- mDefaultNetwork, mSubType, mOemManaged, mSubscriberIdMatchRule);
+ return Objects.hash(mMatchRule, mSubscriberId, Arrays.hashCode(mMatchWifiNetworkKeys),
+ mMetered, mRoaming, mDefaultNetwork, mSubType, mOemManaged, mSubscriberIdMatchRule);
}
@Override
@@ -507,13 +520,13 @@
final NetworkTemplate other = (NetworkTemplate) obj;
return mMatchRule == other.mMatchRule
&& Objects.equals(mSubscriberId, other.mSubscriberId)
- && Objects.equals(mNetworkId, other.mNetworkId)
&& mMetered == other.mMetered
&& mRoaming == other.mRoaming
&& mDefaultNetwork == other.mDefaultNetwork
&& mSubType == other.mSubType
&& mOemManaged == other.mOemManaged
- && mSubscriberIdMatchRule == other.mSubscriberIdMatchRule;
+ && mSubscriberIdMatchRule == other.mSubscriberIdMatchRule
+ && Arrays.equals(mMatchWifiNetworkKeys, other.mMatchWifiNetworkKeys);
}
return false;
}
@@ -579,14 +592,22 @@
*/
@Nullable
public String getWifiNetworkKey() {
- return mNetworkId;
+ return CollectionUtils.isEmpty(mMatchWifiNetworkKeys) ? null : mMatchWifiNetworkKeys[0];
+ }
+
+ /**
+ * Get set of Wifi Network Keys of the template.
+ */
+ @Nullable
+ public Set<String> getWifiNetworkKeys() {
+ return new ArraySet<>(Arrays.asList(mMatchWifiNetworkKeys));
}
/** @hide */
// TODO: Remove this and replace all callers with {@link #getWifiNetworkKey()}.
@Nullable
public String getNetworkId() {
- return mNetworkId;
+ return getWifiNetworkKey();
}
/**
@@ -707,16 +728,21 @@
}
/**
- * Check if network with matching SSID. Returns true when the SSID matches, or when
- * {@code mNetworkId} is {@code WIFI_NETWORK_KEY_ALL}.
+ * Check if network matches key of the wifi network.
+ * Returns true when the key matches, or when {@code mMatchWifiNetworkKeys} is
+ * empty.
+ *
+ * @param wifiNetworkKey key of the wifi network. see {@link WifiInfo#getCurrentNetworkKey()}
+ * to know details about the key.
*/
- private boolean matchesWifiNetworkId(@Nullable String networkId) {
- return Objects.equals(mNetworkId, WIFI_NETWORK_KEY_ALL)
- || Objects.equals(sanitizeSsid(mNetworkId), sanitizeSsid(networkId));
+ private boolean matchesWifiNetworkKey(@NonNull String wifiNetworkKey) {
+ Objects.requireNonNull(wifiNetworkKey);
+ return CollectionUtils.isEmpty(mMatchWifiNetworkKeys)
+ || CollectionUtils.contains(mMatchWifiNetworkKeys, wifiNetworkKey);
}
/**
- * Check if mobile network with matching IMSI.
+ * Check if mobile network matches IMSI.
*/
private boolean matchesMobile(NetworkIdentity ident) {
if (ident.mType == TYPE_WIMAX) {
@@ -814,7 +840,7 @@
switch (ident.mType) {
case TYPE_WIFI:
return matchesSubscriberId(ident.mSubscriberId)
- && matchesWifiNetworkId(ident.mNetworkId);
+ && matchesWifiNetworkKey(ident.mNetworkId);
default:
return false;
}
@@ -956,8 +982,10 @@
if (CollectionUtils.contains(merged, template.mSubscriberId)) {
// Requested template subscriber is part of the merge group; return
// a template that matches all merged subscribers.
+ final String[] matchWifiNetworkKeys = template.mMatchWifiNetworkKeys;
return new NetworkTemplate(template.mMatchRule, merged[0], merged,
- template.mNetworkId);
+ CollectionUtils.isEmpty(matchWifiNetworkKeys)
+ ? null : matchWifiNetworkKeys[0]);
}
}
@@ -984,9 +1012,10 @@
private final int mMatchRule;
// Use a SortedSet to provide a deterministic order when fetching the first one.
@NonNull
- private final SortedSet<String> mMatchSubscriberIds = new TreeSet<>();
- @Nullable
- private String mWifiNetworkKey;
+ private final SortedSet<String> mMatchSubscriberIds =
+ new TreeSet<>(Comparator.nullsFirst(Comparator.naturalOrder()));
+ @NonNull
+ private final SortedSet<String> mMatchWifiNetworkKeys = new TreeSet<>();
// Matches for the NetworkStats constants METERED_*, ROAMING_* and DEFAULT_NETWORK_*.
private int mMetered;
@@ -1006,7 +1035,6 @@
assertRequestableMatchRule(matchRule);
// Initialize members with default values.
mMatchRule = matchRule;
- mWifiNetworkKey = WIFI_NETWORK_KEY_ALL;
mMetered = METERED_ALL;
mRoaming = ROAMING_ALL;
mDefaultNetwork = DEFAULT_NETWORK_ALL;
@@ -1030,15 +1058,28 @@
}
/**
- * Set the Wifi Network Key.
+ * Set the Wifi Network Keys. Calling this function with an empty set represents
+ * the intention of matching any Wifi Network Key.
*
- * @param wifiNetworkKey the Wifi Network Key, see {@link WifiInfo#getCurrentNetworkKey()}.
- * Or null to match all networks.
+ * @param wifiNetworkKeys the list of Wifi Network Key,
+ * see {@link WifiInfo#getCurrentNetworkKey()}.
+ * Or an empty list to match all networks.
+ * Note that {@code getCurrentNetworkKey()} might get null key
+ * when wifi disconnects. However, the caller should never invoke
+ * this function with a null Wifi Network Key since such statistics
+ * never exists.
* @return this builder.
*/
@NonNull
- public Builder setWifiNetworkKey(@Nullable String wifiNetworkKey) {
- mWifiNetworkKey = wifiNetworkKey;
+ public Builder setWifiNetworkKeys(@NonNull Set<String> wifiNetworkKeys) {
+ Objects.requireNonNull(wifiNetworkKeys);
+ for (String key : wifiNetworkKeys) {
+ if (key == null) {
+ throw new IllegalArgumentException("Null is not a valid key");
+ }
+ }
+ mMatchWifiNetworkKeys.clear();
+ mMatchWifiNetworkKeys.addAll(wifiNetworkKeys);
return this;
}
@@ -1122,9 +1163,17 @@
}
private void assertRequestableParameters() {
+ validateWifiNetworkKeys();
// TODO: Check all the input are legitimate.
}
+ private void validateWifiNetworkKeys() {
+ if (mMatchRule != MATCH_WIFI && !mMatchWifiNetworkKeys.isEmpty()) {
+ throw new IllegalArgumentException("Trying to build non wifi match rule: "
+ + mMatchRule + " with wifi network keys");
+ }
+ }
+
/**
* For backward compatibility, deduce match rule to a wildcard match rule
* if the Subscriber Ids are empty.
@@ -1133,7 +1182,7 @@
if (mMatchRule == MATCH_MOBILE && mMatchSubscriberIds.isEmpty()) {
return MATCH_MOBILE_WILDCARD;
} else if (mMatchRule == MATCH_WIFI && mMatchSubscriberIds.isEmpty()
- && mWifiNetworkKey == WIFI_NETWORK_KEY_ALL) {
+ && mMatchWifiNetworkKeys.isEmpty()) {
return MATCH_WIFI_WILDCARD;
}
return mMatchRule;
@@ -1153,8 +1202,8 @@
return new NetworkTemplate(getWildcardDeducedMatchRule(),
mMatchSubscriberIds.isEmpty() ? null : mMatchSubscriberIds.iterator().next(),
mMatchSubscriberIds.toArray(new String[0]),
- mWifiNetworkKey, mMetered, mRoaming, mDefaultNetwork, mRatType, mOemManaged,
- subscriberIdMatchRule);
+ mMatchWifiNetworkKeys.toArray(new String[0]), mMetered, mRoaming,
+ mDefaultNetwork, mRatType, mOemManaged, subscriberIdMatchRule);
}
}
}
diff --git a/packages/ConnectivityT/framework-t/src/android/net/TrafficStats.java b/packages/ConnectivityT/framework-t/src/android/net/TrafficStats.java
index fa65061..d8feb88 100644
--- a/packages/ConnectivityT/framework-t/src/android/net/TrafficStats.java
+++ b/packages/ConnectivityT/framework-t/src/android/net/TrafficStats.java
@@ -29,7 +29,6 @@
import android.os.Build;
import android.os.RemoteException;
import android.os.ServiceManager;
-import android.util.DataUnit;
import com.android.server.NetworkManagementSocketTagger;
@@ -59,19 +58,19 @@
*/
public final static int UNSUPPORTED = -1;
- /** @hide @deprecated use {@link DataUnit} instead to clarify SI-vs-IEC */
+ /** @hide @deprecated use {@code DataUnit} instead to clarify SI-vs-IEC */
@Deprecated
public static final long KB_IN_BYTES = 1024;
- /** @hide @deprecated use {@link DataUnit} instead to clarify SI-vs-IEC */
+ /** @hide @deprecated use {@code DataUnit} instead to clarify SI-vs-IEC */
@Deprecated
public static final long MB_IN_BYTES = KB_IN_BYTES * 1024;
- /** @hide @deprecated use {@link DataUnit} instead to clarify SI-vs-IEC */
+ /** @hide @deprecated use {@code DataUnit} instead to clarify SI-vs-IEC */
@Deprecated
public static final long GB_IN_BYTES = MB_IN_BYTES * 1024;
- /** @hide @deprecated use {@link DataUnit} instead to clarify SI-vs-IEC */
+ /** @hide @deprecated use {@code DataUnit} instead to clarify SI-vs-IEC */
@Deprecated
public static final long TB_IN_BYTES = GB_IN_BYTES * 1024;
- /** @hide @deprecated use {@link DataUnit} instead to clarify SI-vs-IEC */
+ /** @hide @deprecated use {@code DataUnit} instead to clarify SI-vs-IEC */
@Deprecated
public static final long PB_IN_BYTES = TB_IN_BYTES * 1024;
diff --git a/packages/ConnectivityT/service/src/com/android/server/net/NetworkStatsFactory.java b/packages/ConnectivityT/service/src/com/android/server/net/NetworkStatsFactory.java
index e6433db..c7c8893 100644
--- a/packages/ConnectivityT/service/src/com/android/server/net/NetworkStatsFactory.java
+++ b/packages/ConnectivityT/service/src/com/android/server/net/NetworkStatsFactory.java
@@ -35,8 +35,8 @@
import com.android.internal.annotations.GuardedBy;
import com.android.internal.annotations.VisibleForTesting;
-import com.android.internal.util.ArrayUtils;
import com.android.internal.util.ProcFileReader;
+import com.android.net.module.util.CollectionUtils;
import libcore.io.IoUtils;
@@ -434,7 +434,7 @@
entry.txBytes = reader.nextLong();
entry.txPackets = reader.nextLong();
- if ((limitIfaces == null || ArrayUtils.contains(limitIfaces, entry.iface))
+ if ((limitIfaces == null || CollectionUtils.contains(limitIfaces, entry.iface))
&& (limitUid == UID_ALL || limitUid == entry.uid)
&& (limitTag == TAG_ALL || limitTag == entry.tag)) {
stats.insertEntry(entry);
diff --git a/packages/ConnectivityT/service/src/com/android/server/net/NetworkStatsObservers.java b/packages/ConnectivityT/service/src/com/android/server/net/NetworkStatsObservers.java
index 1a0866d..b57a4f9 100644
--- a/packages/ConnectivityT/service/src/com/android/server/net/NetworkStatsObservers.java
+++ b/packages/ConnectivityT/service/src/com/android/server/net/NetworkStatsObservers.java
@@ -18,8 +18,6 @@
import static android.app.usage.NetworkStatsManager.MIN_THRESHOLD_BYTES;
-import static com.android.internal.util.Preconditions.checkArgument;
-
import android.app.usage.NetworkStatsManager;
import android.net.DataUsageRequest;
import android.net.NetworkIdentitySet;
@@ -38,7 +36,7 @@
import android.os.Process;
import android.os.RemoteException;
import android.util.ArrayMap;
-import android.util.Slog;
+import android.util.Log;
import android.util.SparseArray;
import com.android.internal.annotations.VisibleForTesting;
@@ -83,7 +81,7 @@
RequestInfo requestInfo = buildRequestInfo(request, messenger, binder, callingUid,
accessLevel);
- if (LOGV) Slog.v(TAG, "Registering observer for " + request);
+ if (LOGV) Log.v(TAG, "Registering observer for " + request);
getHandler().sendMessage(mHandler.obtainMessage(MSG_REGISTER, requestInfo));
return request;
}
@@ -116,7 +114,7 @@
if (mHandler == null) {
synchronized (this) {
if (mHandler == null) {
- if (LOGV) Slog.v(TAG, "Creating handler");
+ if (LOGV) Log.v(TAG, "Creating handler");
mHandler = new Handler(getHandlerLooperLocked(), mHandlerCallback);
}
}
@@ -172,15 +170,15 @@
RequestInfo requestInfo;
requestInfo = mDataUsageRequests.get(request.requestId);
if (requestInfo == null) {
- if (LOGV) Slog.v(TAG, "Trying to unregister unknown request " + request);
+ if (LOGV) Log.v(TAG, "Trying to unregister unknown request " + request);
return;
}
if (Process.SYSTEM_UID != callingUid && requestInfo.mCallingUid != callingUid) {
- Slog.w(TAG, "Caller uid " + callingUid + " is not owner of " + request);
+ Log.w(TAG, "Caller uid " + callingUid + " is not owner of " + request);
return;
}
- if (LOGV) Slog.v(TAG, "Unregistering " + request);
+ if (LOGV) Log.v(TAG, "Unregistering " + request);
mDataUsageRequests.remove(request.requestId);
requestInfo.unlinkDeathRecipient();
requestInfo.callCallback(NetworkStatsManager.CALLBACK_RELEASED);
@@ -201,7 +199,7 @@
// Cap the minimum threshold to a safe default to avoid too many callbacks
long thresholdInBytes = Math.max(MIN_THRESHOLD_BYTES, request.thresholdInBytes);
if (thresholdInBytes < request.thresholdInBytes) {
- Slog.w(TAG, "Threshold was too low for " + request
+ Log.w(TAG, "Threshold was too low for " + request
+ ". Overriding to a safer default of " + thresholdInBytes + " bytes");
}
return new DataUsageRequest(mNextDataUsageRequestId.incrementAndGet(),
@@ -216,7 +214,10 @@
accessLevel);
} else {
// Safety check in case a new access level is added and we forgot to update this
- checkArgument(accessLevel >= NetworkStatsAccess.Level.DEVICESUMMARY);
+ if (accessLevel < NetworkStatsAccess.Level.DEVICESUMMARY) {
+ throw new IllegalArgumentException(
+ "accessLevel " + accessLevel + " is less than DEVICESUMMARY.");
+ }
return new NetworkUsageRequestInfo(this, request, messenger, binder, callingUid,
accessLevel);
}
@@ -255,8 +256,9 @@
@Override
public void binderDied() {
- if (LOGV) Slog.v(TAG, "RequestInfo binderDied("
- + mRequest + ", " + mBinder + ")");
+ if (LOGV) {
+ Log.v(TAG, "RequestInfo binderDied(" + mRequest + ", " + mBinder + ")");
+ }
mStatsObserver.unregister(mRequest, Process.SYSTEM_UID);
callCallback(NetworkStatsManager.CALLBACK_RELEASED);
}
@@ -299,13 +301,13 @@
msg.setData(bundle);
try {
if (LOGV) {
- Slog.v(TAG, "sending notification " + callbackTypeToName(callbackType)
+ Log.v(TAG, "sending notification " + callbackTypeToName(callbackType)
+ " for " + mRequest);
}
mMessenger.send(msg);
} catch (RemoteException e) {
// May occur naturally in the race of binder death.
- Slog.w(TAG, "RemoteException caught trying to send a callback msg for " + mRequest);
+ Log.w(TAG, "RemoteException caught trying to send a callback msg for " + mRequest);
}
}
@@ -341,7 +343,7 @@
protected boolean checkStats() {
long bytesSoFar = getTotalBytesForNetwork(mRequest.template);
if (LOGV) {
- Slog.v(TAG, bytesSoFar + " bytes so far since notification for "
+ Log.v(TAG, bytesSoFar + " bytes so far since notification for "
+ mRequest.template);
}
if (bytesSoFar > mRequest.thresholdInBytes) {
@@ -416,7 +418,7 @@
return history.getTotalBytes();
} catch (SecurityException e) {
if (LOGV) {
- Slog.w(TAG, "CallerUid " + mCallingUid + " may have lost access to uid "
+ Log.w(TAG, "CallerUid " + mCallingUid + " may have lost access to uid "
+ uid);
}
return 0;
diff --git a/packages/ConnectivityT/service/src/com/android/server/net/NetworkStatsRecorder.java b/packages/ConnectivityT/service/src/com/android/server/net/NetworkStatsRecorder.java
index 5e27c77..c371f08 100644
--- a/packages/ConnectivityT/service/src/com/android/server/net/NetworkStatsRecorder.java
+++ b/packages/ConnectivityT/service/src/com/android/server/net/NetworkStatsRecorder.java
@@ -32,15 +32,12 @@
import android.os.Binder;
import android.os.DropBoxManager;
import android.service.NetworkStatsRecorderProto;
+import android.util.IndentingPrintWriter;
import android.util.Log;
-import android.util.MathUtils;
-import android.util.Slog;
import android.util.proto.ProtoOutputStream;
import com.android.internal.util.FileRotator;
-import com.android.internal.util.IndentingPrintWriter;
-
-import com.google.android.collect.Sets;
+import com.android.net.module.util.NetworkStatsUtils;
import libcore.io.IoUtils;
@@ -132,8 +129,8 @@
}
public void setPersistThreshold(long thresholdBytes) {
- if (LOGV) Slog.v(TAG, "setPersistThreshold() with " + thresholdBytes);
- mPersistThresholdBytes = MathUtils.constrain(
+ if (LOGV) Log.v(TAG, "setPersistThreshold() with " + thresholdBytes);
+ mPersistThresholdBytes = NetworkStatsUtils.constrain(
thresholdBytes, 1 * KB_IN_BYTES, 100 * MB_IN_BYTES);
}
@@ -185,7 +182,7 @@
}
private NetworkStatsCollection loadLocked(long start, long end) {
- if (LOGD) Slog.d(TAG, "loadLocked() reading from disk for " + mCookie);
+ if (LOGD) Log.d(TAG, "loadLocked() reading from disk for " + mCookie);
final NetworkStatsCollection res = new NetworkStatsCollection(mBucketDuration);
try {
mRotator.readMatching(res, start, end);
@@ -207,7 +204,7 @@
*/
public void recordSnapshotLocked(NetworkStats snapshot,
Map<String, NetworkIdentitySet> ifaceIdent, long currentTimeMillis) {
- final HashSet<String> unknownIfaces = Sets.newHashSet();
+ final HashSet<String> unknownIfaces = new HashSet<>();
// skip recording when snapshot missing
if (snapshot == null) return;
@@ -272,7 +269,7 @@
mLastSnapshot = snapshot;
if (LOGV && unknownIfaces.size() > 0) {
- Slog.w(TAG, "unknown interfaces " + unknownIfaces + ", ignoring those stats");
+ Log.w(TAG, "unknown interfaces " + unknownIfaces + ", ignoring those stats");
}
}
@@ -296,7 +293,7 @@
public void forcePersistLocked(long currentTimeMillis) {
Objects.requireNonNull(mRotator, "missing FileRotator");
if (mPending.isDirty()) {
- if (LOGD) Slog.d(TAG, "forcePersistLocked() writing for " + mCookie);
+ if (LOGD) Log.d(TAG, "forcePersistLocked() writing for " + mCookie);
try {
mRotator.rewriteActive(mPendingRewriter, currentTimeMillis);
mRotator.maybeRotate(currentTimeMillis);
diff --git a/packages/ConnectivityT/service/src/com/android/server/net/NetworkStatsService.java b/packages/ConnectivityT/service/src/com/android/server/net/NetworkStatsService.java
index df066c21..a3de9e4 100644
--- a/packages/ConnectivityT/service/src/com/android/server/net/NetworkStatsService.java
+++ b/packages/ConnectivityT/service/src/com/android/server/net/NetworkStatsService.java
@@ -25,7 +25,6 @@
import static android.content.Intent.EXTRA_UID;
import static android.content.pm.PackageManager.PERMISSION_GRANTED;
import static android.net.NetworkIdentity.SUBTYPE_COMBINED;
-import static android.net.NetworkStack.checkNetworkStackPermission;
import static android.net.NetworkStats.DEFAULT_NETWORK_ALL;
import static android.net.NetworkStats.IFACE_ALL;
import static android.net.NetworkStats.IFACE_VT;
@@ -141,28 +140,29 @@
import android.util.ArrayMap;
import android.util.ArraySet;
import android.util.EventLog;
+import android.util.IndentingPrintWriter;
import android.util.Log;
-import android.util.MathUtils;
-import android.util.Slog;
import android.util.SparseIntArray;
import android.util.proto.ProtoOutputStream;
import com.android.internal.annotations.GuardedBy;
import com.android.internal.annotations.VisibleForTesting;
-import com.android.internal.util.ArrayUtils;
-import com.android.internal.util.DumpUtils;
import com.android.internal.util.FileRotator;
-import com.android.internal.util.IndentingPrintWriter;
import com.android.net.module.util.BinderUtils;
+import com.android.net.module.util.CollectionUtils;
+import com.android.net.module.util.NetworkStatsUtils;
+import com.android.net.module.util.PermissionUtils;
import com.android.server.EventLogTags;
import com.android.server.LocalServices;
import java.io.File;
import java.io.FileDescriptor;
+import java.io.FileOutputStream;
import java.io.IOException;
import java.io.PrintWriter;
import java.time.Clock;
import java.time.ZoneOffset;
+import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashSet;
import java.util.List;
@@ -646,7 +646,7 @@
try {
mNetworkManager.setGlobalAlert(mGlobalAlertBytes);
} catch (IllegalStateException e) {
- Slog.w(TAG, "problem registering for global alert: " + e);
+ Log.w(TAG, "problem registering for global alert: " + e);
} catch (RemoteException e) {
// ignored; service lives in system_server
}
@@ -764,7 +764,7 @@
return stats;
} catch (NullPointerException e) {
// TODO: Track down and fix the cause of this crash and remove this catch block.
- Slog.wtf(TAG, "NullPointerException in getSummaryForAllUid", e);
+ Log.wtf(TAG, "NullPointerException in getSummaryForAllUid", e);
throw e;
}
}
@@ -809,7 +809,7 @@
private @NetworkStatsAccess.Level int checkAccessLevel(String callingPackage) {
return NetworkStatsAccess.checkAccessLevel(
- mContext, Binder.getCallingUid(), callingPackage);
+ mContext, Binder.getCallingPid(), Binder.getCallingUid(), callingPackage);
}
/**
@@ -821,7 +821,7 @@
SubscriptionPlan plan = null;
if ((flags & NetworkStatsManager.FLAG_AUGMENT_WITH_SUBSCRIPTION_PLAN) != 0
&& mSettings.getAugmentEnabled()) {
- if (LOGD) Slog.d(TAG, "Resolving plan for " + template);
+ if (LOGD) Log.d(TAG, "Resolving plan for " + template);
final long token = Binder.clearCallingIdentity();
try {
plan = LocalServices.getService(NetworkPolicyManagerInternal.class)
@@ -829,7 +829,7 @@
} finally {
Binder.restoreCallingIdentity(token);
}
- if (LOGD) Slog.d(TAG, "Resolved to plan " + plan);
+ if (LOGD) Log.d(TAG, "Resolved to plan " + plan);
}
return plan;
}
@@ -932,7 +932,7 @@
@Override
public String[] getMobileIfaces() {
// TODO (b/192758557): Remove debug log.
- if (ArrayUtils.contains(mMobileIfaces, null)) {
+ if (CollectionUtils.contains(mMobileIfaces, null)) {
throw new NullPointerException(
"null element in mMobileIfaces: " + Arrays.toString(mMobileIfaces));
}
@@ -981,7 +981,7 @@
@NonNull NetworkStateSnapshot[] networkStates,
@Nullable String activeIface,
@NonNull UnderlyingNetworkInfo[] underlyingNetworkInfos) {
- checkNetworkStackPermission(mContext);
+ PermissionUtils.enforceNetworkStackPermission(mContext);
final long token = Binder.clearCallingIdentity();
try {
@@ -1010,9 +1010,10 @@
private void advisePersistThreshold(long thresholdBytes) {
// clamp threshold into safe range
- mPersistThreshold = MathUtils.constrain(thresholdBytes, 128 * KB_IN_BYTES, 2 * MB_IN_BYTES);
+ mPersistThreshold = NetworkStatsUtils.constrain(thresholdBytes,
+ 128 * KB_IN_BYTES, 2 * MB_IN_BYTES);
if (LOGV) {
- Slog.v(TAG, "advisePersistThreshold() given " + thresholdBytes + ", clamped to "
+ Log.v(TAG, "advisePersistThreshold() given " + thresholdBytes + ", clamped to "
+ mPersistThreshold);
}
@@ -1196,13 +1197,13 @@
// On background handler thread, and USER_REMOVED is protected
// broadcast.
- final int userId = intent.getIntExtra(Intent.EXTRA_USER_HANDLE, -1);
- if (userId == -1) return;
+ final UserHandle userHandle = intent.getParcelableExtra(Intent.EXTRA_USER);
+ if (userHandle == null) return;
synchronized (mStatsLock) {
mWakeLock.acquire();
try {
- removeUserLocked(userId);
+ removeUserLocked(userHandle);
} finally {
mWakeLock.release();
}
@@ -1227,7 +1228,7 @@
@Override
public void limitReached(String limitName, String iface) {
// only someone like NMS should be calling us
- NetworkStack.checkNetworkStackPermission(mContext);
+ PermissionUtils.enforceNetworkStackPermission(mContext);
if (LIMIT_GLOBAL_ALERT.equals(limitName)) {
// kick off background poll to collect network stats unless there is already
@@ -1275,7 +1276,7 @@
private void handleNotifyNetworkStatusLocked(@NonNull Network[] defaultNetworks,
@NonNull NetworkStateSnapshot[] snapshots) {
if (!mSystemReady) return;
- if (LOGV) Slog.v(TAG, "handleNotifyNetworkStatusLocked()");
+ if (LOGV) Log.v(TAG, "handleNotifyNetworkStatusLocked()");
// take one last stats snapshot before updating iface mapping. this
// isn't perfect, since the kernel may already be counting traffic from
@@ -1299,7 +1300,8 @@
final int displayTransport =
getDisplayTransport(snapshot.getNetworkCapabilities().getTransportTypes());
final boolean isMobile = (NetworkCapabilities.TRANSPORT_CELLULAR == displayTransport);
- final boolean isDefault = ArrayUtils.contains(mDefaultNetworks, snapshot.getNetwork());
+ final boolean isDefault = CollectionUtils.contains(
+ mDefaultNetworks, snapshot.getNetwork());
final int subType = combineSubtypeEnabled ? SUBTYPE_COMBINED
: getSubTypeForStateSnapshot(snapshot);
final NetworkIdentity ident = NetworkIdentity.buildNetworkIdentity(mContext, snapshot,
@@ -1382,7 +1384,7 @@
mMobileIfaces = mobileIfaces.toArray(new String[0]);
// TODO (b/192758557): Remove debug log.
- if (ArrayUtils.contains(mMobileIfaces, null)) {
+ if (CollectionUtils.contains(mMobileIfaces, null)) {
throw new NullPointerException(
"null element in mMobileIfaces: " + Arrays.toString(mMobileIfaces));
}
@@ -1397,7 +1399,7 @@
if (spec instanceof TelephonyNetworkSpecifier) {
return ((TelephonyNetworkSpecifier) spec).getSubscriptionId();
} else {
- Slog.wtf(TAG, "getSubIdForState invalid NetworkSpecifier");
+ Log.wtf(TAG, "getSubIdForState invalid NetworkSpecifier");
return INVALID_SUBSCRIPTION_ID;
}
}
@@ -1481,7 +1483,7 @@
try {
recordSnapshotLocked(currentTime);
} catch (IllegalStateException e) {
- Slog.w(TAG, "problem reading network stats: " + e);
+ Log.w(TAG, "problem reading network stats: " + e);
} catch (RemoteException e) {
// ignored; service lives in system_server
}
@@ -1506,7 +1508,7 @@
@GuardedBy("mStatsLock")
private void performPollLocked(int flags) {
if (!mSystemReady) return;
- if (LOGV) Slog.v(TAG, "performPollLocked(flags=0x" + Integer.toHexString(flags) + ")");
+ if (LOGV) Log.v(TAG, "performPollLocked(flags=0x" + Integer.toHexString(flags) + ")");
Trace.traceBegin(TRACE_TAG_NETWORK, "performPollLocked");
final boolean persistNetwork = (flags & FLAG_PERSIST_NETWORK) != 0;
@@ -1628,7 +1630,7 @@
*/
@GuardedBy("mStatsLock")
private void removeUidsLocked(int... uids) {
- if (LOGV) Slog.v(TAG, "removeUidsLocked() for UIDs " + Arrays.toString(uids));
+ if (LOGV) Log.v(TAG, "removeUidsLocked() for UIDs " + Arrays.toString(uids));
// Perform one last poll before removing
performPollLocked(FLAG_PERSIST_ALL);
@@ -1646,20 +1648,20 @@
* Clean up {@link #mUidRecorder} after user is removed.
*/
@GuardedBy("mStatsLock")
- private void removeUserLocked(int userId) {
- if (LOGV) Slog.v(TAG, "removeUserLocked() for userId=" + userId);
+ private void removeUserLocked(@NonNull UserHandle userHandle) {
+ if (LOGV) Log.v(TAG, "removeUserLocked() for UserHandle=" + userHandle);
// Build list of UIDs that we should clean up
- int[] uids = new int[0];
+ final ArrayList<Integer> uids = new ArrayList<>();
final List<ApplicationInfo> apps = mContext.getPackageManager().getInstalledApplications(
PackageManager.MATCH_ANY_USER
| PackageManager.MATCH_DISABLED_COMPONENTS);
for (ApplicationInfo app : apps) {
- final int uid = UserHandle.getUid(userId, app.uid);
- uids = ArrayUtils.appendInt(uids, uid);
+ final int uid = userHandle.getUid(app.uid);
+ uids.add(uid);
}
- removeUidsLocked(uids);
+ removeUidsLocked(CollectionUtils.toIntArray(uids));
}
private class NetworkStatsManagerInternalImpl extends NetworkStatsManagerInternal {
@@ -1702,7 +1704,7 @@
public void setStatsProviderWarningAndLimitAsync(
@NonNull String iface, long warning, long limit) {
if (LOGV) {
- Slog.v(TAG, "setStatsProviderWarningAndLimitAsync("
+ Log.v(TAG, "setStatsProviderWarningAndLimitAsync("
+ iface + "," + warning + "," + limit + ")");
}
invokeForAllStatsProviderCallbacks((cb) -> cb.mProvider.onSetWarningAndLimit(iface,
@@ -1712,7 +1714,7 @@
@Override
protected void dump(FileDescriptor fd, PrintWriter rawWriter, String[] args) {
- if (!DumpUtils.checkDumpPermission(mContext, TAG, rawWriter)) return;
+ if (!PermissionUtils.checkDumpPermission(mContext, TAG, rawWriter)) return;
long duration = DateUtils.DAY_IN_MILLIS;
final HashSet<String> argSet = new HashSet<String>();
@@ -1773,15 +1775,15 @@
pw.println("Configs:");
pw.increaseIndent();
- pw.printPair(NETSTATS_COMBINE_SUBTYPE_ENABLED, mSettings.getCombineSubtypeEnabled());
+ pw.print(NETSTATS_COMBINE_SUBTYPE_ENABLED, mSettings.getCombineSubtypeEnabled());
pw.println();
pw.decreaseIndent();
pw.println("Active interfaces:");
pw.increaseIndent();
for (int i = 0; i < mActiveIfaces.size(); i++) {
- pw.printPair("iface", mActiveIfaces.keyAt(i));
- pw.printPair("ident", mActiveIfaces.valueAt(i));
+ pw.print("iface", mActiveIfaces.keyAt(i));
+ pw.print("ident", mActiveIfaces.valueAt(i));
pw.println();
}
pw.decreaseIndent();
@@ -1789,8 +1791,8 @@
pw.println("Active UID interfaces:");
pw.increaseIndent();
for (int i = 0; i < mActiveUidIfaces.size(); i++) {
- pw.printPair("iface", mActiveUidIfaces.keyAt(i));
- pw.printPair("ident", mActiveUidIfaces.valueAt(i));
+ pw.print("iface", mActiveUidIfaces.keyAt(i));
+ pw.print("ident", mActiveUidIfaces.valueAt(i));
pw.println();
}
pw.decreaseIndent();
@@ -1863,7 +1865,7 @@
@GuardedBy("mStatsLock")
private void dumpProtoLocked(FileDescriptor fd) {
- final ProtoOutputStream proto = new ProtoOutputStream(fd);
+ final ProtoOutputStream proto = new ProtoOutputStream(new FileOutputStream(fd));
// TODO Right now it writes all history. Should it limit to the "since-boot" log?
diff --git a/packages/ConnectivityT/service/src/com/android/server/net/NetworkStatsSubscriptionsMonitor.java b/packages/ConnectivityT/service/src/com/android/server/net/NetworkStatsSubscriptionsMonitor.java
index 5646c75..93d0ae7 100644
--- a/packages/ConnectivityT/service/src/com/android/server/net/NetworkStatsSubscriptionsMonitor.java
+++ b/packages/ConnectivityT/service/src/com/android/server/net/NetworkStatsSubscriptionsMonitor.java
@@ -33,7 +33,7 @@
import android.util.Pair;
import com.android.internal.annotations.VisibleForTesting;
-import com.android.internal.util.CollectionUtils;
+import com.android.net.module.util.CollectionUtils;
import java.util.ArrayList;
import java.util.List;
@@ -99,18 +99,19 @@
// prevent binder call to telephony when querying RAT. Keep listener registration with empty
// IMSI is meaningless since the RAT type changed is ambiguous for multi-SIM if reported
// with empty IMSI. So filter the subs w/o a valid IMSI to prevent such registration.
- final List<Pair<Integer, String>> filteredNewSubs =
- CollectionUtils.mapNotNull(newSubs, subId -> {
- final String subscriberId = mTeleManager.getSubscriberId(subId);
- return TextUtils.isEmpty(subscriberId) ? null : new Pair(subId, subscriberId);
- });
+ final List<Pair<Integer, String>> filteredNewSubs = new ArrayList<>();
+ for (final int subId : newSubs) {
+ final String subscriberId = mTeleManager.getSubscriberId(subId);
+ if (!TextUtils.isEmpty(subscriberId)) {
+ filteredNewSubs.add(new Pair(subId, subscriberId));
+ }
+ }
for (final Pair<Integer, String> sub : filteredNewSubs) {
// Fully match listener with subId and IMSI, since in some rare cases, IMSI might be
// suddenly change regardless of subId, such as switch IMSI feature in modem side.
// If that happens, register new listener with new IMSI and remove old one later.
- if (CollectionUtils.find(mRatListeners,
- it -> it.equalsKey(sub.first, sub.second)) != null) {
+ if (CollectionUtils.any(mRatListeners, it -> it.equalsKey(sub.first, sub.second))) {
continue;
}
@@ -126,8 +127,8 @@
for (final RatTypeListener listener : new ArrayList<>(mRatListeners)) {
// If there is no subId and IMSI matched the listener, removes it.
- if (CollectionUtils.find(filteredNewSubs,
- it -> listener.equalsKey(it.first, it.second)) == null) {
+ if (!CollectionUtils.any(filteredNewSubs,
+ it -> listener.equalsKey(it.first, it.second))) {
handleRemoveRatTypeListener(listener);
}
}
@@ -148,9 +149,10 @@
* @return collapsed RatType for the given subscriberId
*/
public int getRatTypeForSubscriberId(@NonNull String subscriberId) {
- final RatTypeListener match = CollectionUtils.find(mRatListeners,
+ final int index = CollectionUtils.indexOf(mRatListeners,
it -> TextUtils.equals(subscriberId, it.mSubscriberId));
- return match != null ? match.mLastCollapsedRatType : TelephonyManager.NETWORK_TYPE_UNKNOWN;
+ return index != -1 ? mRatListeners.get(index).mLastCollapsedRatType
+ : TelephonyManager.NETWORK_TYPE_UNKNOWN;
}
/**
diff --git a/packages/PackageInstaller/src/com/android/packageinstaller/wear/PackageInstallerImpl.java b/packages/PackageInstaller/src/com/android/packageinstaller/wear/PackageInstallerImpl.java
index 7e0490a..063d789 100644
--- a/packages/PackageInstaller/src/com/android/packageinstaller/wear/PackageInstallerImpl.java
+++ b/packages/PackageInstaller/src/com/android/packageinstaller/wear/PackageInstallerImpl.java
@@ -264,7 +264,8 @@
String action = ACTION_INSTALL_COMMIT + "." + packageName;
IntentFilter intentFilter = new IntentFilter();
intentFilter.addAction(action);
- mContext.registerReceiver(broadcastReceiver, intentFilter);
+ mContext.registerReceiver(broadcastReceiver, intentFilter,
+ Context.RECEIVER_EXPORTED_UNAUDITED);
// Create a matching PendingIntent and use it to generate the IntentSender
Intent broadcastIntent = new Intent(action);
diff --git a/packages/SettingsLib/src/com/android/settingslib/wifi/WifiTracker.java b/packages/SettingsLib/src/com/android/settingslib/wifi/WifiTracker.java
index bf5ab1c..426ea42 100644
--- a/packages/SettingsLib/src/com/android/settingslib/wifi/WifiTracker.java
+++ b/packages/SettingsLib/src/com/android/settingslib/wifi/WifiTracker.java
@@ -342,7 +342,8 @@
resumeScanning();
if (!mRegistered) {
- mContext.registerReceiver(mReceiver, mFilter, null /* permission */, mWorkHandler);
+ mContext.registerReceiver(mReceiver, mFilter, null /* permission */, mWorkHandler,
+ Context.RECEIVER_EXPORTED_UNAUDITED);
// NetworkCallback objects cannot be reused. http://b/20701525 .
mNetworkCallback = new WifiTrackerNetworkCallback();
mConnectivityManager.registerNetworkCallback(
diff --git a/packages/SettingsProvider/test/src/android/provider/SettingsBackupTest.java b/packages/SettingsProvider/test/src/android/provider/SettingsBackupTest.java
index ed813a0..368dda1 100644
--- a/packages/SettingsProvider/test/src/android/provider/SettingsBackupTest.java
+++ b/packages/SettingsProvider/test/src/android/provider/SettingsBackupTest.java
@@ -657,6 +657,7 @@
Settings.Global.Wearable.WRIST_ORIENTATION_MODE,
Settings.Global.Wearable.CLOCKWORK_SYSUI_PACKAGE,
Settings.Global.Wearable.CLOCKWORK_SYSUI_MAIN_ACTIVITY,
+ Settings.Global.Wearable.CLOCKWORK_LONG_PRESS_TO_ASSISTANT_ENABLED,
Settings.Global.Wearable.WEAR_ACTIVITY_AUTO_RESUME_TIMEOUT_SET_BY_USER);
private static final Set<String> BACKUP_DENY_LIST_SECURE_SETTINGS =
diff --git a/packages/SystemUI/res/values/strings.xml b/packages/SystemUI/res/values/strings.xml
index b64f81a..8482044 100644
--- a/packages/SystemUI/res/values/strings.xml
+++ b/packages/SystemUI/res/values/strings.xml
@@ -1273,6 +1273,10 @@
<!-- [CHAR LIMIT=NONE] Importance Tuner setting title -->
<string name="tuner_full_importance_settings">Power notification controls</string>
+
+ <!-- [CHAR LIMIT=NONE] Notification camera based rotation enabled description -->
+ <string name="rotation_lock_camera_rotation_on">On - Face-based</string>
+
<string name="power_notification_controls_description">With power notification controls, you can set an importance level from 0 to 5 for an app\'s notifications.
\n\n<b>Level 5</b>
\n- Show at the top of the notification list
diff --git a/packages/SystemUI/src/com/android/systemui/flags/Flags.java b/packages/SystemUI/src/com/android/systemui/flags/Flags.java
index 4e852a8..726f865 100644
--- a/packages/SystemUI/src/com/android/systemui/flags/Flags.java
+++ b/packages/SystemUI/src/com/android/systemui/flags/Flags.java
@@ -43,15 +43,18 @@
public static final BooleanFlag NEW_NOTIFICATION_PIPELINE_RENDERING =
new BooleanFlag(101, false);
- public static final BooleanFlag NOTIFICATION_UPDATES =
- new BooleanFlag(102, true);
-
public static final BooleanFlag NOTIFICATION_PIPELINE_DEVELOPER_LOGGING =
new BooleanFlag(103, false);
public static final ResourceBooleanFlag NOTIFICATION_SHADE_DRAG =
new ResourceBooleanFlag(104, R.bool.config_enableNotificationShadeDrag);
+ public static final BooleanFlag NSSL_DEBUG_LINES =
+ new BooleanFlag(105, false);
+
+ public static final BooleanFlag NSSL_DEBUG_REMOVE_ANIMATION =
+ new BooleanFlag(106, false);
+
/***************************************/
// 200 - keyguard/lockscreen
public static final BooleanFlag KEYGUARD_LAYOUT =
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardService.java b/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardService.java
index 22a69d4..fb31d88 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardService.java
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardService.java
@@ -583,6 +583,18 @@
checkPermission();
mKeyguardViewMediator.onShortPowerPressedGoHome();
}
+
+ @Override
+ public void dismissKeyguardToLaunch(Intent intentToLaunch) {
+ checkPermission();
+ mKeyguardViewMediator.dismissKeyguardToLaunch(intentToLaunch);
+ }
+
+ @Override
+ public void onSystemKeyPressed(int keycode) {
+ checkPermission();
+ mKeyguardViewMediator.onSystemKeyPressed(keycode);
+ }
};
}
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewMediator.java b/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewMediator.java
index 0512d48..8f4a28c 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewMediator.java
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewMediator.java
@@ -2750,6 +2750,14 @@
// do nothing
}
+ public void dismissKeyguardToLaunch(Intent intentToLaunch) {
+ // do nothing
+ }
+
+ public void onSystemKeyPressed(int keycode) {
+ // do nothing
+ }
+
public ViewMediatorCallback getViewMediatorCallback() {
return mViewMediatorCallback;
}
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/RotationLockTile.java b/packages/SystemUI/src/com/android/systemui/qs/tiles/RotationLockTile.java
index 0bbb5bd..082dc5c 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/RotationLockTile.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/RotationLockTile.java
@@ -16,12 +16,18 @@
package com.android.systemui.qs.tiles;
+import static android.hardware.SensorPrivacyManager.Sensors.CAMERA;
+
+import static com.android.systemui.statusbar.policy.RotationLockControllerImpl.hasSufficientPermission;
+
import android.content.Intent;
import android.content.res.Configuration;
import android.content.res.Resources;
+import android.hardware.SensorPrivacyManager;
import android.os.Handler;
import android.os.Looper;
import android.provider.Settings;
+import android.provider.Settings.Secure;
import android.service.quicksettings.Tile;
import android.view.View;
import android.widget.Switch;
@@ -38,18 +44,25 @@
import com.android.systemui.plugins.qs.QSTile.BooleanState;
import com.android.systemui.plugins.statusbar.StatusBarStateController;
import com.android.systemui.qs.QSHost;
+import com.android.systemui.qs.SettingObserver;
import com.android.systemui.qs.logging.QSLogger;
import com.android.systemui.qs.tileimpl.QSTileImpl;
+import com.android.systemui.statusbar.policy.BatteryController;
import com.android.systemui.statusbar.policy.RotationLockController;
import com.android.systemui.statusbar.policy.RotationLockController.RotationLockControllerCallback;
+import com.android.systemui.util.settings.SecureSettings;
import javax.inject.Inject;
/** Quick settings tile: Rotation **/
-public class RotationLockTile extends QSTileImpl<BooleanState> {
+public class RotationLockTile extends QSTileImpl<BooleanState> implements
+ BatteryController.BatteryStateChangeCallback {
private final Icon mIcon = ResourceIcon.get(com.android.internal.R.drawable.ic_qs_auto_rotate);
private final RotationLockController mController;
+ private final SensorPrivacyManager mPrivacyManager;
+ private final BatteryController mBatteryController;
+ private final SettingObserver mSetting;
@Inject
public RotationLockTile(
@@ -61,12 +74,41 @@
StatusBarStateController statusBarStateController,
ActivityStarter activityStarter,
QSLogger qsLogger,
- RotationLockController rotationLockController
+ RotationLockController rotationLockController,
+ SensorPrivacyManager privacyManager,
+ BatteryController batteryController,
+ SecureSettings secureSettings
) {
super(host, backgroundLooper, mainHandler, falsingManager, metricsLogger,
statusBarStateController, activityStarter, qsLogger);
mController = rotationLockController;
mController.observe(this, mCallback);
+ mPrivacyManager = privacyManager;
+ mBatteryController = batteryController;
+ int currentUser = host.getUserContext().getUserId();
+ mSetting = new SettingObserver(
+ secureSettings,
+ mHandler,
+ Secure.CAMERA_AUTOROTATE,
+ currentUser
+ ) {
+ @Override
+ protected void handleValueChanged(int value, boolean observedChange) {
+ // mHandler is the background handler so calling this is OK
+ handleRefreshState(null);
+ }
+ };
+ mBatteryController.observe(getLifecycle(), this);
+ }
+
+ @Override
+ protected void handleInitialize() {
+ mPrivacyManager.addSensorPrivacyListener(CAMERA, mSensorPrivacyChangedListener);
+ }
+
+ @Override
+ public void onPowerSaveChanged(boolean isPowerSave) {
+ refreshState();
}
@Override
@@ -95,14 +137,46 @@
protected void handleUpdateState(BooleanState state, Object arg) {
final boolean rotationLocked = mController.isRotationLocked();
+ final boolean powerSave = mBatteryController.isPowerSave();
+ final boolean cameraLocked = mPrivacyManager.isSensorPrivacyEnabled(CAMERA);
+ final boolean cameraRotation =
+ !powerSave && !cameraLocked && hasSufficientPermission(mContext)
+ && mController.isCameraRotationEnabled();
state.value = !rotationLocked;
state.label = mContext.getString(R.string.quick_settings_rotation_unlocked_label);
state.icon = mIcon;
state.contentDescription = getAccessibilityString(rotationLocked);
+ if (!rotationLocked && cameraRotation) {
+ state.secondaryLabel = mContext.getResources().getString(
+ R.string.rotation_lock_camera_rotation_on);
+ } else {
+ state.secondaryLabel = "";
+ }
+ state.stateDescription = state.secondaryLabel;
+
state.expandedAccessibilityClassName = Switch.class.getName();
state.state = state.value ? Tile.STATE_ACTIVE : Tile.STATE_INACTIVE;
}
+ @Override
+ protected void handleDestroy() {
+ super.handleDestroy();
+ mSetting.setListening(false);
+ mPrivacyManager.removeSensorPrivacyListener(CAMERA, mSensorPrivacyChangedListener);
+ }
+
+ @Override
+ public void handleSetListening(boolean listening) {
+ super.handleSetListening(listening);
+ mSetting.setListening(listening);
+ }
+
+ @Override
+ protected void handleUserSwitch(int newUserId) {
+ mSetting.setUserId(newUserId);
+ handleRefreshState(null);
+ }
+
public static boolean isCurrentOrientationLockPortrait(RotationLockController controller,
Resources resources) {
int lockOrientation = controller.getRotationLockOrientation();
@@ -140,4 +214,8 @@
refreshState(rotationLocked);
}
};
+
+ private final SensorPrivacyManager.OnSensorPrivacyChangedListener
+ mSensorPrivacyChangedListener =
+ (sensor, enabled) -> refreshState();
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationMediaManager.java b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationMediaManager.java
index 210ee96..3730d12 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationMediaManager.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationMediaManager.java
@@ -379,6 +379,7 @@
}
}
+ @Nullable
public String getMediaNotificationKey() {
return mMediaNotificationKey;
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationEntryManager.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationEntryManager.java
index f500d39..4717b3a 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationEntryManager.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationEntryManager.java
@@ -20,8 +20,6 @@
import static com.android.systemui.statusbar.notification.collection.NotifCollection.REASON_UNKNOWN;
import static com.android.systemui.statusbar.notification.row.NotificationRowContentBinder.InflationCallback;
-import android.annotation.NonNull;
-import android.annotation.Nullable;
import android.app.Notification;
import android.os.RemoteException;
import android.os.SystemClock;
@@ -33,6 +31,9 @@
import android.util.ArraySet;
import android.util.Log;
+import androidx.annotation.NonNull;
+import androidx.annotation.Nullable;
+
import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.statusbar.IStatusBarService;
import com.android.internal.statusbar.NotificationVisibility;
@@ -123,8 +124,8 @@
* filtered out if for instance they are not for the current user
*/
private final ArrayMap<String, NotificationEntry> mActiveNotifications = new ArrayMap<>();
- @VisibleForTesting
/** This is the list of "active notifications for this user in this context" */
+ @VisibleForTesting
protected final ArrayList<NotificationEntry> mSortedAndFiltered = new ArrayList<>();
private final List<NotificationEntry> mReadOnlyNotifications =
Collections.unmodifiableList(mSortedAndFiltered);
@@ -899,7 +900,7 @@
}
/** Calls to NotificationRankingManager and updates mSortedAndFiltered */
- private void updateRankingAndSort(@NonNull RankingMap rankingMap, String reason) {
+ private void updateRankingAndSort(RankingMap rankingMap, String reason) {
if (mNotifPipelineFlags.isNewPipelineEnabled()) {
mLogger.logUseWhileNewPipelineActive("updateRankingAndSort", reason);
return;
@@ -961,6 +962,7 @@
* Returns a collections containing ALL notifications we know about, including ones that are
* hidden or for other users. See {@link CommonNotifCollection#getAllNotifs()}.
*/
+ @NonNull
@Override
public Collection<NotificationEntry> getAllNotifs() {
mNotifPipelineFlags.checkLegacyPipelineEnabled();
@@ -969,7 +971,7 @@
@Nullable
@Override
- public NotificationEntry getEntry(String key) {
+ public NotificationEntry getEntry(@NonNull String key) {
mNotifPipelineFlags.checkLegacyPipelineEnabled();
return getPendingOrActiveNotif(key);
}
@@ -989,7 +991,7 @@
}
@Override
- public void addCollectionListener(NotifCollectionListener listener) {
+ public void addCollectionListener(@NonNull NotifCollectionListener listener) {
mNotifCollectionListeners.add(listener);
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/NotifCollection.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/NotifCollection.java
index f8f1279..b6b9c3f 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/NotifCollection.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/NotifCollection.java
@@ -44,7 +44,6 @@
import android.annotation.IntDef;
import android.annotation.MainThread;
-import android.annotation.Nullable;
import android.annotation.UserIdInt;
import android.app.Notification;
import android.os.Handler;
@@ -59,6 +58,7 @@
import android.util.Pair;
import androidx.annotation.NonNull;
+import androidx.annotation.Nullable;
import com.android.internal.statusbar.IStatusBarService;
import com.android.systemui.Dumpable;
@@ -193,7 +193,8 @@
}
/** @see NotifPipeline#getEntry(String) () */
- NotificationEntry getEntry(String key) {
+ @Nullable
+ NotificationEntry getEntry(@NonNull String key) {
return mNotificationSet.get(key);
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/BubbleCoordinator.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/BubbleCoordinator.java
index e9b7caa5..85c0064 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/BubbleCoordinator.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/BubbleCoordinator.java
@@ -82,11 +82,8 @@
public void attach(NotifPipeline pipeline) {
mNotifPipeline = pipeline;
mNotifPipeline.addNotificationDismissInterceptor(mDismissInterceptor);
- mNotifPipeline.addFinalizeFilter(mNotifFilter);
- if (mBubblesManagerOptional.isPresent()) {
- mBubblesManagerOptional.get().addNotifCallback(mNotifCallback);
- }
-
+ mNotifPipeline.addPreGroupFilter(mNotifFilter);
+ mBubblesManagerOptional.ifPresent(manager -> manager.addNotifCallback(mNotifCallback));
}
private final NotifFilter mNotifFilter = new NotifFilter(TAG) {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/KeyguardCoordinator.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/KeyguardCoordinator.java
index fe1cd7b..33005b3 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/KeyguardCoordinator.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/KeyguardCoordinator.java
@@ -90,6 +90,7 @@
readShowSilentNotificationSetting();
setupInvalidateNotifListCallbacks();
+ // Filter at the "finalize" stage so that views remain bound by PreparationCoordinator
pipeline.addFinalizeFilter(mNotifFilter);
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/MediaCoordinator.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/MediaCoordinator.java
index 8769969..ecee006 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/MediaCoordinator.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/MediaCoordinator.java
@@ -49,6 +49,6 @@
@Override
public void attach(NotifPipeline pipeline) {
- pipeline.addFinalizeFilter(mMediaFilter);
+ pipeline.addPreGroupFilter(mMediaFilter);
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/notifcollection/CommonNotifCollection.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/notifcollection/CommonNotifCollection.java
index 471c357..beaa1ba 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/notifcollection/CommonNotifCollection.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/notifcollection/CommonNotifCollection.java
@@ -16,6 +16,7 @@
package com.android.systemui.statusbar.notification.collection.notifcollection;
+import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import com.android.systemui.statusbar.notification.NotificationEntryManager;
@@ -37,7 +38,7 @@
* Registers a listener to be informed when notifications are created, added, updated, removed,
* or deleted.
*/
- void addCollectionListener(NotifCollectionListener listener);
+ void addCollectionListener(@NonNull NotifCollectionListener listener);
/**
* Returns the list of all known notifications, i.e. the notifications that are currently posted
@@ -46,11 +47,11 @@
*
* The returned collection is read-only, unsorted, unfiltered, and ungrouped.
*/
- Collection<NotificationEntry> getAllNotifs();
+ @NonNull Collection<NotificationEntry> getAllNotifs();
/**
* Returns the notification entry for the given notification key;
* the returned entry (if present) may be in any state.
*/
- @Nullable NotificationEntry getEntry(String key);
+ @Nullable NotificationEntry getEntry(@NonNull String key);
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/interruption/BypassHeadsUpNotifier.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/interruption/BypassHeadsUpNotifier.kt
index b2e15f4..b61a540 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/interruption/BypassHeadsUpNotifier.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/interruption/BypassHeadsUpNotifier.kt
@@ -77,11 +77,11 @@
override fun onPrimaryMetadataOrStateChanged(metadata: MediaMetadata?, state: Int) {
val previous = currentMediaEntry
- var newEntry = commonNotifCollection.getEntry(mediaManager.mediaNotificationKey)
- if (!NotificationMediaManager.isPlayingState(state)) {
- newEntry = null
- }
- currentMediaEntry = newEntry
+ val mediaNotificationKey = mediaManager.mediaNotificationKey
+ currentMediaEntry =
+ if (mediaNotificationKey != null && NotificationMediaManager.isPlayingState(state))
+ commonNotifCollection.getEntry(mediaNotificationKey)
+ else null
updateAutoHeadsUp(previous)
updateAutoHeadsUp(currentMediaEntry)
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationMenuRow.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationMenuRow.java
index 3a37fb4..9d599cb 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationMenuRow.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationMenuRow.java
@@ -41,11 +41,8 @@
import android.widget.FrameLayout.LayoutParams;
import com.android.internal.annotations.VisibleForTesting;
-import com.android.systemui.Dependency;
import com.android.systemui.R;
import com.android.systemui.animation.Interpolators;
-import com.android.systemui.flags.FeatureFlags;
-import com.android.systemui.flags.Flags;
import com.android.systemui.plugins.statusbar.NotificationMenuRowPlugin;
import com.android.systemui.statusbar.AlphaOptimizedImageView;
import com.android.systemui.statusbar.notification.collection.NotificationEntry;
@@ -304,12 +301,9 @@
} else {
mMenuContainer = new FrameLayout(mContext);
}
- // The setting can win (which is needed for tests) but if not set, then use the flag
final int showDismissSetting = Settings.Global.getInt(mContext.getContentResolver(),
- Settings.Global.SHOW_NEW_NOTIF_DISMISS, -1);
- final boolean newFlowHideShelf = showDismissSetting == -1
- ? Dependency.get(FeatureFlags.class).isEnabled(Flags.NOTIFICATION_UPDATES)
- : showDismissSetting == 1;
+ Settings.Global.SHOW_NEW_NOTIF_DISMISS, /* default = */ 1);
+ final boolean newFlowHideShelf = showDismissSetting == 1;
if (newFlowHideShelf) {
return;
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationRoundnessManager.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationRoundnessManager.java
index 1cb5e62..464fd06 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationRoundnessManager.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationRoundnessManager.java
@@ -21,8 +21,6 @@
import com.android.systemui.R;
import com.android.systemui.dagger.SysUISingleton;
-import com.android.systemui.flags.FeatureFlags;
-import com.android.systemui.flags.Flags;
import com.android.systemui.statusbar.notification.NotificationSectionsFeatureManager;
import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow;
import com.android.systemui.statusbar.notification.row.ExpandableView;
@@ -41,7 +39,6 @@
private final ExpandableView[] mLastInSectionViews;
private final ExpandableView[] mTmpFirstInSectionViews;
private final ExpandableView[] mTmpLastInSectionViews;
- private final FeatureFlags mFeatureFlags;
private boolean mExpanded;
private HashSet<ExpandableView> mAnimatedChildren;
private Runnable mRoundingChangedCallback;
@@ -56,9 +53,7 @@
@Inject
NotificationRoundnessManager(
- NotificationSectionsFeatureManager sectionsFeatureManager,
- FeatureFlags featureFlags) {
- mFeatureFlags = featureFlags;
+ NotificationSectionsFeatureManager sectionsFeatureManager) {
int numberOfSections = sectionsFeatureManager.getNumberOfBuckets();
mFirstInSectionViews = new ExpandableView[numberOfSections];
mLastInSectionViews = new ExpandableView[numberOfSections];
@@ -125,9 +120,6 @@
ExpandableView viewBefore,
ExpandableView viewSwiped,
ExpandableView viewAfter) {
- if (!mFeatureFlags.isEnabled(Flags.NOTIFICATION_UPDATES)) {
- return;
- }
final boolean animate = true;
ExpandableView oldViewBefore = mViewBeforeSwipedView;
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayout.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayout.java
index 3f16426..3e9ce25 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayout.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayout.java
@@ -43,7 +43,6 @@
import android.graphics.PointF;
import android.graphics.Rect;
import android.os.Bundle;
-import android.os.SystemProperties;
import android.os.UserHandle;
import android.provider.Settings;
import android.util.AttributeSet;
@@ -80,6 +79,8 @@
import com.android.systemui.ExpandHelper;
import com.android.systemui.R;
import com.android.systemui.animation.Interpolators;
+import com.android.systemui.flags.FeatureFlags;
+import com.android.systemui.flags.Flags;
import com.android.systemui.plugins.statusbar.NotificationSwipeActionHelper;
import com.android.systemui.statusbar.CommandQueue;
import com.android.systemui.statusbar.EmptyShadeView;
@@ -132,14 +133,6 @@
public static final float BACKGROUND_ALPHA_DIMMED = 0.7f;
private static final String TAG = "StackScroller";
- // Usage:
- // adb shell setprop persist.debug.nssl true && adb reboot
- private static final boolean DEBUG = SystemProperties.getBoolean("persist.debug.nssl",
- false /* default */);
- // TODO(b/187291379) disable again before release
- private static final boolean DEBUG_REMOVE_ANIMATION = SystemProperties.getBoolean(
- "persist.debug.nssl.dismiss", false /* default */);
-
// Delay in milli-seconds before shade closes for clear all.
private final int DELAY_BEFORE_SHADE_CLOSE = 200;
private boolean mShadeNeedsToClose = false;
@@ -192,7 +185,12 @@
private float mInitialTouchX;
private float mInitialTouchY;
+ private final boolean mDebugLines;
private Paint mDebugPaint;
+ /** Used to track the Y positions that were already used to draw debug text labels. */
+ private Set<Integer> mDebugTextUsedYPositions;
+ private final boolean mDebugRemoveAnimation;
+
private int mContentHeight;
private int mIntrinsicContentHeight;
private int mCollapsedSize;
@@ -569,6 +567,9 @@
public NotificationStackScrollLayout(Context context, AttributeSet attrs) {
super(context, attrs, 0, 0);
Resources res = getResources();
+ FeatureFlags featureFlags = Dependency.get(FeatureFlags.class);
+ mDebugLines = featureFlags.isEnabled(Flags.NSSL_DEBUG_LINES);
+ mDebugRemoveAnimation = featureFlags.isEnabled(Flags.NSSL_DEBUG_REMOVE_ANIMATION);
mSectionsManager = Dependency.get(NotificationSectionsManager.class);
mScreenOffAnimationController =
Dependency.get(ScreenOffAnimationController.class);
@@ -591,10 +592,10 @@
res.getBoolean(R.bool.config_drawNotificationBackground);
setOutlineProvider(mOutlineProvider);
- boolean willDraw = mShouldDrawNotificationBackground || DEBUG;
+ boolean willDraw = mShouldDrawNotificationBackground || mDebugLines;
setWillNotDraw(!willDraw);
mBackgroundPaint.setAntiAlias(true);
- if (DEBUG) {
+ if (mDebugLines) {
mDebugPaint = new Paint();
mDebugPaint.setColor(0xffff0000);
mDebugPaint.setStrokeWidth(2);
@@ -729,18 +730,17 @@
drawHeadsUpBackground(canvas);
}
- if (DEBUG) {
+ if (mDebugLines) {
onDrawDebug(canvas);
}
}
- /** Used to track the Y positions that were already used to draw debug text labels. */
- private static final Set<Integer> DEBUG_TEXT_USED_Y_POSITIONS =
- DEBUG ? new HashSet<>() : Collections.emptySet();
-
private void onDrawDebug(Canvas canvas) {
- DEBUG_TEXT_USED_Y_POSITIONS.clear();
-
+ if (mDebugTextUsedYPositions == null) {
+ mDebugTextUsedYPositions = new HashSet<>();
+ } else {
+ mDebugTextUsedYPositions.clear();
+ }
int y = mTopPadding;
drawDebugInfo(canvas, y, Color.RED, /* label= */ "mTopPadding");
@@ -776,10 +776,10 @@
private int computeDebugYTextPosition(int lineY) {
int textY = lineY;
- while (DEBUG_TEXT_USED_Y_POSITIONS.contains(textY)) {
+ while (mDebugTextUsedYPositions.contains(textY)) {
textY = (int) (textY + mDebugPaint.getTextSize());
}
- DEBUG_TEXT_USED_Y_POSITIONS.add(textY);
+ mDebugTextUsedYPositions.add(textY);
return textY;
}
@@ -2702,14 +2702,14 @@
@ShadeViewRefactor(RefactorComponent.STATE_RESOLVER)
boolean generateRemoveAnimation(ExpandableView child) {
String key = "";
- if (DEBUG_REMOVE_ANIMATION) {
+ if (mDebugRemoveAnimation) {
if (child instanceof ExpandableNotificationRow) {
key = ((ExpandableNotificationRow) child).getEntry().getKey();
}
Log.d(TAG, "generateRemoveAnimation " + key);
}
if (removeRemovedChildFromHeadsUpChangeAnimations(child)) {
- if (DEBUG_REMOVE_ANIMATION) {
+ if (mDebugRemoveAnimation) {
Log.d(TAG, "removedBecauseOfHeadsUp " + key);
}
mAddedHeadsUpChildren.remove(child);
@@ -2720,7 +2720,7 @@
mClearTransientViewsWhenFinished.add(child);
return true;
}
- if (DEBUG_REMOVE_ANIMATION) {
+ if (mDebugRemoveAnimation) {
Log.d(TAG, "generateRemove " + key
+ "\nmIsExpanded " + mIsExpanded
+ "\nmAnimationsEnabled " + mAnimationsEnabled
@@ -2728,7 +2728,7 @@
}
if (mIsExpanded && mAnimationsEnabled && !isChildInInvisibleGroup(child)) {
if (!mChildrenToAddAnimated.contains(child)) {
- if (DEBUG_REMOVE_ANIMATION) {
+ if (mDebugRemoveAnimation) {
Log.d(TAG, "needsAnimation = true " + key);
}
// Generate Animations
@@ -3227,7 +3227,7 @@
ignoreChildren);
mAnimationEvents.add(event);
mSwipedOutViews.remove(child);
- if (DEBUG_REMOVE_ANIMATION) {
+ if (mDebugRemoveAnimation) {
String key = "";
if (child instanceof ExpandableNotificationRow) {
key = ((ExpandableNotificationRow) child).getEntry().getKey();
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/RotationLockController.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/RotationLockController.java
index f258fb1..1158324 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/RotationLockController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/RotationLockController.java
@@ -23,6 +23,7 @@
int getRotationLockOrientation();
boolean isRotationLockAffordanceVisible();
boolean isRotationLocked();
+ boolean isCameraRotationEnabled();
void setRotationLocked(boolean locked);
void setRotationLockedAtAngle(boolean locked, int rotation);
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/RotationLockControllerImpl.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/RotationLockControllerImpl.java
index 67f5364..3143a47 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/RotationLockControllerImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/RotationLockControllerImpl.java
@@ -18,6 +18,9 @@
import static com.android.systemui.statusbar.policy.dagger.StatusBarPolicyModule.DEVICE_STATE_ROTATION_LOCK_DEFAULTS;
+import android.Manifest;
+import android.content.Context;
+import android.content.pm.PackageManager;
import android.os.UserHandle;
import androidx.annotation.NonNull;
@@ -34,6 +37,7 @@
/** Platform implementation of the rotation lock controller. **/
@SysUISingleton
public final class RotationLockControllerImpl implements RotationLockController {
+
private final CopyOnWriteArrayList<RotationLockControllerCallback> mCallbacks =
new CopyOnWriteArrayList<>();
@@ -86,11 +90,15 @@
return mRotationPolicy.isRotationLocked();
}
+ public boolean isCameraRotationEnabled() {
+ return mRotationPolicy.isCameraRotationEnabled();
+ }
+
public void setRotationLocked(boolean locked) {
mRotationPolicy.setRotationLock(locked);
}
- public void setRotationLockedAtAngle(boolean locked, int rotation){
+ public void setRotationLockedAtAngle(boolean locked, int rotation) {
mRotationPolicy.setRotationLockAtAngle(locked, rotation);
}
@@ -121,4 +129,11 @@
callback.onRotationLockStateChanged(mRotationPolicy.isRotationLocked(),
mRotationPolicy.isRotationLockToggleVisible());
}
+
+ public static boolean hasSufficientPermission(Context context) {
+ final PackageManager packageManager = context.getPackageManager();
+ final String rotationPackage = packageManager.getRotationResolverPackageName();
+ return rotationPackage != null && packageManager.checkPermission(
+ Manifest.permission.CAMERA, rotationPackage) == PackageManager.PERMISSION_GRANTED;
+ }
}
diff --git a/packages/SystemUI/src/com/android/systemui/util/wrapper/RotationPolicyWrapper.kt b/packages/SystemUI/src/com/android/systemui/util/wrapper/RotationPolicyWrapper.kt
index 2a0cc7d..b64d7be 100644
--- a/packages/SystemUI/src/com/android/systemui/util/wrapper/RotationPolicyWrapper.kt
+++ b/packages/SystemUI/src/com/android/systemui/util/wrapper/RotationPolicyWrapper.kt
@@ -17,8 +17,10 @@
package com.android.systemui.util.wrapper
import android.content.Context
+import android.provider.Settings.Secure.CAMERA_AUTOROTATE
import com.android.internal.view.RotationPolicy
import com.android.internal.view.RotationPolicy.RotationPolicyListener
+import com.android.systemui.util.settings.SecureSettings
import javax.inject.Inject
/**
@@ -30,12 +32,16 @@
fun getRotationLockOrientation(): Int
fun isRotationLockToggleVisible(): Boolean
fun isRotationLocked(): Boolean
+ fun isCameraRotationEnabled(): Boolean
fun registerRotationPolicyListener(listener: RotationPolicyListener, userHandle: Int)
fun unregisterRotationPolicyListener(listener: RotationPolicyListener)
}
-class RotationPolicyWrapperImpl @Inject constructor(private val context: Context) :
- RotationPolicyWrapper {
+class RotationPolicyWrapperImpl @Inject constructor(
+ private val context: Context,
+ private val secureSettings: SecureSettings
+) :
+ RotationPolicyWrapper {
override fun setRotationLock(enabled: Boolean) {
RotationPolicy.setRotationLock(context, enabled)
@@ -54,6 +60,9 @@
override fun isRotationLocked(): Boolean =
RotationPolicy.isRotationLocked(context)
+ override fun isCameraRotationEnabled(): Boolean =
+ secureSettings.getInt(CAMERA_AUTOROTATE, 0) == 1
+
override fun registerRotationPolicyListener(
listener: RotationPolicyListener,
userHandle: Int
diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/RotationLockTileTest.java b/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/RotationLockTileTest.java
new file mode 100644
index 0000000..55c51b2
--- /dev/null
+++ b/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/RotationLockTileTest.java
@@ -0,0 +1,212 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.qs.tiles;
+
+import static android.hardware.SensorPrivacyManager.Sensors.CAMERA;
+
+import static junit.framework.TestCase.assertEquals;
+
+import static org.mockito.Mockito.doReturn;
+import static org.mockito.Mockito.when;
+
+import android.Manifest;
+import android.content.pm.PackageManager;
+import android.hardware.SensorPrivacyManager;
+import android.os.Handler;
+import android.test.suitebuilder.annotation.SmallTest;
+import android.testing.AndroidTestingRunner;
+import android.testing.TestableLooper;
+
+import com.android.internal.logging.MetricsLogger;
+import com.android.systemui.R;
+import com.android.systemui.SysuiTestCase;
+import com.android.systemui.classifier.FalsingManagerFake;
+import com.android.systemui.plugins.ActivityStarter;
+import com.android.systemui.plugins.statusbar.StatusBarStateController;
+import com.android.systemui.qs.QSTileHost;
+import com.android.systemui.qs.logging.QSLogger;
+import com.android.systemui.statusbar.policy.BatteryController;
+import com.android.systemui.statusbar.policy.DeviceStateRotationLockSettingController;
+import com.android.systemui.statusbar.policy.RotationLockController;
+import com.android.systemui.statusbar.policy.RotationLockControllerImpl;
+import com.android.systemui.util.settings.FakeSettings;
+import com.android.systemui.util.wrapper.RotationPolicyWrapper;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.Mock;
+import org.mockito.MockitoAnnotations;
+
+
+@RunWith(AndroidTestingRunner.class)
+@TestableLooper.RunWithLooper(setAsMainLooper = true)
+@SmallTest
+public class RotationLockTileTest extends SysuiTestCase {
+
+ private static final String PACKAGE_NAME = "package_name";
+ private static final String[] DEFAULT_SETTINGS = new String[]{
+ "0:0",
+ "1:2"
+ };
+
+ @Mock
+ private PackageManager mPackageManager;
+ @Mock
+ private ActivityStarter mActivityStarter;
+ @Mock
+ private QSTileHost mHost;
+ @Mock
+ private MetricsLogger mMetricsLogger;
+ @Mock
+ private StatusBarStateController mStatusBarStateController;
+ @Mock
+ private QSLogger mQSLogger;
+ @Mock
+ private SensorPrivacyManager mPrivacyManager;
+ @Mock
+ private BatteryController mBatteryController;
+ @Mock
+ DeviceStateRotationLockSettingController mDeviceStateRotationLockSettingController;
+ @Mock
+ RotationPolicyWrapper mRotationPolicyWrapper;
+
+ private RotationLockController mController;
+ private TestableLooper mTestableLooper;
+ private RotationLockTile mLockTile;
+
+ @Before
+ public void setUp() throws Exception {
+ MockitoAnnotations.initMocks(this);
+ mTestableLooper = TestableLooper.get(this);
+
+ when(mHost.getContext()).thenReturn(mContext);
+ when(mHost.getUserContext()).thenReturn(mContext);
+
+ mController = new RotationLockControllerImpl(mRotationPolicyWrapper,
+ mDeviceStateRotationLockSettingController, DEFAULT_SETTINGS);
+
+ mLockTile = new RotationLockTile(
+ mHost,
+ mTestableLooper.getLooper(),
+ new Handler(mTestableLooper.getLooper()),
+ new FalsingManagerFake(),
+ mMetricsLogger,
+ mStatusBarStateController,
+ mActivityStarter,
+ mQSLogger,
+ mController,
+ mPrivacyManager,
+ mBatteryController,
+ new FakeSettings()
+ );
+
+ mLockTile.initialize();
+
+ // We are not setting the mocks to listening, so we trigger a first refresh state to
+ // set the initial state
+ mLockTile.refreshState();
+
+ mTestableLooper.processAllMessages();
+
+ mContext.setMockPackageManager(mPackageManager);
+ doReturn(PACKAGE_NAME).when(mPackageManager).getRotationResolverPackageName();
+ doReturn(PackageManager.PERMISSION_GRANTED).when(mPackageManager).checkPermission(
+ Manifest.permission.CAMERA, PACKAGE_NAME);
+
+ when(mBatteryController.isPowerSave()).thenReturn(false);
+ when(mPrivacyManager.isSensorPrivacyEnabled(CAMERA)).thenReturn(false);
+ enableAutoRotation();
+ enableCameraBasedRotation();
+
+ mLockTile.refreshState();
+ mTestableLooper.processAllMessages();
+ }
+
+ @Test
+ public void testSecondaryString_cameraRotateOn_returnsFaceBased() {
+ assertEquals(mContext.getString(R.string.rotation_lock_camera_rotation_on),
+ mLockTile.getState().secondaryLabel.toString());
+ }
+
+ @Test
+ public void testSecondaryString_rotateOff_isEmpty() {
+ disableAutoRotation();
+
+ mLockTile.refreshState();
+ mTestableLooper.processAllMessages();
+
+ assertEquals("", mLockTile.getState().secondaryLabel.toString());
+ }
+
+ @Test
+ public void testSecondaryString_cameraRotateOff_isEmpty() {
+ disableCameraBasedRotation();
+
+ mLockTile.refreshState();
+ mTestableLooper.processAllMessages();
+
+ assertEquals("", mLockTile.getState().secondaryLabel.toString());
+ }
+
+ @Test
+ public void testSecondaryString_powerSaveEnabled_isEmpty() {
+ when(mBatteryController.isPowerSave()).thenReturn(true);
+
+ mLockTile.refreshState();
+ mTestableLooper.processAllMessages();
+
+ assertEquals("", mLockTile.getState().secondaryLabel.toString());
+ }
+
+ @Test
+ public void testSecondaryString_cameraDisabled_isEmpty() {
+ when(mPrivacyManager.isSensorPrivacyEnabled(CAMERA)).thenReturn(true);
+
+ mLockTile.refreshState();
+ mTestableLooper.processAllMessages();
+
+ assertEquals("", mLockTile.getState().secondaryLabel.toString());
+ }
+
+ @Test
+ public void testSecondaryString_noCameraPermission_isEmpty() {
+ doReturn(PackageManager.PERMISSION_DENIED).when(mPackageManager).checkPermission(
+ Manifest.permission.CAMERA, PACKAGE_NAME);
+
+ mLockTile.refreshState();
+ mTestableLooper.processAllMessages();
+
+ assertEquals("", mLockTile.getState().secondaryLabel.toString());
+ }
+
+ private void enableAutoRotation() {
+ when(mRotationPolicyWrapper.isRotationLocked()).thenReturn(false);
+ }
+
+ private void disableAutoRotation() {
+ when(mRotationPolicyWrapper.isRotationLocked()).thenReturn(true);
+ }
+
+ private void enableCameraBasedRotation() {
+ when(mRotationPolicyWrapper.isCameraRotationEnabled()).thenReturn(true);
+ }
+
+ private void disableCameraBasedRotation() {
+ when(mRotationPolicyWrapper.isCameraRotationEnabled()).thenReturn(false);
+ }
+}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/coordinator/MediaCoordinatorTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/coordinator/MediaCoordinatorTest.java
index c5dc2b4..3ddff49 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/coordinator/MediaCoordinatorTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/coordinator/MediaCoordinatorTest.java
@@ -120,7 +120,7 @@
private NotifFilter captureFilter(MediaCoordinator coordinator) {
ArgumentCaptor<NotifFilter> filterCaptor = ArgumentCaptor.forClass(NotifFilter.class);
coordinator.attach(mNotifPipeline);
- verify(mNotifPipeline).addFinalizeFilter(filterCaptor.capture());
+ verify(mNotifPipeline).addPreGroupFilter(filterCaptor.capture());
return filterCaptor.getValue();
}
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/NotificationRoundnessManagerTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/NotificationRoundnessManagerTest.java
index 3c84c01..d3c1dc9 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/NotificationRoundnessManagerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/NotificationRoundnessManagerTest.java
@@ -32,7 +32,6 @@
import com.android.systemui.R;
import com.android.systemui.SysuiTestCase;
-import com.android.systemui.flags.FeatureFlags;
import com.android.systemui.statusbar.notification.NotificationSectionsFeatureManager;
import com.android.systemui.statusbar.notification.collection.NotificationEntry;
import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow;
@@ -44,7 +43,6 @@
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
-import org.mockito.Mock;
import org.mockito.MockitoAnnotations;
import java.util.HashSet;
@@ -59,8 +57,6 @@
private Runnable mRoundnessCallback = mock(Runnable.class);
private ExpandableNotificationRow mFirst;
private ExpandableNotificationRow mSecond;
- @Mock
- private FeatureFlags mFeatureFlags;
private float mSmallRadiusRatio;
@Before
@@ -70,8 +66,7 @@
mSmallRadiusRatio = resources.getDimension(R.dimen.notification_corner_radius_small)
/ resources.getDimension(R.dimen.notification_corner_radius);
mRoundnessManager = new NotificationRoundnessManager(
- new NotificationSectionsFeatureManager(new DeviceConfigProxy(), mContext),
- mFeatureFlags);
+ new NotificationSectionsFeatureManager(new DeviceConfigProxy(), mContext));
allowTestableLooperAsMainThread();
NotificationTestHelper testHelper = new NotificationTestHelper(
mContext,
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/DeviceStateRotationLockSettingControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/DeviceStateRotationLockSettingControllerTest.java
index 30717f4..db7b2f2 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/DeviceStateRotationLockSettingControllerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/DeviceStateRotationLockSettingControllerTest.java
@@ -225,6 +225,11 @@
}
@Override
+ public boolean isCameraRotationEnabled() {
+ throw new AssertionError("Not implemented");
+ }
+
+ @Override
public void registerRotationPolicyListener(RotationPolicy.RotationPolicyListener listener,
int userHandle) {
throw new AssertionError("Not implemented");
diff --git a/packages/SystemUI/tests/src/com/android/systemui/utils/leaks/FakeRotationLockController.java b/packages/SystemUI/tests/src/com/android/systemui/utils/leaks/FakeRotationLockController.java
index be11024..4f9cb35 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/utils/leaks/FakeRotationLockController.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/utils/leaks/FakeRotationLockController.java
@@ -51,6 +51,11 @@
}
@Override
+ public boolean isCameraRotationEnabled() {
+ return false;
+ }
+
+ @Override
public void setRotationLockedAtAngle(boolean locked, int rotation) {
}
diff --git a/services/companion/java/com/android/server/companion/AssociationRequestsProcessor.java b/services/companion/java/com/android/server/companion/AssociationRequestsProcessor.java
index 637994f..1914164 100644
--- a/services/companion/java/com/android/server/companion/AssociationRequestsProcessor.java
+++ b/services/companion/java/com/android/server/companion/AssociationRequestsProcessor.java
@@ -22,7 +22,6 @@
import static android.companion.CompanionDeviceManager.COMPANION_DEVICE_DISCOVERY_PACKAGE_NAME;
import static android.content.ComponentName.createRelative;
-import static com.android.internal.util.CollectionUtils.filter;
import static com.android.server.companion.CompanionDeviceManagerService.DEBUG;
import static com.android.server.companion.CompanionDeviceManagerService.LOG_TAG;
import static com.android.server.companion.PermissionsUtils.enforcePermissionsForAssociation;
@@ -57,6 +56,7 @@
import java.util.Arrays;
import java.util.HashSet;
+import java.util.List;
import java.util.Set;
/**
@@ -124,14 +124,17 @@
private static final int ASSOCIATE_WITHOUT_PROMPT_MAX_PER_TIME_WINDOW = 5;
private static final long ASSOCIATE_WITHOUT_PROMPT_WINDOW_MS = 60 * 60 * 1000; // 60 min;
- private final Context mContext;
- private final CompanionDeviceManagerService mService;
- private final PackageManagerInternal mPackageManager;
+ private final @NonNull Context mContext;
+ private final @NonNull CompanionDeviceManagerService mService;
+ private final @NonNull PackageManagerInternal mPackageManager;
+ private final @NonNull AssociationStore mAssociationStore;
- AssociationRequestsProcessor(CompanionDeviceManagerService service) {
+ AssociationRequestsProcessor(@NonNull CompanionDeviceManagerService service,
+ @NonNull AssociationStore associationStore) {
mContext = service.getContext();
mService = service;
mPackageManager = service.mPackageManagerInternal;
+ mAssociationStore = associationStore;
}
/**
@@ -330,18 +333,24 @@
}
// Throttle frequent associations
- long now = System.currentTimeMillis();
- Set<AssociationInfo> recentAssociations = filter(
- mService.getAssociations(userId, packageName),
- a -> now - a.getTimeApprovedMs() < ASSOCIATE_WITHOUT_PROMPT_WINDOW_MS);
-
- if (recentAssociations.size() >= ASSOCIATE_WITHOUT_PROMPT_MAX_PER_TIME_WINDOW) {
- Slog.w(TAG, "Too many associations. " + packageName
- + " already associated " + recentAssociations.size()
- + " devices within the last " + ASSOCIATE_WITHOUT_PROMPT_WINDOW_MS
- + "ms: " + recentAssociations);
- return false;
+ final long now = System.currentTimeMillis();
+ final List<AssociationInfo> associationForPackage =
+ mAssociationStore.getAssociationsForPackage(userId, packageName);
+ // Number of "recent" associations.
+ int recent = 0;
+ for (AssociationInfo association : associationForPackage) {
+ final boolean isRecent =
+ now - association.getTimeApprovedMs() < ASSOCIATE_WITHOUT_PROMPT_WINDOW_MS;
+ if (isRecent) {
+ if (++recent >= ASSOCIATE_WITHOUT_PROMPT_MAX_PER_TIME_WINDOW) {
+ Slog.w(TAG, "Too many associations: " + packageName + " already "
+ + "associated " + recent + " devices within the last "
+ + ASSOCIATE_WITHOUT_PROMPT_WINDOW_MS + "ms");
+ return false;
+ }
+ }
}
+
String[] sameOemCerts = mContext.getResources()
.getStringArray(com.android.internal.R.array.config_companionDeviceCerts);
diff --git a/services/companion/java/com/android/server/companion/AssociationStore.java b/services/companion/java/com/android/server/companion/AssociationStore.java
new file mode 100644
index 0000000..58fc8f7
--- /dev/null
+++ b/services/companion/java/com/android/server/companion/AssociationStore.java
@@ -0,0 +1,126 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.companion;
+
+import android.annotation.IntDef;
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.annotation.UserIdInt;
+import android.companion.AssociationInfo;
+
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.util.Collection;
+import java.util.List;
+
+/**
+ * Interface for a store of {@link AssociationInfo}-s.
+ */
+public interface AssociationStore {
+
+ @IntDef(prefix = { "CHANGE_TYPE_" }, value = {
+ CHANGE_TYPE_ADDED,
+ CHANGE_TYPE_REMOVED,
+ CHANGE_TYPE_UPDATED_ADDRESS_CHANGED,
+ CHANGE_TYPE_UPDATED_ADDRESS_UNCHANGED,
+ })
+ @Retention(RetentionPolicy.SOURCE)
+ @interface ChangeType {}
+
+ int CHANGE_TYPE_ADDED = 0;
+ int CHANGE_TYPE_REMOVED = 1;
+ int CHANGE_TYPE_UPDATED_ADDRESS_CHANGED = 2;
+ int CHANGE_TYPE_UPDATED_ADDRESS_UNCHANGED = 3;
+
+ /** Listener for any changes to {@link AssociationInfo}-s. */
+ interface OnChangeListener {
+ default void onAssociationChanged(
+ @ChangeType int changeType, AssociationInfo association) {}
+
+ default void onAssociationAdded(AssociationInfo association) {}
+
+ default void onAssociationRemoved(AssociationInfo association) {}
+
+ default void onAssociationUpdated(AssociationInfo association, boolean addressChanged) {}
+ }
+
+ /**
+ * @return all CDM associations.
+ */
+ @NonNull
+ Collection<AssociationInfo> getAssociations();
+
+ /**
+ * @return a {@link List} of associations that belong to the user.
+ */
+ @NonNull
+ List<AssociationInfo> getAssociationsForUser(@UserIdInt int userId);
+
+ /**
+ * @return a {@link List} of association that belong to the package.
+ */
+ @NonNull
+ List<AssociationInfo> getAssociationsForPackage(
+ @UserIdInt int userId, @NonNull String packageName);
+
+ /**
+ * @return an association with the given address that belong to the given package if such an
+ * association exists, otherwise {@code null}.
+ */
+ @Nullable
+ AssociationInfo getAssociationsForPackageWithAddress(
+ @UserIdInt int userId, @NonNull String packageName, @NonNull String macAddress);
+
+ /**
+ * @return an association with the given id if such an association exists, otherwise
+ * {@code null}.
+ */
+ @Nullable
+ AssociationInfo getAssociationById(int id);
+
+ /**
+ * @return all associations with the given MAc address.
+ */
+ @NonNull
+ List<AssociationInfo> getAssociationsByAddress(@NonNull String macAddress);
+
+ /** Register a {@link OnChangeListener} */
+ void registerListener(@NonNull OnChangeListener listener);
+
+ /** Un-register a previously registered {@link OnChangeListener} */
+ void unregisterListener(@NonNull OnChangeListener listener);
+
+ /** @hide */
+ static String changeTypeToString(@ChangeType int changeType) {
+ switch (changeType) {
+ case CHANGE_TYPE_ADDED:
+ return "ASSOCIATION_ADDED";
+
+ case CHANGE_TYPE_REMOVED:
+ return "ASSOCIATION_REMOVED";
+
+ case CHANGE_TYPE_UPDATED_ADDRESS_CHANGED:
+ return "ASSOCIATION_UPDATED";
+
+ case CHANGE_TYPE_UPDATED_ADDRESS_UNCHANGED:
+ return "ASSOCIATION_UPDATED_ADDRESS_UNCHANGED";
+
+ default:
+ return "Unknown (" + changeType + ")";
+ }
+ }
+}
diff --git a/services/companion/java/com/android/server/companion/AssociationStoreImpl.java b/services/companion/java/com/android/server/companion/AssociationStoreImpl.java
new file mode 100644
index 0000000..3f0200e
--- /dev/null
+++ b/services/companion/java/com/android/server/companion/AssociationStoreImpl.java
@@ -0,0 +1,302 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.companion;
+
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.annotation.UserIdInt;
+import android.companion.AssociationInfo;
+import android.net.MacAddress;
+import android.util.Log;
+import android.util.SparseArray;
+
+import com.android.internal.annotations.GuardedBy;
+import com.android.internal.util.CollectionUtils;
+
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.LinkedHashSet;
+import java.util.List;
+import java.util.Map;
+import java.util.Objects;
+import java.util.Set;
+
+/**
+ * Implementation of the {@link AssociationStore}, with addition of the methods for modification.
+ * <ul>
+ * <li> {@link #addAssociation(AssociationInfo)}
+ * <li> {@link #removeAssociation(int)}
+ * <li> {@link #updateAssociation(AssociationInfo)}
+ * </ul>
+ *
+ * The class has package-private access level, and instances of the class should only be created by
+ * the {@link CompanionDeviceManagerService}.
+ * Other system component (both inside and outside if the com.android.server.companion package)
+ * should use public {@link AssociationStore} interface.
+ */
+class AssociationStoreImpl implements AssociationStore {
+ private static final boolean DEBUG = false;
+ private static final String TAG = "AssociationStore";
+
+ private final Object mLock = new Object();
+
+ @GuardedBy("mLock")
+ private final Map<Integer, AssociationInfo> mIdMap;
+ @GuardedBy("mLock")
+ private final Map<MacAddress, Set<Integer>> mAddressMap;
+ @GuardedBy("mLock")
+ private final SparseArray<List<AssociationInfo>> mCachedPerUser = new SparseArray<>();
+
+ @GuardedBy("mListeners")
+ private final Set<OnChangeListener> mListeners = new LinkedHashSet<>();
+
+ AssociationStoreImpl(Collection<AssociationInfo> associations) {
+ synchronized (mLock) {
+ final int size = associations.size();
+ mIdMap = new HashMap<>(size);
+ mAddressMap = new HashMap<>(size);
+
+ for (AssociationInfo association : associations) {
+ final int id = association.getId();
+ mIdMap.put(id, association);
+
+ final MacAddress address = association.getDeviceMacAddress();
+ if (address != null) {
+ mAddressMap.computeIfAbsent(address, it -> new HashSet<>()).add(id);
+ }
+ }
+ }
+ }
+
+ void addAssociation(@NonNull AssociationInfo association) {
+ final int id = association.getId();
+
+ if (DEBUG) {
+ Log.i(TAG, "addAssociation() " + association.toShortString());
+ Log.d(TAG, " association=" + association);
+ }
+
+ synchronized (mLock) {
+ if (mIdMap.containsKey(id)) {
+ if (DEBUG) Log.w(TAG, "Association already stored.");
+ return;
+ }
+ mIdMap.put(id, association);
+
+ final MacAddress address = association.getDeviceMacAddress();
+ if (address != null) {
+ mAddressMap.computeIfAbsent(address, it -> new HashSet<>()).add(id);
+ }
+
+ invalidateCacheForUserLocked(association.getUserId());
+ }
+
+ broadcastChange(CHANGE_TYPE_ADDED, association);
+ }
+
+ void updateAssociation(@NonNull AssociationInfo updated) {
+ final int id = updated.getId();
+
+ if (DEBUG) {
+ Log.i(TAG, "updateAssociation() " + updated.toShortString());
+ Log.d(TAG, " updated=" + updated);
+ }
+
+ final AssociationInfo current;
+ final boolean macAddressChanged;
+ synchronized (mLock) {
+ current = mIdMap.get(id);
+ if (current == null) {
+ if (DEBUG) Log.w(TAG, "Association with id " + id + " does not exist.");
+ return;
+ }
+ if (DEBUG) Log.d(TAG, " current=" + current);
+
+ if (current.equals(updated)) {
+ if (DEBUG) Log.w(TAG, " No changes.");
+ return;
+ }
+
+ // Update the ID-to-Association map.
+ mIdMap.put(id, updated);
+
+ // Update the MacAddress-to-List<Association> map if needed.
+ final MacAddress updatedAddress = updated.getDeviceMacAddress();
+ final MacAddress currentAddress = current.getDeviceMacAddress();
+ macAddressChanged = Objects.equals(
+ current.getDeviceMacAddress(), updated.getDeviceMacAddress());
+ if (macAddressChanged) {
+ if (currentAddress != null) {
+ mAddressMap.get(currentAddress).remove(id);
+ }
+ if (updatedAddress != null) {
+ mAddressMap.computeIfAbsent(updatedAddress, it -> new HashSet<>()).add(id);
+ }
+ }
+ }
+
+ final int changeType = macAddressChanged ? CHANGE_TYPE_UPDATED_ADDRESS_CHANGED
+ : CHANGE_TYPE_UPDATED_ADDRESS_UNCHANGED;
+ broadcastChange(changeType, updated);
+ }
+
+ void removeAssociation(int id) {
+ if (DEBUG) Log.i(TAG, "removeAssociation() id=" + id);
+
+ final AssociationInfo association;
+ synchronized (mLock) {
+ association = mIdMap.remove(id);
+
+ if (association == null) {
+ if (DEBUG) Log.w(TAG, "Association with id " + id + " is not stored.");
+ return;
+ } else {
+ if (DEBUG) {
+ Log.i(TAG, "removed " + association.toShortString());
+ Log.d(TAG, " association=" + association);
+ }
+ }
+
+ final MacAddress macAddress = association.getDeviceMacAddress();
+ if (macAddress != null) {
+ mAddressMap.get(macAddress).remove(id);
+ }
+
+ invalidateCacheForUserLocked(association.getUserId());
+ }
+
+ broadcastChange(CHANGE_TYPE_REMOVED, association);
+ }
+
+ public @NonNull Collection<AssociationInfo> getAssociations() {
+ final Collection<AssociationInfo> allAssociations;
+ synchronized (mLock) {
+ allAssociations = mIdMap.values();
+ }
+ return Collections.unmodifiableCollection(allAssociations);
+ }
+
+ public @NonNull List<AssociationInfo> getAssociationsForUser(@UserIdInt int userId) {
+ synchronized (mLock) {
+ return getAssociationsForUserLocked(userId);
+ }
+ }
+
+ public @NonNull List<AssociationInfo> getAssociationsForPackage(
+ @UserIdInt int userId, @NonNull String packageName) {
+ final List<AssociationInfo> associationsForUser = getAssociationsForUser(userId);
+ final List<AssociationInfo> associationsForPackage =
+ CollectionUtils.filter(associationsForUser,
+ it -> it.getPackageName().equals(packageName));
+ return Collections.unmodifiableList(associationsForPackage);
+ }
+
+ public @Nullable AssociationInfo getAssociationsForPackageWithAddress(
+ @UserIdInt int userId, @NonNull String packageName, @NonNull String macAddress) {
+ final List<AssociationInfo> associations = getAssociationsByAddress(macAddress);
+ return CollectionUtils.find(associations,
+ it -> it.belongsToPackage(userId, packageName));
+ }
+
+ public @Nullable AssociationInfo getAssociationById(int id) {
+ synchronized (mLock) {
+ return mIdMap.get(id);
+ }
+ }
+
+ public @NonNull List<AssociationInfo> getAssociationsByAddress(@NonNull String macAddress) {
+ final MacAddress address = MacAddress.fromString(macAddress);
+
+ synchronized (mLock) {
+ final Set<Integer> ids = mAddressMap.get(address);
+ if (ids == null) return Collections.emptyList();
+
+ final List<AssociationInfo> associations = new ArrayList<>();
+ for (AssociationInfo association : mIdMap.values()) {
+ if (address.equals(association.getDeviceMacAddress())) {
+ associations.add(association);
+ }
+ }
+
+ return Collections.unmodifiableList(associations);
+ }
+ }
+
+ @GuardedBy("mLock")
+ private @NonNull List<AssociationInfo> getAssociationsForUserLocked(@UserIdInt int userId) {
+ final List<AssociationInfo> cached = mCachedPerUser.get(userId);
+ if (cached != null) {
+ return cached;
+ }
+
+ final List<AssociationInfo> associationsForUser = new ArrayList<>();
+ for (AssociationInfo association : mIdMap.values()) {
+ if (association.getUserId() == userId) {
+ associationsForUser.add(association);
+ }
+ }
+ final List<AssociationInfo> set = Collections.unmodifiableList(associationsForUser);
+ mCachedPerUser.set(userId, set);
+ return set;
+ }
+
+ @GuardedBy("mLock")
+ private void invalidateCacheForUserLocked(@UserIdInt int userId) {
+ mCachedPerUser.delete(userId);
+ }
+
+ public void registerListener(@NonNull OnChangeListener listener) {
+ synchronized (mListeners) {
+ mListeners.add(listener);
+ }
+ }
+
+ public void unregisterListener(@NonNull OnChangeListener listener) {
+ synchronized (mListeners) {
+ mListeners.remove(listener);
+ }
+ }
+
+ private void broadcastChange(@ChangeType int changeType, AssociationInfo association) {
+ synchronized (mListeners) {
+ for (OnChangeListener listener : mListeners) {
+ listener.onAssociationChanged(changeType, association);
+
+ switch (changeType) {
+ case CHANGE_TYPE_ADDED:
+ listener.onAssociationAdded(association);
+ break;
+
+ case CHANGE_TYPE_REMOVED:
+ listener.onAssociationRemoved(association);
+ break;
+
+ case CHANGE_TYPE_UPDATED_ADDRESS_CHANGED:
+ listener.onAssociationUpdated(association, true);
+ break;
+
+ case CHANGE_TYPE_UPDATED_ADDRESS_UNCHANGED:
+ listener.onAssociationUpdated(association, false);
+ break;
+ }
+ }
+ }
+ }
+}
diff --git a/services/companion/java/com/android/server/companion/CompanionDeviceManagerService.java b/services/companion/java/com/android/server/companion/CompanionDeviceManagerService.java
index a06672b..5aa1c93 100644
--- a/services/companion/java/com/android/server/companion/CompanionDeviceManagerService.java
+++ b/services/companion/java/com/android/server/companion/CompanionDeviceManagerService.java
@@ -27,15 +27,12 @@
import static android.os.Process.SYSTEM_UID;
import static android.os.UserHandle.getCallingUserId;
-import static com.android.internal.util.CollectionUtils.add;
import static com.android.internal.util.CollectionUtils.any;
-import static com.android.internal.util.CollectionUtils.filter;
import static com.android.internal.util.CollectionUtils.find;
-import static com.android.internal.util.CollectionUtils.forEach;
-import static com.android.internal.util.CollectionUtils.map;
import static com.android.internal.util.Preconditions.checkState;
import static com.android.internal.util.function.pooled.PooledLambda.obtainMessage;
import static com.android.internal.util.function.pooled.PooledLambda.obtainRunnable;
+import static com.android.server.companion.AssociationStore.CHANGE_TYPE_UPDATED_ADDRESS_UNCHANGED;
import static com.android.server.companion.PermissionsUtils.checkCallerCanManageAssociationsForPackage;
import static com.android.server.companion.PermissionsUtils.checkCallerCanManageCompanionDevice;
import static com.android.server.companion.PermissionsUtils.enforceCallerCanInteractWithUserId;
@@ -45,8 +42,6 @@
import static com.android.server.companion.RolesUtils.addRoleHolderForAssociation;
import static com.android.server.companion.RolesUtils.removeRoleHolderForAssociation;
-import static java.util.Collections.emptySet;
-import static java.util.Collections.unmodifiableSet;
import static java.util.Objects.requireNonNull;
import static java.util.concurrent.TimeUnit.MINUTES;
@@ -82,7 +77,6 @@
import android.content.pm.PackageItemInfo;
import android.content.pm.PackageManager;
import android.content.pm.PackageManagerInternal;
-import android.content.pm.UserInfo;
import android.net.MacAddress;
import android.net.NetworkPolicyManager;
import android.os.Binder;
@@ -112,9 +106,7 @@
import com.android.internal.notification.NotificationAccessConfirmationActivityContract;
import com.android.internal.os.BackgroundThread;
import com.android.internal.util.ArrayUtils;
-import com.android.internal.util.CollectionUtils;
import com.android.internal.util.DumpUtils;
-import com.android.internal.util.function.pooled.PooledLambda;
import com.android.server.FgThread;
import com.android.server.LocalServices;
import com.android.server.SystemService;
@@ -127,6 +119,7 @@
import java.text.DateFormat;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
+import java.util.Collections;
import java.util.Date;
import java.util.HashMap;
import java.util.HashSet;
@@ -135,12 +128,11 @@
import java.util.Objects;
import java.util.Set;
import java.util.TimeZone;
-import java.util.function.Function;
-import java.util.function.Predicate;
/** @hide */
@SuppressLint("LongLogTag")
-public class CompanionDeviceManagerService extends SystemService {
+public class CompanionDeviceManagerService extends SystemService
+ implements AssociationStore.OnChangeListener {
static final String LOG_TAG = "CompanionDeviceManagerService";
static final boolean DEBUG = false;
@@ -162,10 +154,11 @@
sDateFormat.setTimeZone(TimeZone.getDefault());
}
- private final CompanionDeviceManagerImpl mImpl;
// Persistent data store for all Associations.
- private final PersistentDataStore mPersistentDataStore;
- private final AssociationRequestsProcessor mAssociationRequestsProcessor;
+ private PersistentDataStore mPersistentStore;
+ private AssociationStoreImpl mAssociationStore;
+ private AssociationRequestsProcessor mAssociationRequestsProcessor;
+
private PowerWhitelistManager mPowerWhitelistManager;
private IAppOpsService mAppOpsManager;
private BluetoothAdapter mBluetoothAdapter;
@@ -187,20 +180,16 @@
private final RemoteCallbackList<IOnAssociationsChangedListener> mListeners =
new RemoteCallbackList<>();
- final Object mLock = new Object();
final Handler mMainHandler = Handler.getMain();
private CompanionDevicePresenceController mCompanionDevicePresenceController;
- /** Maps a {@link UserIdInt} to a set of associations for the user. */
- @GuardedBy("mLock")
- private final SparseArray<Set<AssociationInfo>> mCachedAssociations = new SparseArray<>();
/**
* A structure that consist of two nested maps, and effectively maps (userId + packageName) to
* a list of IDs that have been previously assigned to associations for that package.
* We maintain this structure so that we never re-use association IDs for the same package
* (until it's uninstalled).
*/
- @GuardedBy("mLock")
+ @GuardedBy("mPreviouslyUsedIds")
private final SparseArray<Map<String, Set<Integer>>> mPreviouslyUsedIds = new SparseArray<>();
ActivityTaskManagerInternal mAtmInternal;
@@ -209,8 +198,6 @@
public CompanionDeviceManagerService(Context context) {
super(context);
- mImpl = new CompanionDeviceManagerImpl();
- mPersistentDataStore = new PersistentDataStore();
mPowerWhitelistManager = context.getSystemService(PowerWhitelistManager.class);
mAppOpsManager = IAppOpsService.Stub.asInterface(
@@ -221,49 +208,36 @@
mPermissionControllerManager = requireNonNull(
context.getSystemService(PermissionControllerManager.class));
mUserManager = context.getSystemService(UserManager.class);
- mCompanionDevicePresenceController = new CompanionDevicePresenceController(this);
- mAssociationRequestsProcessor = new AssociationRequestsProcessor(this);
-
- registerPackageMonitor();
- }
-
- private void registerPackageMonitor() {
- new PackageMonitor() {
- @Override
- public void onPackageRemoved(String packageName, int uid) {
- final int userId = getChangingUserId();
- Slog.i(LOG_TAG, "onPackageRemoved() u" + userId + "/" + packageName);
-
- clearAssociationForPackage(userId, packageName);
- }
-
- @Override
- public void onPackageDataCleared(String packageName, int uid) {
- final int userId = getChangingUserId();
- Slog.i(LOG_TAG, "onPackageDataCleared() u" + userId + "/" + packageName);
-
- clearAssociationForPackage(userId, packageName);
- }
-
- @Override
- public void onPackageModified(String packageName) {
- final int userId = getChangingUserId();
- Slog.i(LOG_TAG, "onPackageModified() u" + userId + "/" + packageName);
-
- forEach(getAssociations(userId, packageName), association ->
- updateSpecialAccessPermissionForAssociatedPackage(association));
- }
- }.register(getContext(), FgThread.get().getLooper(), UserHandle.ALL, true);
}
@Override
public void onStart() {
- publishBinderService(Context.COMPANION_DEVICE_SERVICE, mImpl);
+ mPersistentStore = new PersistentDataStore();
+ final Set<AssociationInfo> allAssociations = new ArraySet<>();
+
+ synchronized (mPreviouslyUsedIds) {
+ // The data is stored in DE directories, so we can read the data for all users now
+ // (which would not be possible if the data was stored to CE directories).
+ mPersistentStore.readStateForUsers(
+ mUserManager.getAliveUsers(), allAssociations, mPreviouslyUsedIds);
+ }
+
+ mAssociationStore = new AssociationStoreImpl(allAssociations);
+ mAssociationStore.registerListener(this);
+
+ mCompanionDevicePresenceController = new CompanionDevicePresenceController(this);
+ mAssociationRequestsProcessor = new AssociationRequestsProcessor(this, mAssociationStore);
+
+ // Publish "binder service"
+ final CompanionDeviceManagerImpl impl = new CompanionDeviceManagerImpl();
+ publishBinderService(Context.COMPANION_DEVICE_SERVICE, impl);
}
@Override
public void onBootPhase(int phase) {
if (phase == SystemService.PHASE_SYSTEM_SERVICES_READY) {
+ registerPackageMonitor();
+
// Init Bluetooth
mBluetoothAdapter = BluetoothAdapter.getDefaultAdapter();
if (mBluetoothAdapter != null) {
@@ -282,7 +256,7 @@
@Override
public void onUserUnlocking(@NonNull TargetUser user) {
final int userId = user.getUserIdentifier();
- final Set<AssociationInfo> associations = getAllAssociationsForUser(userId);
+ final List<AssociationInfo> associations = mAssociationStore.getAssociationsForUser(userId);
if (associations.isEmpty()) return;
@@ -293,42 +267,18 @@
MINUTES.toMillis(10));
}
- @NonNull
- Set<AssociationInfo> getAllAssociationsForUser(@UserIdInt int userId) {
- synchronized (mLock) {
- readPersistedStateForUserIfNeededLocked(userId);
- // This returns non-null, because the readAssociationsInfoForUserIfNeededLocked() method
- // we just called adds an empty set, if there was no previously saved data.
- return mCachedAssociations.get(userId);
- }
- }
-
- @NonNull
- Set<AssociationInfo> getAssociations(@UserIdInt int userId, @NonNull String packageName) {
- return filter(getAllAssociationsForUser(userId),
- a -> a.belongsToPackage(userId, packageName));
- }
-
- @Nullable
- private AssociationInfo getAssociation(int associationId) {
- return find(getAllAssociations(), association -> association.getId() == associationId);
- }
-
- @Nullable
- AssociationInfo getAssociation(
- @UserIdInt int userId, @NonNull String packageName, @NonNull String macAddress) {
- return find(getAssociations(userId, packageName), a -> a.isLinkedTo(macAddress));
- }
-
@Nullable
AssociationInfo getAssociationWithCallerChecks(
@UserIdInt int userId, @NonNull String packageName, @NonNull String macAddress) {
- return sanitizeWithCallerChecks(getAssociation(userId, packageName, macAddress));
+ final AssociationInfo association = mAssociationStore.getAssociationsForPackageWithAddress(
+ userId, packageName, macAddress);
+ return sanitizeWithCallerChecks(association);
}
@Nullable
AssociationInfo getAssociationWithCallerChecks(int associationId) {
- return sanitizeWithCallerChecks(getAssociation(associationId));
+ final AssociationInfo association = mAssociationStore.getAssociationById(associationId);
+ return sanitizeWithCallerChecks(association);
}
@Nullable
@@ -344,19 +294,6 @@
return association;
}
- private Set<AssociationInfo> getAllAssociations() {
- final long identity = Binder.clearCallingIdentity();
- try {
- final Set<AssociationInfo> result = new ArraySet<>();
- for (UserInfo user : mUserManager.getAliveUsers()) {
- result.addAll(getAllAssociationsForUser(user.id));
- }
- return result;
- } finally {
- Binder.restoreCallingIdentity(identity);
- }
- }
-
void maybeGrantAutoRevokeExemptions() {
Slog.d(LOG_TAG, "maybeGrantAutoRevokeExemptions()");
PackageManager pm = getContext().getPackageManager();
@@ -369,10 +306,8 @@
}
try {
- Set<AssociationInfo> associations = getAllAssociationsForUser(userId);
- if (associations == null) {
- continue;
- }
+ final List<AssociationInfo> associations =
+ mAssociationStore.getAssociationsForUser(userId);
for (AssociationInfo a : associations) {
try {
int uid = pm.getPackageUidAsUser(a.getPackageName(), userId);
@@ -387,6 +322,61 @@
}
}
+ @Override
+ public void onAssociationChanged(
+ @AssociationStore.ChangeType int changeType, AssociationInfo association) {
+ final int id = association.getId();
+ final int userId = association.getUserId();
+ final String packageName = association.getPackageName();
+
+ if (changeType == AssociationStore.CHANGE_TYPE_REMOVED) {
+ markIdAsPreviouslyUsedForPackage(id, userId, packageName);
+ }
+
+ final List<AssociationInfo> updatedAssociations =
+ mAssociationStore.getAssociationsForUser(userId);
+ final Map<String, Set<Integer>> usedIdsForUser = getPreviouslyUsedIdsForUser(userId);
+ BackgroundThread.getHandler().post(() ->
+ mPersistentStore.persistStateForUser(userId, updatedAssociations, usedIdsForUser));
+
+ // Notify listeners if ADDED, REMOVED or UPDATED_ADDRESS_CHANGED.
+ // Do NOT notify when UPDATED_ADDRESS_UNCHANGED, which means a minor tweak in association's
+ // configs, which "listeners" won't (and shouldn't) be able to see.
+ if (changeType != CHANGE_TYPE_UPDATED_ADDRESS_UNCHANGED) {
+ notifyListeners(userId, updatedAssociations);
+ }
+ updateAtm(userId, updatedAssociations);
+
+ restartBleScan();
+ }
+
+ private void notifyListeners(
+ @UserIdInt int userId, @NonNull List<AssociationInfo> associations) {
+ mListeners.broadcast((listener, callbackUserId) -> {
+ if ((int) callbackUserId == userId) {
+ try {
+ listener.onAssociationsChanged(associations);
+ } catch (RemoteException ignored) {
+ }
+ }
+ });
+ }
+
+ private void markIdAsPreviouslyUsedForPackage(
+ int associationId, @UserIdInt int userId, @NonNull String packageName) {
+ synchronized (mPreviouslyUsedIds) {
+ Map<String, Set<Integer>> usedIdsForUser = mPreviouslyUsedIds.get(userId);
+ if (usedIdsForUser == null) {
+ usedIdsForUser = new HashMap<>();
+ mPreviouslyUsedIds.put(userId, usedIdsForUser);
+ }
+
+ final Set<Integer> usedIdsForPackage =
+ usedIdsForUser.computeIfAbsent(packageName, it -> new HashSet<>());
+ usedIdsForPackage.add(associationId);
+ }
+ }
+
class CompanionDeviceManagerImpl extends ICompanionDeviceManager.Stub {
@Override
@@ -424,8 +414,7 @@
checkUsesFeature(packageName, getCallingUserId());
}
- return new ArrayList<>(
- CompanionDeviceManagerService.this.getAssociations(userId, packageName));
+ return mAssociationStore.getAssociationsForPackage(userId, packageName);
}
@Override
@@ -433,8 +422,7 @@
enforceCallerCanInteractWithUserId(getContext(), userId);
enforceCallerCanManageCompanionDevice(getContext(), "getAllAssociationsForUser");
- return new ArrayList<>(
- CompanionDeviceManagerService.this.getAllAssociationsForUser(userId));
+ return mAssociationStore.getAssociationsForUser(userId);
}
@Override
@@ -470,7 +458,7 @@
+ "(ie. it belongs to a different package or a different user).");
}
- disassociateInternal(userId, association.getId());
+ disassociateInternal(association.getId());
}
@Override
@@ -483,7 +471,7 @@
+ "or belongs to a different user");
}
- disassociateInternal(association.getUserId(), associationId);
+ disassociateInternal(associationId);
}
@Override
@@ -540,7 +528,7 @@
return true;
}
- return any(CompanionDeviceManagerService.this.getAssociations(userId, packageName),
+ return any(mAssociationStore.getAssociationsForPackage(userId, packageName),
a -> a.isLinkedTo(macAddress));
}
@@ -623,25 +611,18 @@
final int userId = getCallingUserId();
enforceCallerIsSystemOr(userId, packageName);
- Set<AssociationInfo> deviceAssociations = filter(
- CompanionDeviceManagerService.this.getAssociations(userId, packageName),
- a -> a.isLinkedTo(deviceAddress));
+ final AssociationInfo association =
+ mAssociationStore.getAssociationsForPackageWithAddress(
+ userId, packageName, deviceAddress);
- if (deviceAssociations.isEmpty()) {
+ if (association == null) {
throw new RemoteException(new DeviceNotAssociatedException("App " + packageName
+ " is not associated with device " + deviceAddress
+ " for user " + userId));
}
- updateAssociations(associations -> map(associations, association -> {
- if (association.belongsToPackage(userId, packageName)
- && association.isLinkedTo(deviceAddress)) {
- association.setNotifyOnDeviceNearby(active);
- }
- return association;
- }), userId);
-
- restartBleScan();
+ association.setNotifyOnDeviceNearby(active);
+ mAssociationStore.updateAssociation(association);
}
@Override
@@ -664,14 +645,16 @@
enforceCallerIsSystemOr(userId, callingPackage);
checkState(!ArrayUtils.isEmpty(
- CompanionDeviceManagerService.this.getAssociations(userId, callingPackage)),
+ mAssociationStore.getAssociationsForPackage(userId, callingPackage)),
"App must have an association before calling this API");
checkUsesFeature(callingPackage, userId);
}
@Override
public boolean canPairWithoutPrompt(String packageName, String macAddress, int userId) {
- final AssociationInfo association = getAssociation(userId, packageName, macAddress);
+ final AssociationInfo association =
+ mAssociationStore.getAssociationsForPackageWithAddress(
+ userId, packageName, macAddress);
if (association == null) {
return false;
}
@@ -684,7 +667,8 @@
String[] args, ShellCallback callback, ResultReceiver resultReceiver)
throws RemoteException {
enforceCallerCanManageCompanionDevice(getContext(), "onShellCommand");
- new CompanionDeviceShellCommand(CompanionDeviceManagerService.this)
+ new CompanionDeviceShellCommand(
+ CompanionDeviceManagerService.this, mAssociationStore)
.exec(this, in, out, err, args, callback, resultReceiver);
}
@@ -697,13 +681,8 @@
}
fout.append("Companion Device Associations:").append('\n');
- synchronized (mLock) {
- for (UserInfo user : getAllUsers()) {
- forEach(mCachedAssociations.get(user.id), a -> {
- fout.append(" ").append(a.toString()).append('\n');
- });
- }
-
+ for (AssociationInfo a : mAssociationStore.getAssociations()) {
+ fout.append(" ").append(a.toString()).append('\n');
}
fout.append("Currently Connected Devices:").append('\n');
@@ -754,31 +733,56 @@
@Nullable String deviceProfile, boolean selfManaged) {
final int id = getNewAssociationIdForPackage(userId, packageName);
final long timestamp = System.currentTimeMillis();
+
final AssociationInfo association = new AssociationInfo(id, userId, packageName,
macAddress, displayName, deviceProfile, selfManaged, false, timestamp);
+ Slog.i(LOG_TAG, "New CDM association created=" + association);
+ mAssociationStore.addAssociation(association);
updateSpecialAccessPermissionForAssociatedPackage(association);
- recordAssociation(association, userId);
return association;
}
- @GuardedBy("mLock")
+ @NonNull
+ private Map<String, Set<Integer>> getPreviouslyUsedIdsForUser(@UserIdInt int userId) {
+ synchronized (mPreviouslyUsedIds) {
+ return getPreviouslyUsedIdsForUserLocked(userId);
+ }
+ }
+
+ @GuardedBy("mPreviouslyUsedIds")
+ @NonNull
+ private Map<String, Set<Integer>> getPreviouslyUsedIdsForUserLocked(@UserIdInt int userId) {
+ final Map<String, Set<Integer>> usedIdsForUser = mPreviouslyUsedIds.get(userId);
+ if (usedIdsForUser == null) {
+ return Collections.emptyMap();
+ }
+ return deepUnmodifiableCopy(usedIdsForUser);
+ }
+
+ @GuardedBy("mPreviouslyUsedIds")
@NonNull
private Set<Integer> getPreviouslyUsedIdsForPackageLocked(
@UserIdInt int userId, @NonNull String packageName) {
- final Set<Integer> previouslyUsedIds = mPreviouslyUsedIds.get(userId).get(packageName);
- if (previouslyUsedIds != null) return previouslyUsedIds;
- return emptySet();
+ // "Deeply unmodifiable" map: the map itself and the Set<Integer> values it contains are all
+ // unmodifiable.
+ final Map<String, Set<Integer>> usedIdsForUser = getPreviouslyUsedIdsForUserLocked(userId);
+ final Set<Integer> usedIdsForPackage = usedIdsForUser.get(packageName);
+
+ if (usedIdsForPackage == null) {
+ return Collections.emptySet();
+ }
+
+ //The set is already unmodifiable.
+ return usedIdsForPackage;
}
private int getNewAssociationIdForPackage(@UserIdInt int userId, @NonNull String packageName) {
- synchronized (mLock) {
- readPersistedStateForUserIfNeededLocked(userId);
-
+ synchronized (mPreviouslyUsedIds) {
// First: collect all IDs currently in use for this user's Associations.
final SparseBooleanArray usedIds = new SparseBooleanArray();
- for (AssociationInfo it : getAllAssociationsForUser(userId)) {
+ for (AssociationInfo it : mAssociationStore.getAssociationsForUser(userId)) {
usedIds.put(it.getId(), true);
}
@@ -804,41 +808,14 @@
}
}
- //TODO also revoke notification access
- void disassociateInternal(@UserIdInt int userId, int associationId) {
- updateAssociations(associations ->
- filterOut(associations, it -> {
- if (it.getId() != associationId) return false;
-
- onAssociationPreRemove(it);
- markIdAsPreviouslyUsedForPackage(
- it.getId(), it.getUserId(), it.getPackageName());
- return true;
- }), userId);
-
- restartBleScan();
+ //TODO: also revoke notification access
+ void disassociateInternal(int associationId) {
+ onAssociationPreRemove(associationId);
+ mAssociationStore.removeAssociation(associationId);
}
- void clearAssociationForPackage(@UserIdInt int userId, @NonNull String packageName) {
- if (DEBUG) Slog.d(LOG_TAG, "clearAssociationForPackage() u" + userId + "/" + packageName);
-
- mCompanionDevicePresenceController.unbindDevicePresenceListener(packageName, userId);
- updateAssociations(set -> filterOut(set, it -> it.belongsToPackage(userId, packageName)),
- userId);
- }
-
- private void markIdAsPreviouslyUsedForPackage(
- int associationId, @UserIdInt int userId, @NonNull String packageName) {
- synchronized (mLock) {
- // Mark as previously used.
- readPersistedStateForUserIfNeededLocked(userId);
- mPreviouslyUsedIds.get(userId)
- .computeIfAbsent(packageName, it -> new HashSet<>())
- .add(associationId);
- }
- }
-
- void onAssociationPreRemove(AssociationInfo association) {
+ void onAssociationPreRemove(int associationId) {
+ final AssociationInfo association = mAssociationStore.getAssociationById(associationId);
if (association.isNotifyOnDeviceNearby()
|| (association.isSelfManaged()
&& mPresentSelfManagedDevices.contains(association.getId()))) {
@@ -849,7 +826,7 @@
String deviceProfile = association.getDeviceProfile();
if (deviceProfile != null) {
AssociationInfo otherAssociationWithDeviceProfile = find(
- getAllAssociationsForUser(association.getUserId()),
+ mAssociationStore.getAssociationsForUser(association.getUserId()),
a -> !a.equals(association) && deviceProfile.equals(a.getDeviceProfile()));
if (otherAssociationWithDeviceProfile != null) {
Slog.i(LOG_TAG, "Not revoking " + deviceProfile
@@ -941,49 +918,7 @@
.getPackageInfoAsUser(packageName, flags , userId));
}
- private void recordAssociation(AssociationInfo association, int userId) {
- Slog.i(LOG_TAG, "recordAssociation(" + association + ")");
- updateAssociations(associations -> add(associations, association), userId);
- }
-
- private void updateAssociations(Function<Set<AssociationInfo>, Set<AssociationInfo>> update,
- int userId) {
- final List<AssociationInfo> associationList;
- synchronized (mLock) {
- if (DEBUG) Slog.d(LOG_TAG, "Updating Associations set...");
-
- final Set<AssociationInfo> prevAssociations = getAllAssociationsForUser(userId);
- if (DEBUG) Slog.d(LOG_TAG, " > Before : " + prevAssociations + "...");
-
- final Set<AssociationInfo> updatedAssociations = update.apply(
- new ArraySet<>(prevAssociations));
- if (DEBUG) Slog.d(LOG_TAG, " > After: " + updatedAssociations);
-
- associationList = new ArrayList<>(updatedAssociations);
-
- mCachedAssociations.put(userId, unmodifiableSet(updatedAssociations));
-
- BackgroundThread.getHandler().sendMessage(
- PooledLambda.obtainMessage(
- (associations, usedIds) ->
- mPersistentDataStore
- .persistStateForUser(userId, associations, usedIds),
- updatedAssociations, deepCopy(mPreviouslyUsedIds.get(userId))));
-
- updateAtm(userId, updatedAssociations);
- }
-
- mListeners.broadcast((listener, callbackUserId) -> {
- if ((int) callbackUserId == userId) {
- try {
- listener.onAssociationsChanged(associationList);
- } catch (RemoteException ignored) {
- }
- }
- });
- }
-
- private void updateAtm(int userId, Set<AssociationInfo> associations) {
+ private void updateAtm(int userId, List<AssociationInfo> associations) {
final Set<Integer> companionAppUids = new ArraySet<>();
for (AssociationInfo association : associations) {
final int uid = mPackageManagerInternal.getPackageUid(association.getPackageName(),
@@ -998,50 +933,18 @@
}
}
- @GuardedBy("mLock")
- private void readPersistedStateForUserIfNeededLocked(@UserIdInt int userId) {
- if (mCachedAssociations.get(userId) != null) return;
-
- Slog.i(LOG_TAG, "Reading state for user " + userId + " from the disk");
-
- final Set<AssociationInfo> associations = new ArraySet<>();
- final Map<String, Set<Integer>> previouslyUsedIds = new ArrayMap<>();
- mPersistentDataStore.readStateForUser(userId, associations, previouslyUsedIds);
-
- if (DEBUG) {
- Slog.d(LOG_TAG, " > associations=" + associations + "\n"
- + " > previouslyUsedIds=" + previouslyUsedIds);
- }
-
- mCachedAssociations.put(userId, unmodifiableSet(associations));
- mPreviouslyUsedIds.append(userId, previouslyUsedIds);
- }
-
- private List<UserInfo> getAllUsers() {
- final long identity = Binder.clearCallingIdentity();
- try {
- return mUserManager.getUsers();
- } finally {
- Binder.restoreCallingIdentity(identity);
- }
- }
-
void onDeviceConnected(String address) {
Slog.d(LOG_TAG, "onDeviceConnected(address = " + address + ")");
mCurrentlyConnectedDevices.add(address);
- for (UserInfo user : getAllUsers()) {
- for (AssociationInfo association : getAllAssociationsForUser(user.id)) {
- if (association.isLinkedTo(address)) {
- if (association.getDeviceProfile() != null) {
- Slog.i(LOG_TAG, "Granting role " + association.getDeviceProfile()
- + " to " + association.getPackageName()
- + " due to device connected: " + association.getDeviceMacAddress());
+ for (AssociationInfo association : mAssociationStore.getAssociationsByAddress(address)) {
+ if (association.getDeviceProfile() != null) {
+ Slog.i(LOG_TAG, "Granting role " + association.getDeviceProfile()
+ + " to " + association.getPackageName()
+ + " due to device connected: " + association.getDeviceMacAddress());
- addRoleHolderForAssociation(getContext(), association);
- }
- }
+ addRoleHolderForAssociation(getContext(), association);
}
}
@@ -1134,7 +1037,9 @@
Date lastNearby = mDevicesLastNearby.valueAt(i);
if (isDeviceDisappeared(lastNearby)) {
- for (AssociationInfo association : getAllAssociations(address)) {
+ final List<AssociationInfo> associations =
+ mAssociationStore.getAssociationsByAddress(address);
+ for (AssociationInfo association : associations) {
if (association.isNotifyOnDeviceNearby()) {
mCompanionDevicePresenceController.unbindDevicePresenceListener(
association.getPackageName(), association.getUserId());
@@ -1176,20 +1081,6 @@
}
}
- private Set<AssociationInfo> getAllAssociations(String deviceAddress) {
- List<UserInfo> aliveUsers = mUserManager.getAliveUsers();
- Set<AssociationInfo> result = new ArraySet<>();
- for (int i = 0, size = aliveUsers.size(); i < size; i++) {
- UserInfo user = aliveUsers.get(i);
- for (AssociationInfo association : getAllAssociationsForUser(user.id)) {
- if (association.isLinkedTo(deviceAddress)) {
- result.add(association);
- }
- }
- }
- return result;
- }
-
private void onDeviceNearby(String address) {
Date timestamp = new Date();
Date oldTimestamp = mDevicesLastNearby.put(address, timestamp);
@@ -1205,7 +1096,9 @@
|| timestamp.getTime() - oldTimestamp.getTime() >= DEVICE_DISAPPEARED_TIMEOUT_MS;
if (justAppeared) {
Slog.i(LOG_TAG, "onDeviceNearby(justAppeared, address = " + address + ")");
- for (AssociationInfo association : getAllAssociations(address)) {
+ final List<AssociationInfo> associations =
+ mAssociationStore.getAssociationsByAddress(address);
+ for (AssociationInfo association : associations) {
if (association.isNotifyOnDeviceNearby()) {
mCompanionDevicePresenceController.onDeviceNotifyAppeared(association,
getContext(), mMainHandler);
@@ -1218,7 +1111,9 @@
Slog.i(LOG_TAG, "onDeviceDisappeared(address = " + address + ")");
boolean hasDeviceListeners = false;
- for (AssociationInfo association : getAllAssociations(address)) {
+ final List<AssociationInfo> associations =
+ mAssociationStore.getAssociationsByAddress(address);
+ for (AssociationInfo association : associations) {
if (association.isNotifyOnDeviceNearby()) {
mCompanionDevicePresenceController.onDeviceNotifyDisappeared(
association, getContext(), mMainHandler);
@@ -1291,7 +1186,7 @@
private List<ScanFilter> getBleScanFilters() {
ArrayList<ScanFilter> result = new ArrayList<>();
ArraySet<String> addressesSeen = new ArraySet<>();
- for (AssociationInfo association : getAllAssociations()) {
+ for (AssociationInfo association : mAssociationStore.getAssociations()) {
if (association.isSelfManaged()) {
continue;
}
@@ -1331,17 +1226,6 @@
}
}
- private static @NonNull <T> Set<T> filterOut(
- @NonNull Set<T> set, @NonNull Predicate<? super T> predicate) {
- return CollectionUtils.filter(set, predicate.negate());
- }
-
- private Map<String, Set<Integer>> deepCopy(Map<String, Set<Integer>> orig) {
- final Map<String, Set<Integer>> copy = new HashMap<>(orig.size(), 1f);
- forEach(orig, (key, value) -> copy.put(key, new ArraySet<>(value)));
- return copy;
- }
-
void checkUsesFeature(@NonNull String pkg, @UserIdInt int userId) {
if (getCallingUid() == SYSTEM_UID) return;
@@ -1356,4 +1240,61 @@
+ FEATURE_COMPANION_DEVICE_SETUP
+ " in manifest to use this API");
}
+
+ private void registerPackageMonitor() {
+ new PackageMonitor() {
+ @Override
+ public void onPackageRemoved(String packageName, int uid) {
+ final int userId = getChangingUserId();
+ Slog.i(LOG_TAG, "onPackageRemoved() u" + userId + "/" + packageName);
+
+ clearAssociationForPackage(userId, packageName);
+ }
+
+ @Override
+ public void onPackageDataCleared(String packageName, int uid) {
+ final int userId = getChangingUserId();
+ Slog.i(LOG_TAG, "onPackageDataCleared() u" + userId + "/" + packageName);
+
+ clearAssociationForPackage(userId, packageName);
+ }
+
+ @Override
+ public void onPackageModified(String packageName) {
+ final int userId = getChangingUserId();
+ Slog.i(LOG_TAG, "onPackageModified() u" + userId + "/" + packageName);
+
+ final List<AssociationInfo> associationsForPackage =
+ mAssociationStore.getAssociationsForPackage(userId, packageName);
+ for (AssociationInfo association : associationsForPackage) {
+ updateSpecialAccessPermissionForAssociatedPackage(association);
+ }
+ }
+ }.register(getContext(), FgThread.get().getLooper(), UserHandle.ALL, true);
+ }
+
+ private void clearAssociationForPackage(@UserIdInt int userId, @NonNull String packageName) {
+ if (DEBUG) Slog.d(LOG_TAG, "clearAssociationForPackage() u" + userId + "/" + packageName);
+
+ // First, unbind CompanionService if needed.
+ mCompanionDevicePresenceController.unbindDevicePresenceListener(packageName, userId);
+
+ // Clear associations.
+ final List<AssociationInfo> associationsForPackage =
+ mAssociationStore.getAssociationsForPackage(userId, packageName);
+ for (AssociationInfo association : associationsForPackage) {
+ mAssociationStore.removeAssociation(association.getId());
+ }
+ }
+
+ private static Map<String, Set<Integer>> deepUnmodifiableCopy(Map<String, Set<Integer>> orig) {
+ final Map<String, Set<Integer>> copy = new HashMap<>();
+
+ for (Map.Entry<String, Set<Integer>> entry : orig.entrySet()) {
+ final Set<Integer> valueCopy = new HashSet<>(entry.getValue());
+ copy.put(entry.getKey(), Collections.unmodifiableSet(valueCopy));
+ }
+
+ return Collections.unmodifiableMap(copy);
+ }
}
diff --git a/services/companion/java/com/android/server/companion/CompanionDeviceShellCommand.java b/services/companion/java/com/android/server/companion/CompanionDeviceShellCommand.java
index 5cb3079..5c0571d 100644
--- a/services/companion/java/com/android/server/companion/CompanionDeviceShellCommand.java
+++ b/services/companion/java/com/android/server/companion/CompanionDeviceShellCommand.java
@@ -16,7 +16,6 @@
package com.android.server.companion;
-import static com.android.internal.util.CollectionUtils.forEach;
import static com.android.server.companion.CompanionDeviceManagerService.LOG_TAG;
import android.companion.AssociationInfo;
@@ -24,24 +23,33 @@
import android.util.Slog;
import java.io.PrintWriter;
+import java.util.List;
class CompanionDeviceShellCommand extends android.os.ShellCommand {
private final CompanionDeviceManagerService mService;
+ private final AssociationStore mAssociationStore;
- CompanionDeviceShellCommand(CompanionDeviceManagerService service) {
+ CompanionDeviceShellCommand(CompanionDeviceManagerService service,
+ AssociationStore associationStore) {
mService = service;
+ mAssociationStore = associationStore;
}
@Override
public int onCommand(String cmd) {
+ final PrintWriter out = getOutPrintWriter();
try {
switch (cmd) {
case "list": {
- forEach(
- mService.getAllAssociationsForUser(getNextArgInt()),
- a -> getOutPrintWriter()
- .println(a.getPackageName() + " "
- + a.getDeviceMacAddress()));
+ final int userId = getNextArgInt();
+ final List<AssociationInfo> associationsForUser =
+ mAssociationStore.getAssociationsForUser(userId);
+ for (AssociationInfo association : associationsForUser) {
+ // TODO(b/212535524): use AssociationInfo.toShortString(), once it's not
+ // longer referenced in tests.
+ out.println(association.getPackageName() + " "
+ + association.getDeviceMacAddress());
+ }
}
break;
@@ -60,7 +68,7 @@
final AssociationInfo association =
mService.getAssociationWithCallerChecks(userId, packageName, address);
if (association != null) {
- mService.disassociateInternal(userId, association.getId());
+ mService.disassociateInternal(association.getId());
}
}
break;
diff --git a/services/companion/java/com/android/server/companion/PersistentDataStore.java b/services/companion/java/com/android/server/companion/PersistentDataStore.java
index 87558df..e2a814b 100644
--- a/services/companion/java/com/android/server/companion/PersistentDataStore.java
+++ b/services/companion/java/com/android/server/companion/PersistentDataStore.java
@@ -33,11 +33,14 @@
import android.annotation.Nullable;
import android.annotation.UserIdInt;
import android.companion.AssociationInfo;
+import android.content.pm.UserInfo;
import android.net.MacAddress;
import android.os.Environment;
+import android.util.ArrayMap;
import android.util.AtomicFile;
import android.util.ExceptionUtils;
import android.util.Slog;
+import android.util.SparseArray;
import android.util.TypedXmlPullParser;
import android.util.TypedXmlSerializer;
import android.util.Xml;
@@ -51,7 +54,9 @@
import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
+import java.util.Collection;
import java.util.HashSet;
+import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.ConcurrentHashMap;
@@ -68,8 +73,8 @@
*
* Before Android T the data was stored using the v0 schema.
*
- * @see #readAssociationsV0(TypedXmlPullParser, int, Set)
- * @see #readAssociationV0(TypedXmlPullParser, int, int, Set)
+ * @see #readAssociationsV0(TypedXmlPullParser, int, Collection)
+ * @see #readAssociationV0(TypedXmlPullParser, int, int, Collection)
*
* The following snippet is a sample of a the file that is using v0 schema.
* <pre>{@code
@@ -100,8 +105,8 @@
* optional.
*
* @see #CURRENT_PERSISTENCE_VERSION
- * @see #readAssociationsV1(TypedXmlPullParser, int, Set)
- * @see #readAssociationV1(TypedXmlPullParser, int, Set)
+ * @see #readAssociationsV1(TypedXmlPullParser, int, Collection)
+ * @see #readAssociationV1(TypedXmlPullParser, int, Collection)
* @see #readPreviouslyUsedIdsV1(TypedXmlPullParser, Map)
*
* The following snippet is a sample of a the file that is using v0 schema.
@@ -168,6 +173,23 @@
private final @NonNull ConcurrentMap<Integer, AtomicFile> mUserIdToStorageFile =
new ConcurrentHashMap<>();
+ void readStateForUsers(@NonNull List<UserInfo> users,
+ @NonNull Set<AssociationInfo> allAssociationsOut,
+ @NonNull SparseArray<Map<String, Set<Integer>>> previouslyUsedIdsPerUserOut) {
+ for (UserInfo user : users) {
+ final int userId = user.id;
+ // Previously used IDs are stored in the "out" collection per-user.
+ final Map<String, Set<Integer>> previouslyUsedIds = new ArrayMap<>();
+
+ // Associations for all users are stored in a single "flat" set: so we read directly
+ // into it.
+ readStateForUser(userId, allAssociationsOut, previouslyUsedIds);
+
+ // Save previously used IDs for this user into the "out" structure.
+ previouslyUsedIdsPerUserOut.append(userId, previouslyUsedIds);
+ }
+ }
+
/**
* Reads previously persisted data for the given user "into" the provided containers.
*
@@ -176,7 +198,7 @@
* @param previouslyUsedIdsPerPackageOut a container to read the used IDs "into".
*/
void readStateForUser(@UserIdInt int userId,
- @NonNull Set<AssociationInfo> associationsOut,
+ @NonNull Collection<AssociationInfo> associationsOut,
@NonNull Map<String, Set<Integer>> previouslyUsedIdsPerPackageOut) {
Slog.i(LOG_TAG, "Reading associations for user " + userId + " from disk");
final AtomicFile file = getStorageFileForUser(userId);
@@ -237,7 +259,8 @@
* @param associations a set of user's associations.
* @param previouslyUsedIdsPerPackage a set previously used Association IDs for the user.
*/
- void persistStateForUser(@UserIdInt int userId, @NonNull Set<AssociationInfo> associations,
+ void persistStateForUser(@UserIdInt int userId,
+ @NonNull Collection<AssociationInfo> associations,
@NonNull Map<String, Set<Integer>> previouslyUsedIdsPerPackage) {
Slog.i(LOG_TAG, "Writing associations for user " + userId + " to disk");
if (DEBUG) Slog.d(LOG_TAG, " > " + associations);
@@ -250,7 +273,7 @@
}
private int readStateFromFileLocked(@UserIdInt int userId, @NonNull AtomicFile file,
- @NonNull String rootTag, @Nullable Set<AssociationInfo> associationsOut,
+ @NonNull String rootTag, @Nullable Collection<AssociationInfo> associationsOut,
@NonNull Map<String, Set<Integer>> previouslyUsedIdsPerPackageOut) {
try (FileInputStream in = file.openRead()) {
final TypedXmlPullParser parser = Xml.resolvePullParser(in);
@@ -282,7 +305,7 @@
}
private void persistStateToFileLocked(@NonNull AtomicFile file,
- @Nullable Set<AssociationInfo> associations,
+ @Nullable Collection<AssociationInfo> associations,
@NonNull Map<String, Set<Integer>> previouslyUsedIdsPerPackage) {
file.write(out -> {
try {
@@ -321,7 +344,7 @@
}
private static void readAssociationsV0(@NonNull TypedXmlPullParser parser,
- @UserIdInt int userId, @NonNull Set<AssociationInfo> out)
+ @UserIdInt int userId, @NonNull Collection<AssociationInfo> out)
throws XmlPullParserException, IOException {
requireStartOfTag(parser, XML_TAG_ASSOCIATIONS);
@@ -342,7 +365,8 @@
}
private static void readAssociationV0(@NonNull TypedXmlPullParser parser, @UserIdInt int userId,
- int associationId, @NonNull Set<AssociationInfo> out) throws XmlPullParserException {
+ int associationId, @NonNull Collection<AssociationInfo> out)
+ throws XmlPullParserException {
requireStartOfTag(parser, XML_TAG_ASSOCIATION);
final String appPackage = readStringAttribute(parser, XML_ATTR_PACKAGE);
@@ -360,7 +384,7 @@
}
private static void readAssociationsV1(@NonNull TypedXmlPullParser parser,
- @UserIdInt int userId, @NonNull Set<AssociationInfo> out)
+ @UserIdInt int userId, @NonNull Collection<AssociationInfo> out)
throws XmlPullParserException, IOException {
requireStartOfTag(parser, XML_TAG_ASSOCIATIONS);
@@ -374,7 +398,7 @@
}
private static void readAssociationV1(@NonNull TypedXmlPullParser parser, @UserIdInt int userId,
- @NonNull Set<AssociationInfo> out) throws XmlPullParserException, IOException {
+ @NonNull Collection<AssociationInfo> out) throws XmlPullParserException, IOException {
requireStartOfTag(parser, XML_TAG_ASSOCIATION);
final int associationId = readIntAttribute(parser, XML_ATTR_ID);
@@ -421,9 +445,11 @@
}
private static void writeAssociations(@NonNull XmlSerializer parent,
- @Nullable Set<AssociationInfo> associations) throws IOException {
+ @Nullable Collection<AssociationInfo> associations) throws IOException {
final XmlSerializer serializer = parent.startTag(null, XML_TAG_ASSOCIATIONS);
- forEach(associations, it -> writeAssociation(serializer, it));
+ for (AssociationInfo association : associations) {
+ writeAssociation(serializer, association);
+ }
serializer.endTag(null, XML_TAG_ASSOCIATIONS);
}
diff --git a/services/core/java/android/content/pm/PackageManagerInternal.java b/services/core/java/android/content/pm/PackageManagerInternal.java
index 6fe2806..9b2948f 100644
--- a/services/core/java/android/content/pm/PackageManagerInternal.java
+++ b/services/core/java/android/content/pm/PackageManagerInternal.java
@@ -50,6 +50,7 @@
import com.android.server.pm.pkg.AndroidPackageApi;
import com.android.server.pm.pkg.PackageState;
import com.android.server.pm.pkg.PackageStateInternal;
+import com.android.server.pm.pkg.mutate.PackageStateMutator;
import java.io.IOException;
import java.lang.annotation.Retention;
@@ -885,17 +886,6 @@
public abstract boolean userNeedsBadging(int userId);
/**
- * Perform the given action for each package.
- * Note that packages lock will be held while performing the actions.
- *
- * If the caller does not need all packages, prefer the potentially non-locking
- * {@link #withPackageSettingsSnapshot(Consumer)}.
- *
- * @param actionLocked action to be performed
- */
- public abstract void forEachPackage(Consumer<AndroidPackage> actionLocked);
-
- /**
* Perform the given action for each {@link PackageSetting}.
* Note that packages lock will be held while performing the actions.
*
@@ -918,12 +908,24 @@
public abstract void forEachPackageState(boolean locked, Consumer<PackageStateInternal> action);
/**
+ * {@link #forEachPackageState(boolean, Consumer)} but filtered to only states with packages
+ * on device where {@link PackageStateInternal#getPkg()} is not null.
+ */
+ public abstract void forEachPackage(Consumer<AndroidPackage> action);
+
+ /**
* Perform the given action for each installed package for a user.
* Note that packages lock will be held while performing the actions.
*/
public abstract void forEachInstalledPackage(
@NonNull Consumer<AndroidPackage> actionLocked, @UserIdInt int userId);
+ /**
+ * Perform the given action for each installed package for a user.
+ */
+ public abstract void forEachInstalledPackage(boolean locked,
+ @NonNull Consumer<AndroidPackage> action, @UserIdInt int userId);
+
/** Returns the list of enabled components */
public abstract ArraySet<String> getEnabledComponents(String packageName, int userId);
@@ -1265,4 +1267,62 @@
*/
public abstract void reconcileAppsData(int userId, @StorageManager.StorageFlags int flags,
boolean migrateAppsData);
+
+ /**
+ * Initiates a package state mutation request, returning the current state as known by
+ * PackageManager. This allows the later commit request to compare the initial values and
+ * determine if any state was changed or any packages were updated since the whole request
+ * was initiated.
+ *
+ * As a concrete example, consider the following steps:
+ * <ol>
+ * <li>Read a package state without taking a lock</li>
+ * <li>Check some values in that state, determine that a mutation needs to occur</li>
+ * <li>Call to commit the change with the new value, takes lock</li>
+ * </ol>
+ *
+ * Between steps 1 and 3, because the lock was not taken for the entire flow, it's possible
+ * a package state was changed by another consumer or a package was updated/installed.
+ *
+ * If anything has changed,
+ * {@link #commitPackageStateMutation(PackageStateMutator.InitialState, Consumer)} will return
+ * a {@link PackageStateMutator.Result} indicating so. If the caller has not indicated it can
+ * ignore changes, it can opt to re-run the commit logic from the top with a true write lock
+ * around all of its read-logic-commit loop.
+ *
+ * Note that if the caller does not care about potential race conditions or package/state
+ * changes between steps 1 and 3, it can simply opt to not call this method and pass in null
+ * for the initial state. This is useful to avoid long running data structure locks when the
+ * caller is changing a value as part of a one-off request. Perhaps from an app side API which
+ * mutates only a single package, where it doesn't care what the state of that package is or
+ * any other packages on the devices.
+ *
+ * Important to note is that if no locking is enforced, callers themselves will not be
+ * synchronized with themselves. The caller may be relying on the PackageManager lock to
+ * enforce ordering within their own code path, and that has to be adjusted if migrated off
+ * the lock.
+ */
+ @NonNull
+ public abstract PackageStateMutator.InitialState recordInitialState();
+
+ /**
+ * Some questions to ask when designing a mutation:
+ * <ol>
+ * <li>What external system state is required and is it synchronized properly?</li>
+ * <li>Are there any package/state changes that could happen to the target (or another)
+ * package that could result in the commit being invalid?</li>
+ * <li>Is the caller synchronized with itself and can handle multiple mutations being
+ * requested from different threads?</li>
+ * <li>What should be done in case of a conflict and the commit can't be finished?</li>
+ * </ol>
+ *
+ * @param state See {@link #recordInitialState()}. If null, no result is returned.
+ * @param consumer Lean wrapper around just the logic that changes state values
+ * @return result if anything changed since initial state, or null if nothing changed and
+ * commit was successful
+ */
+ @Nullable
+ public abstract PackageStateMutator.Result commitPackageStateMutation(
+ @Nullable PackageStateMutator.InitialState state,
+ @NonNull Consumer<PackageStateMutator> consumer);
}
diff --git a/services/core/java/com/android/server/BluetoothManagerService.java b/services/core/java/com/android/server/BluetoothManagerService.java
index 6e058412..bc8da84 100644
--- a/services/core/java/com/android/server/BluetoothManagerService.java
+++ b/services/core/java/com/android/server/BluetoothManagerService.java
@@ -109,10 +109,6 @@
private static final String BLUETOOTH_PRIVILEGED =
android.Manifest.permission.BLUETOOTH_PRIVILEGED;
- private static final String SECURE_SETTINGS_BLUETOOTH_ADDR_VALID = "bluetooth_addr_valid";
- private static final String SECURE_SETTINGS_BLUETOOTH_ADDRESS = "bluetooth_address";
- private static final String SECURE_SETTINGS_BLUETOOTH_NAME = "bluetooth_name";
-
private static final int ACTIVE_LOG_MAX_SIZE = 20;
private static final int CRASH_LOG_MAX_SIZE = 100;
@@ -640,7 +636,7 @@
if (mContext.getResources()
.getBoolean(com.android.internal.R.bool.config_bluetooth_address_validation)
&& Settings.Secure.getIntForUser(mContentResolver,
- SECURE_SETTINGS_BLUETOOTH_ADDR_VALID, 0, mUserId)
+ Settings.Secure.BLUETOOTH_NAME, 0, mUserId)
== 0) {
// if the valid flag is not set, don't load the address and name
if (DBG) {
@@ -649,9 +645,9 @@
return;
}
mName = Settings.Secure.getStringForUser(
- mContentResolver, SECURE_SETTINGS_BLUETOOTH_NAME, mUserId);
+ mContentResolver, Settings.Secure.BLUETOOTH_NAME, mUserId);
mAddress = Settings.Secure.getStringForUser(
- mContentResolver, SECURE_SETTINGS_BLUETOOTH_ADDRESS, mUserId);
+ mContentResolver, Settings.Secure.BLUETOOTH_ADDRESS, mUserId);
if (DBG) {
Slog.d(TAG, "Stored bluetooth Name=" + mName + ",Address=" + mAddress);
}
@@ -665,30 +661,30 @@
*/
private void storeNameAndAddress(String name, String address) {
if (name != null) {
- Settings.Secure.putStringForUser(mContentResolver, SECURE_SETTINGS_BLUETOOTH_NAME, name,
+ Settings.Secure.putStringForUser(mContentResolver, Settings.Secure.BLUETOOTH_NAME, name,
mUserId);
mName = name;
if (DBG) {
Slog.d(TAG, "Stored Bluetooth name: " + Settings.Secure.getStringForUser(
- mContentResolver, SECURE_SETTINGS_BLUETOOTH_NAME,
+ mContentResolver, Settings.Secure.BLUETOOTH_NAME,
mUserId));
}
}
if (address != null) {
- Settings.Secure.putStringForUser(mContentResolver, SECURE_SETTINGS_BLUETOOTH_ADDRESS,
+ Settings.Secure.putStringForUser(mContentResolver, Settings.Secure.BLUETOOTH_ADDRESS,
address, mUserId);
mAddress = address;
if (DBG) {
Slog.d(TAG,
"Stored Bluetoothaddress: " + Settings.Secure.getStringForUser(
- mContentResolver, SECURE_SETTINGS_BLUETOOTH_ADDRESS,
+ mContentResolver, Settings.Secure.BLUETOOTH_ADDRESS,
mUserId));
}
}
if ((name != null) && (address != null)) {
- Settings.Secure.putIntForUser(mContentResolver, SECURE_SETTINGS_BLUETOOTH_ADDR_VALID, 1,
+ Settings.Secure.putIntForUser(mContentResolver, Settings.Secure.BLUETOOTH_ADDR_VALID, 1,
mUserId);
}
}
diff --git a/services/core/java/com/android/server/inputmethod/InputMethodManagerService.java b/services/core/java/com/android/server/inputmethod/InputMethodManagerService.java
index 4a3aa25..34ea86b 100644
--- a/services/core/java/com/android/server/inputmethod/InputMethodManagerService.java
+++ b/services/core/java/com/android/server/inputmethod/InputMethodManagerService.java
@@ -5725,7 +5725,7 @@
final PrintWriter pw = shellCommand.getOutPrintWriter();
switch (cmd) {
case "start":
- ImeTracing.getInstance().getInstance().startTrace(pw);
+ ImeTracing.getInstance().startTrace(pw);
break; // proceed to the next step to update the IME client processes.
case "stop":
ImeTracing.getInstance().stopTrace(pw);
diff --git a/services/core/java/com/android/server/net/NetworkPolicyManagerService.java b/services/core/java/com/android/server/net/NetworkPolicyManagerService.java
index 0a47cbb..65c5b88 100644
--- a/services/core/java/com/android/server/net/NetworkPolicyManagerService.java
+++ b/services/core/java/com/android/server/net/NetworkPolicyManagerService.java
@@ -2005,7 +2005,7 @@
for (final NetworkStateSnapshot snapshot : snapshots) {
mNetIdToSubId.put(snapshot.getNetwork().getNetId(), parseSubId(snapshot));
- // Policies matched by NPMS only match by subscriber ID or by ssid. Thus subtype
+ // Policies matched by NPMS only match by subscriber ID or by network ID. Thus subtype
// in the object created here is never used and its value doesn't matter, so use
// NETWORK_TYPE_UNKNOWN.
final NetworkIdentity ident = NetworkIdentity.buildNetworkIdentity(mContext, snapshot,
@@ -2435,7 +2435,6 @@
}
final NetworkTemplate.Builder builder =
new NetworkTemplate.Builder(templateType)
- .setWifiNetworkKey(networkId)
.setMeteredness(templateMeteredness);
if (subscriberIdMatchRule
== NetworkStatsUtils.SUBSCRIBER_ID_MATCH_RULE_EXACT) {
@@ -2443,6 +2442,9 @@
ids.add(subscriberId);
builder.setSubscriberIds(ids);
}
+ if (networkId != null) {
+ builder.setWifiNetworkKeys(Set.of(networkId));
+ }
final NetworkTemplate template = builder.build();
if (NetworkPolicy.isTemplatePersistable(template)) {
mNetworkPolicy.put(template, new NetworkPolicy(template, cycleRule,
@@ -2593,35 +2595,39 @@
* into {@link WifiConfiguration}.
*/
private void upgradeWifiMeteredOverride() {
- final ArrayMap<String, Boolean> wifiNetworkIds = new ArrayMap<>();
+ final ArrayMap<String, Boolean> wifiNetworkKeys = new ArrayMap<>();
synchronized (mNetworkPoliciesSecondLock) {
for (int i = 0; i < mNetworkPolicy.size();) {
final NetworkPolicy policy = mNetworkPolicy.valueAt(i);
if (policy.template.getMatchRule() == NetworkTemplate.MATCH_WIFI
&& !policy.inferred) {
mNetworkPolicy.removeAt(i);
- wifiNetworkIds.put(policy.template.getNetworkId(), policy.metered);
+ final Set<String> keys = policy.template.getWifiNetworkKeys();
+ wifiNetworkKeys.put(keys.isEmpty() ? null : keys.iterator().next(),
+ policy.metered);
} else {
i++;
}
}
}
- if (wifiNetworkIds.isEmpty()) {
+ if (wifiNetworkKeys.isEmpty()) {
return;
}
final WifiManager wm = mContext.getSystemService(WifiManager.class);
final List<WifiConfiguration> configs = wm.getConfiguredNetworks();
for (int i = 0; i < configs.size(); ++i) {
final WifiConfiguration config = configs.get(i);
- final String networkId = resolveNetworkId(config);
- final Boolean metered = wifiNetworkIds.get(networkId);
- if (metered != null) {
- Slog.d(TAG, "Found network " + networkId + "; upgrading metered hint");
- config.meteredOverride = metered
- ? WifiConfiguration.METERED_OVERRIDE_METERED
- : WifiConfiguration.METERED_OVERRIDE_NOT_METERED;
- wm.updateNetwork(config);
+ for (String key : config.getAllPersistableNetworkKeys()) {
+ final Boolean metered = wifiNetworkKeys.get(key);
+ if (metered != null) {
+ Slog.d(TAG, "Found network " + key + "; upgrading metered hint");
+ config.meteredOverride = metered
+ ? WifiConfiguration.METERED_OVERRIDE_METERED
+ : WifiConfiguration.METERED_OVERRIDE_NOT_METERED;
+ wm.updateNetwork(config);
+ break;
+ }
}
}
@@ -2663,9 +2669,9 @@
? NetworkStatsUtils.SUBSCRIBER_ID_MATCH_RULE_ALL
: NetworkStatsUtils.SUBSCRIBER_ID_MATCH_RULE_EXACT;
writeIntAttribute(out, ATTR_SUBSCRIBER_ID_MATCH_RULE, subscriberIdMatchRule);
- final String networkId = template.getNetworkId();
- if (networkId != null) {
- out.attribute(null, ATTR_NETWORK_ID, networkId);
+ if (!template.getWifiNetworkKeys().isEmpty()) {
+ out.attribute(null, ATTR_NETWORK_ID,
+ template.getWifiNetworkKeys().iterator().next());
}
writeIntAttribute(out, ATTR_TEMPLATE_METERED,
template.getMeteredness());
diff --git a/services/core/java/com/android/server/pm/DeletePackageHelper.java b/services/core/java/com/android/server/pm/DeletePackageHelper.java
index e84d990..c26f3b3 100644
--- a/services/core/java/com/android/server/pm/DeletePackageHelper.java
+++ b/services/core/java/com/android/server/pm/DeletePackageHelper.java
@@ -547,7 +547,6 @@
true /*notLaunched*/,
false /*hidden*/,
0 /*distractionFlags*/,
- false /*suspended*/,
null /*suspendParams*/,
false /*instantApp*/,
false /*virtualPreload*/,
diff --git a/services/core/java/com/android/server/pm/InstallPackageHelper.java b/services/core/java/com/android/server/pm/InstallPackageHelper.java
index 339a951..c595b97 100644
--- a/services/core/java/com/android/server/pm/InstallPackageHelper.java
+++ b/services/core/java/com/android/server/pm/InstallPackageHelper.java
@@ -857,7 +857,7 @@
mPm.notifyInstallObserver(request.mInstallResult, request.mArgs.mObserver);
}
- @GuardedBy("mInstallLock")
+ @GuardedBy("mPm.mInstallLock")
private void installPackagesTracedLI(List<InstallRequest> requests) {
try {
Trace.traceBegin(TRACE_TAG_PACKAGE_MANAGER, "installPackages");
@@ -886,7 +886,7 @@
*
* Failure at any phase will result in a full failure to install all packages.
*/
- @GuardedBy("mInstallLock")
+ @GuardedBy("mPm.mInstallLock")
private void installPackagesLI(List<InstallRequest> requests) {
final Map<String, ScanResult> preparedScans = new ArrayMap<>(requests.size());
final Map<String, InstallArgs> installArgs = new ArrayMap<>(requests.size());
@@ -1038,7 +1038,7 @@
}
}
- @GuardedBy("mInstallLock")
+ @GuardedBy("mPm.mInstallLock")
private PrepareResult preparePackageLI(InstallArgs args, PackageInstalledInfo res)
throws PrepareFailure {
final int installFlags = args.mInstallFlags;
@@ -1882,7 +1882,7 @@
}
}
- @GuardedBy("mLock")
+ @GuardedBy("mPm.mLock")
private void commitPackagesLocked(final CommitRequest request) {
// TODO: remove any expected failures from this method; this should only be able to fail due
// to unavoidable errors (I/O, etc.)
@@ -1999,7 +1999,7 @@
ApplicationPackageManager.invalidateGetPackagesForUidCache();
}
- @GuardedBy("mLock")
+ @GuardedBy("mPm.mLock")
private boolean disableSystemPackageLPw(AndroidPackage oldPkg) {
return mPm.mSettings.disableSystemPackageLPw(oldPkg.getPackageName(), true);
}
diff --git a/services/core/java/com/android/server/pm/PackageInstallerService.java b/services/core/java/com/android/server/pm/PackageInstallerService.java
index 23bc511..f474044 100644
--- a/services/core/java/com/android/server/pm/PackageInstallerService.java
+++ b/services/core/java/com/android/server/pm/PackageInstallerService.java
@@ -135,7 +135,7 @@
/** Automatically destroy sessions older than this */
private static final long MAX_AGE_MILLIS = 3 * DateUtils.DAY_IN_MILLIS;
/** Automatically destroy staged sessions that have not changed state in this time */
- private static final long MAX_TIME_SINCE_UPDATE_MILLIS = 21 * DateUtils.DAY_IN_MILLIS;
+ private static final long MAX_TIME_SINCE_UPDATE_MILLIS = 7 * DateUtils.DAY_IN_MILLIS;
/** Upper bound on number of active sessions for a UID that has INSTALL_PACKAGES */
private static final long MAX_ACTIVE_SESSIONS_WITH_PERMISSION = 1024;
/** Upper bound on number of active sessions for a UID without INSTALL_PACKAGES */
diff --git a/services/core/java/com/android/server/pm/PackageManagerService.java b/services/core/java/com/android/server/pm/PackageManagerService.java
index b2cd642..248944e 100644
--- a/services/core/java/com/android/server/pm/PackageManagerService.java
+++ b/services/core/java/com/android/server/pm/PackageManagerService.java
@@ -239,6 +239,9 @@
import com.android.server.pm.pkg.PackageUserState;
import com.android.server.pm.pkg.PackageUserStateInternal;
import com.android.server.pm.pkg.SuspendParams;
+import com.android.server.pm.pkg.mutate.PackageStateMutator;
+import com.android.server.pm.pkg.mutate.PackageStateWrite;
+import com.android.server.pm.pkg.mutate.PackageUserStateWrite;
import com.android.server.pm.verify.domain.DomainVerificationManagerInternal;
import com.android.server.pm.verify.domain.DomainVerificationService;
import com.android.server.pm.verify.domain.proxy.DomainVerificationProxy;
@@ -594,6 +597,16 @@
// the suffix "Locked". Some methods may use the legacy suffix "LP"
final PackageManagerTracedLock mLock;
+ // Lock alias for doing package state mutation
+ private final PackageManagerTracedLock mPackageStateWriteLock;
+
+ // Lock alias to track syncing a consistent Computer
+ private final PackageManagerTracedLock mLiveComputerSyncLock;
+
+ private final PackageStateMutator mPackageStateMutator = new PackageStateMutator(
+ this::getPackageSettingForMutation,
+ this::getDisabledPackageSettingForMutation);
+
// Keys are String (package name), values are Package.
@Watched
@GuardedBy("mLock")
@@ -1608,6 +1621,8 @@
mInstaller = injector.getInstaller();
mInstallLock = injector.getInstallLock();
mLock = injector.getLock();
+ mPackageStateWriteLock = mLock;
+ mLiveComputerSyncLock = mLock;
mPermissionManager = injector.getPermissionManagerServiceInternal();
mSettings = injector.getSettings();
mUserManager = injector.getUserManagerService();
@@ -1713,6 +1728,8 @@
mInjector.bootstrap(this);
mLock = injector.getLock();
+ mPackageStateWriteLock = mLock;
+ mLiveComputerSyncLock = mLock;
mInstallLock = injector.getInstallLock();
LockGuard.installLock(mLock, LockGuard.INDEX_PACKAGES);
EventLog.writeEvent(EventLogTags.BOOT_PROGRESS_PMS_START,
@@ -4553,8 +4570,8 @@
if (pus.isSuspended()) {
for (int i = 0; i < pus.getSuspendParams().size(); i++) {
final SuspendParams params = pus.getSuspendParams().valueAt(i);
- if (params != null && params.appExtras != null) {
- allExtras.putAll(params.appExtras);
+ if (params != null && params.getAppExtras() != null) {
+ allExtras.putAll(params.getAppExtras());
}
}
}
@@ -4627,9 +4644,9 @@
synchronized (mLock) {
for (String packageName : packagesToChange) {
final PackageSetting ps = mSettings.getPackageLPr(packageName);
- if (ps != null && ps.getSuspended(userId)) {
+ if (ps != null && ps.getUserStateOrDefault(userId).isSuspended()) {
ps.removeSuspension(suspendingPackagePredicate, userId);
- if (!ps.getSuspended(userId)) {
+ if (!ps.getUserStateOrDefault(userId).isSuspended()) {
unsuspendedPackages.add(ps.getPackageName());
unsuspendedUids.add(UserHandle.getUid(userId, ps.getAppId()));
}
@@ -7413,8 +7430,8 @@
if (userState.isSuspended()) {
for (int i = 0; i < userState.getSuspendParams().size(); i++) {
final SuspendParams params = userState.getSuspendParams().valueAt(i);
- if (params != null && params.launcherExtras != null) {
- allExtras.putAll(params.launcherExtras);
+ if (params != null && params.getLauncherExtras() != null) {
+ allExtras.putAll(params.getLauncherExtras());
}
}
}
@@ -7503,7 +7520,7 @@
}
final SuspendParams suspendParams = suspendParamsMap.get(suspendingPackage);
- return (suspendParams != null) ? suspendParams.dialogInfo : null;
+ return (suspendParams != null) ? suspendParams.getDialogInfo() : null;
}
@Override
@@ -7949,7 +7966,13 @@
@Override
public void forEachInstalledPackage(@NonNull Consumer<AndroidPackage> actionLocked,
@UserIdInt int userId) {
- PackageManagerService.this.forEachInstalledPackage(actionLocked, userId);
+ forEachInstalledPackage(true, actionLocked, userId);
+ }
+
+ @Override
+ public void forEachInstalledPackage(boolean locked,
+ @NonNull Consumer<AndroidPackage> action, int userId) {
+ PackageManagerService.this.forEachInstalledPackage(locked, action, userId);
}
@Override
@@ -8222,51 +8245,24 @@
@Override
public void withPackageSettingsSnapshot(
@NonNull Consumer<Function<String, PackageStateInternal>> block) {
- final Computer snapshot = snapshotComputer();
-
- // This method needs to either lock or not lock consistently throughout the method,
- // so if the live computer is returned, force a wrapping sync block.
- if (snapshot == mLiveComputer) {
- synchronized (mLock) {
- block.accept(snapshot::getPackageStateInternal);
- }
- } else {
- block.accept(snapshot::getPackageStateInternal);
- }
+ executeWithConsistentComputer(computer ->
+ block.accept(computer::getPackageStateInternal));
}
@Override
public <Output> Output withPackageSettingsSnapshotReturning(
@NonNull FunctionalUtils.ThrowingFunction<Function<String, PackageStateInternal>,
Output> block) {
- final Computer snapshot = snapshotComputer();
-
- // This method needs to either lock or not lock consistently throughout the method,
- // so if the live computer is returned, force a wrapping sync block.
- if (snapshot == mLiveComputer) {
- synchronized (mLock) {
- return block.apply(snapshot::getPackageStateInternal);
- }
- } else {
- return block.apply(snapshot::getPackageStateInternal);
- }
+ return executeWithConsistentComputerReturning(computer ->
+ block.apply(computer::getPackageStateInternal));
}
@Override
public <ExceptionType extends Exception> void withPackageSettingsSnapshotThrowing(
@NonNull FunctionalUtils.ThrowingCheckedConsumer<Function<String,
PackageStateInternal>, ExceptionType> block) throws ExceptionType {
- final Computer snapshot = snapshotComputer();
-
- // This method needs to either lock or not lock consistently throughout the method,
- // so if the live computer is returned, force a wrapping sync block.
- if (snapshot == mLiveComputer) {
- synchronized (mLock) {
- block.accept(snapshot::getPackageStateInternal);
- }
- } else {
- block.accept(snapshot::getPackageStateInternal);
- }
+ executeWithConsistentComputerThrowing(computer ->
+ block.accept(computer::getPackageStateInternal));
}
@Override
@@ -8276,17 +8272,9 @@
Function<String, PackageStateInternal>, ExceptionOne,
ExceptionTwo> block)
throws ExceptionOne, ExceptionTwo {
- final Computer snapshot = snapshotComputer();
-
- // This method needs to either lock or not lock consistently throughout the method,
- // so if the live computer is returned, force a wrapping sync block.
- if (snapshot == mLiveComputer) {
- synchronized (mLock) {
- block.accept(snapshot::getPackageStateInternal);
- }
- } else {
- block.accept(snapshot::getPackageStateInternal);
- }
+ executeWithConsistentComputerThrowing2(
+ (FunctionalUtils.ThrowingChecked2Consumer<Computer, ExceptionOne,
+ ExceptionTwo>) computer -> block.accept(computer::getPackageStateInternal));
}
@Override
@@ -8296,17 +8284,8 @@
Function<String, PackageStateInternal>, Output,
ExceptionType> block)
throws ExceptionType {
- final Computer snapshot = snapshotComputer();
-
- // This method needs to either lock or not lock consistently throughout the method,
- // so if the live computer is returned, force a wrapping sync block.
- if (snapshot == mLiveComputer) {
- synchronized (mLock) {
- return block.apply(snapshot::getPackageStateInternal);
- }
- } else {
- return block.apply(snapshot::getPackageStateInternal);
- }
+ return executeWithConsistentComputerReturningThrowing(computer ->
+ block.apply(computer::getPackageStateInternal));
}
@Override
@@ -8315,6 +8294,20 @@
PackageManagerService.this.mAppDataHelper.reconcileAppsData(userId, flags,
migrateAppsData);
}
+
+ @NonNull
+ @Override
+ public PackageStateMutator.InitialState recordInitialState() {
+ return PackageManagerService.this.recordInitialState();
+ }
+
+ @Nullable
+ @Override
+ public PackageStateMutator.Result commitPackageStateMutation(
+ @Nullable PackageStateMutator.InitialState state,
+ @NonNull Consumer<PackageStateMutator> consumer) {
+ return PackageManagerService.this.commitPackageStateMutation(state, consumer);
+ }
}
@Override
@@ -8357,7 +8350,15 @@
@Nullable
@GuardedBy("mLock")
PackageSetting getPackageSettingForMutation(String packageName) {
- return (PackageSetting) mComputer.getPackageStateInternal(packageName);
+ return mSettings.getPackageLPr(packageName);
+ }
+
+ // TODO: Remove
+ @Deprecated
+ @Nullable
+ @GuardedBy("mLock")
+ PackageSetting getDisabledPackageSettingForMutation(String packageName) {
+ return mSettings.getDisabledSystemPkgLPr(packageName);
}
@VisibleForTesting(visibility = Visibility.PRIVATE)
@@ -8365,7 +8366,7 @@
PackageStateInternal getPackageStateInternal(String packageName) {
Computer computer = snapshotComputer();
if (computer == mLiveComputer) {
- synchronized (mLock) {
+ synchronized (mLiveComputerSyncLock) {
PackageSetting pkgSetting =
(PackageSetting) computer.getPackageStateInternal(packageName);
if (pkgSetting == null) {
@@ -8374,15 +8375,16 @@
return new PackageSetting(pkgSetting);
}
+ } else {
+ return computer.getPackageStateInternal(packageName);
}
- return computer.getPackageStateInternal(packageName);
}
@Nullable
PackageStateInternal getPackageStateInternal(String packageName, int callingUid) {
Computer computer = snapshotComputer();
if (computer == mLiveComputer) {
- synchronized (mLock) {
+ synchronized (mLiveComputerSyncLock) {
PackageSetting pkgSetting =
(PackageSetting) computer.getPackageStateInternal(packageName, callingUid);
if (pkgSetting == null) {
@@ -8391,8 +8393,9 @@
return new PackageSetting(pkgSetting);
}
+ } else {
+ return computer.getPackageStateInternal(packageName, callingUid);
}
- return computer.getPackageStateInternal(packageName, callingUid);
}
@Nullable
@@ -8400,7 +8403,7 @@
int callingUid, @UserIdInt int userId) {
Computer computer = snapshotComputer();
if (computer == mLiveComputer) {
- synchronized (mLock) {
+ synchronized (mLiveComputerSyncLock) {
PackageSetting pkgSetting =
(PackageSetting) filterPackageStateForInstalledAndFiltered(computer,
packageName, callingUid, userId);
@@ -8409,9 +8412,10 @@
}
return new PackageSetting(pkgSetting);
}
+ } else {
+ return filterPackageStateForInstalledAndFiltered(computer, packageName, callingUid,
+ userId);
}
-
- return filterPackageStateForInstalledAndFiltered(computer, packageName, callingUid, userId);
}
@Nullable
@@ -8439,16 +8443,8 @@
Computer computer = snapshotComputer();
if (computer == mLiveComputer) {
return new ArrayMap<>(computer.getPackageStates());
- }
- return computer.getPackageStates();
- }
-
- void forEachPackage(Consumer<AndroidPackage> actionLocked) {
- synchronized (mLock) {
- int numPackages = mPackages.size();
- for (int i = 0; i < numPackages; i++) {
- actionLocked.accept(mPackages.valueAt(i));
- }
+ } else {
+ return computer.getPackageStates();
}
}
@@ -8461,17 +8457,31 @@
}
}
- private void forEachPackageState(boolean locked, Consumer<PackageStateInternal> action) {
+ void forEachPackageState(boolean locked, Consumer<PackageStateInternal> action) {
if (locked) {
- forEachPackageSetting(action::accept);
+ synchronized (mLiveComputerSyncLock) {
+ forEachPackageState(mComputer.getPackageStates(), action);
+ }
} else {
Computer computer = snapshotComputer();
if (computer == mLiveComputer) {
- synchronized (mLock) {
+ synchronized (mLiveComputerSyncLock) {
forEachPackageState(computer.getPackageStates(), action);
- };
+ }
+ } else {
+ forEachPackageState(computer.getPackageStates(), action);
}
- forEachPackageState(computer.getPackageStates(), action);
+ }
+ }
+
+ void forEachPackage(Consumer<AndroidPackage> action) {
+ Computer computer = snapshotComputer();
+ if (computer == mLiveComputer) {
+ synchronized (mLiveComputerSyncLock) {
+ forEachPackage(computer.getPackageStates(), action);
+ }
+ } else {
+ forEachPackage(computer.getPackageStates(), action);
}
}
@@ -8485,21 +8495,106 @@
}
}
- void forEachInstalledPackage(@NonNull Consumer<AndroidPackage> actionLocked,
- @UserIdInt int userId) {
- synchronized (mLock) {
- int numPackages = mPackages.size();
- for (int i = 0; i < numPackages; i++) {
- AndroidPackage pkg = mPackages.valueAt(i);
- PackageSetting setting = mSettings.getPackageLPr(pkg.getPackageName());
- if (setting == null || !setting.getInstalled(userId)) {
- continue;
- }
- actionLocked.accept(pkg);
+ private void forEachPackage(
+ @NonNull ArrayMap<String, ? extends PackageStateInternal> packageStates,
+ @NonNull Consumer<AndroidPackage> consumer) {
+ int size = packageStates.size();
+ for (int index = 0; index < size; index++) {
+ PackageStateInternal packageState = packageStates.valueAt(index);
+ if (packageState.getPkg() != null) {
+ consumer.accept(packageState.getPkg());
}
}
}
+ void forEachInstalledPackage(boolean locked, @NonNull Consumer<AndroidPackage> action,
+ @UserIdInt int userId) {
+ Consumer<PackageStateInternal> actionWrapped = packageState -> {
+ if (packageState.getPkg() != null
+ && packageState.getUserStateOrDefault(userId).isInstalled()) {
+ action.accept(packageState.getPkg());
+ }
+ };
+ if (locked) {
+ synchronized (mLiveComputerSyncLock) {
+ forEachPackageState(mComputer.getPackageStates(), actionWrapped);
+ }
+ } else {
+ Computer computer = snapshotComputer();
+ if (computer == mLiveComputer) {
+ synchronized (mLiveComputerSyncLock) {
+ forEachPackageState(computer.getPackageStates(), actionWrapped);
+ }
+ } else {
+ forEachPackageState(computer.getPackageStates(), actionWrapped);
+ }
+ }
+ }
+
+ private void executeWithConsistentComputer(
+ @NonNull FunctionalUtils.ThrowingConsumer<Computer> consumer) {
+ Computer computer = snapshotComputer();
+ if (computer == mLiveComputer) {
+ synchronized (mLiveComputerSyncLock) {
+ consumer.accept(computer);
+ }
+ } else {
+ consumer.accept(computer);
+ }
+ }
+
+ private <T> T executeWithConsistentComputerReturning(
+ @NonNull FunctionalUtils.ThrowingFunction<Computer, T> function) {
+ Computer computer = snapshotComputer();
+ if (computer == mLiveComputer) {
+ synchronized (mLiveComputerSyncLock) {
+ return function.apply(computer);
+ }
+ } else {
+ return function.apply(computer);
+ }
+ }
+
+ private <ExceptionType extends Exception> void executeWithConsistentComputerThrowing(
+ @NonNull FunctionalUtils.ThrowingCheckedConsumer<Computer, ExceptionType> consumer)
+ throws ExceptionType {
+ Computer computer = snapshotComputer();
+ if (computer == mLiveComputer) {
+ synchronized (mLiveComputerSyncLock) {
+ consumer.accept(computer);
+ }
+ } else {
+ consumer.accept(computer);
+ }
+ }
+
+ private <ExceptionOne extends Exception, ExceptionTwo extends Exception> void
+ executeWithConsistentComputerThrowing2(
+ @NonNull FunctionalUtils.ThrowingChecked2Consumer<Computer, ExceptionOne,
+ ExceptionTwo> consumer) throws ExceptionOne, ExceptionTwo {
+ Computer computer = snapshotComputer();
+ if (computer == mLiveComputer) {
+ synchronized (mLiveComputerSyncLock) {
+ consumer.accept(computer);
+ }
+ } else {
+ consumer.accept(computer);
+ }
+ }
+
+ private <T, ExceptionType extends Exception> T executeWithConsistentComputerReturningThrowing(
+ @NonNull FunctionalUtils.ThrowingCheckedFunction<Computer, T, ExceptionType> function)
+ throws ExceptionType {
+ Computer computer = snapshotComputer();
+ if (computer == mLiveComputer) {
+ synchronized (mLiveComputerSyncLock) {
+ return function.apply(computer);
+ }
+ } else {
+ return function.apply(computer);
+ }
+ }
+
boolean isHistoricalPackageUsageAvailable() {
return mPackageUsage.isHistoricalPackageUsageAvailable();
}
@@ -8695,7 +8790,8 @@
enforceOwnerRights(packageName, Binder.getCallingUid());
final boolean changed;
synchronized (mLock) {
- changed = mSettings.getPackageLPr(packageName).setMimeGroup(mimeGroup, mimeTypes);
+ changed = mSettings.getPackageLPr(packageName).setMimeGroup(mimeGroup,
+ new ArraySet<>(mimeTypes));
}
if (changed) {
applyMimeGroupChanges(packageName, mimeGroup);
@@ -9198,4 +9294,62 @@
return new Pair<>(rescanFlags, reparseFlags);
}
+
+ /**
+ * @see PackageManagerInternal#recordInitialState()
+ */
+ @NonNull
+ public PackageStateMutator.InitialState recordInitialState() {
+ return mPackageStateMutator.initialState(mChangedPackagesSequenceNumber);
+ }
+
+ /**
+ * @see PackageManagerInternal#commitPackageStateMutation(PackageStateMutator.InitialState,
+ * Consumer)
+ */
+ @NonNull
+ public PackageStateMutator.Result commitPackageStateMutation(
+ @Nullable PackageStateMutator.InitialState initialState,
+ @NonNull Consumer<PackageStateMutator> consumer) {
+ synchronized (mPackageStateWriteLock) {
+ final PackageStateMutator.Result result = mPackageStateMutator.generateResult(
+ initialState, mChangedPackagesSequenceNumber);
+ if (result != PackageStateMutator.Result.SUCCESS) {
+ return result;
+ }
+
+ consumer.accept(mPackageStateMutator);
+ onChanged();
+ }
+
+ return PackageStateMutator.Result.SUCCESS;
+ }
+
+ /**
+ * @see PackageManagerInternal#commitPackageStateMutation(PackageStateMutator.InitialState,
+ * Consumer)
+ */
+ @NonNull
+ public PackageStateMutator.Result commitPackageStateMutation(
+ @Nullable PackageStateMutator.InitialState initialState, @NonNull String packageName,
+ @NonNull Consumer<PackageStateWrite> consumer) {
+ synchronized (mPackageStateWriteLock) {
+ final PackageStateMutator.Result result = mPackageStateMutator.generateResult(
+ initialState, mChangedPackagesSequenceNumber);
+ if (result != PackageStateMutator.Result.SUCCESS) {
+ return result;
+ }
+
+ PackageStateWrite state = mPackageStateMutator.forPackage(packageName);
+ if (state == null) {
+ return PackageStateMutator.Result.SPECIFIC_PACKAGE_NULL;
+ } else {
+ consumer.accept(state);
+ }
+
+ onChanged();
+ }
+
+ return PackageStateMutator.Result.SUCCESS;
+ }
}
diff --git a/services/core/java/com/android/server/pm/PackageSetting.java b/services/core/java/com/android/server/pm/PackageSetting.java
index 923a133..0da57bc 100644
--- a/services/core/java/com/android/server/pm/PackageSetting.java
+++ b/services/core/java/com/android/server/pm/PackageSetting.java
@@ -395,14 +395,13 @@
return this;
}
- public boolean setMimeGroup(String mimeGroup, List<String> mimeTypes) {
+ public boolean setMimeGroup(String mimeGroup, ArraySet<String> newMimeTypes) {
Set<String> oldMimeTypes = mimeGroups == null ? null : mimeGroups.get(mimeGroup);
if (oldMimeTypes == null) {
throw new IllegalArgumentException("Unknown MIME group " + mimeGroup
+ " for package " + mName);
}
- ArraySet<String> newMimeTypes = new ArraySet<>(mimeTypes);
boolean hasChanges = !newMimeTypes.equals(oldMimeTypes);
mimeGroups.put(mimeGroup, newMimeTypes);
if (hasChanges) {
@@ -670,6 +669,15 @@
return state;
}
+ public PackageUserStateImpl getOrCreateUserState(@UserIdInt int userId) {
+ PackageUserStateImpl state = mUserStates.get(userId);
+ if (state == null) {
+ state = new PackageUserStateImpl();
+ mUserStates.put(userId, state);
+ }
+ return state;
+ }
+
@NonNull
public PackageUserStateInternal readUserState(int userId) {
PackageUserStateInternal state = mUserStates.get(userId);
@@ -832,7 +840,6 @@
}
final SuspendParams oldSuspendParams =
existingUserState.getSuspendParams().put(suspendingPackage, newSuspendParams);
- existingUserState.setSuspended(true);
onChanged();
return !Objects.equals(oldSuspendParams, newSuspendParams);
}
@@ -848,7 +855,6 @@
existingUserState.setSuspendParams(null);
}
}
- existingUserState.setSuspended((existingUserState.getSuspendParams() != null));
onChanged();
return wasModified;
}
@@ -866,7 +872,6 @@
existingUserState.setSuspendParams(null);
}
}
- existingUserState.setSuspended((existingUserState.getSuspendParams() != null));
onChanged();
}
@@ -889,7 +894,7 @@
}
void setUserState(int userId, long ceDataInode, int enabled, boolean installed, boolean stopped,
- boolean notLaunched, boolean hidden, int distractionFlags, boolean suspended,
+ boolean notLaunched, boolean hidden, int distractionFlags,
ArrayMap<String, SuspendParams> suspendParams, boolean instantApp,
boolean virtualPreload, String lastDisableAppCaller,
ArraySet<String> enabledComponents, ArraySet<String> disabledComponents,
@@ -904,7 +909,6 @@
.setNotLaunched(notLaunched)
.setHidden(hidden)
.setDistractionFlags(distractionFlags)
- .setSuspended(suspended)
.setLastDisableAppCaller(lastDisableAppCaller)
.setEnabledComponents(enabledComponents)
.setDisabledComponents(disabledComponents)
@@ -919,11 +923,9 @@
void setUserState(int userId, PackageUserStateInternal otherState) {
setUserState(userId, otherState.getCeDataInode(), otherState.getEnabledState(),
- otherState.isInstalled(),
- otherState.isStopped(), otherState.isNotLaunched(), otherState.isHidden(),
- otherState.getDistractionFlags(), otherState.isSuspended(),
- otherState.getSuspendParams(),
- otherState.isInstantApp(),
+ otherState.isInstalled(), otherState.isStopped(), otherState.isNotLaunched(),
+ otherState.isHidden(), otherState.getDistractionFlags(),
+ otherState.getSuspendParams(), otherState.isInstantApp(),
otherState.isVirtualPreload(), otherState.getLastDisableAppCaller(),
new ArraySet<>(otherState.getEnabledComponentsNoCopy()),
new ArraySet<>(otherState.getDisabledComponentsNoCopy()),
@@ -1553,10 +1555,10 @@
}
@DataClass.Generated(
- time = 1635870549646L,
+ time = 1636145878985L,
codegenVersion = "1.0.23",
sourceFile = "frameworks/base/services/core/java/com/android/server/pm/PackageSetting.java",
- inputSignatures = "private int sharedUserId\nprivate @android.annotation.Nullable java.util.Map<java.lang.String,java.util.Set<java.lang.String>> mimeGroups\nprivate @java.lang.Deprecated @android.annotation.Nullable java.util.Set<java.lang.String> mOldCodePaths\nprivate @android.annotation.Nullable java.lang.String[] usesStaticLibraries\nprivate @android.annotation.Nullable long[] usesStaticLibrariesVersions\nprivate @android.annotation.Nullable @java.lang.Deprecated java.lang.String legacyNativeLibraryPath\nprivate @android.annotation.NonNull java.lang.String mName\nprivate @android.annotation.Nullable java.lang.String mRealName\nprivate int mAppId\nprivate @android.annotation.Nullable com.android.server.pm.parsing.pkg.AndroidPackage pkg\nprivate @android.annotation.Nullable com.android.server.pm.SharedUserSetting sharedUser\nprivate @android.annotation.NonNull java.io.File mPath\nprivate @android.annotation.NonNull java.lang.String mPathString\nprivate float mLoadingProgress\nprivate @android.annotation.Nullable java.lang.String mPrimaryCpuAbi\nprivate @android.annotation.Nullable java.lang.String mSecondaryCpuAbi\nprivate @android.annotation.Nullable java.lang.String mCpuAbiOverride\nprivate long mLastModifiedTime\nprivate long firstInstallTime\nprivate long lastUpdateTime\nprivate long versionCode\nprivate @android.annotation.NonNull com.android.server.pm.PackageSignatures signatures\nprivate boolean installPermissionsFixed\nprivate @android.annotation.NonNull com.android.server.pm.PackageKeySetData keySetData\nprivate final @android.annotation.NonNull android.util.SparseArray<com.android.server.pm.pkg.PackageUserStateImpl> mUserStates\nprivate @android.annotation.NonNull com.android.server.pm.InstallSource installSource\nprivate @android.annotation.Nullable java.lang.String volumeUuid\nprivate int categoryOverride\nprivate boolean updateAvailable\nprivate boolean forceQueryableOverride\nprivate @android.annotation.NonNull com.android.server.pm.pkg.PackageStateUnserialized pkgState\nprivate @android.annotation.NonNull java.util.UUID mDomainSetId\nprivate final @android.annotation.NonNull com.android.server.utils.SnapshotCache<com.android.server.pm.PackageSetting> mSnapshot\nprivate com.android.server.utils.SnapshotCache<com.android.server.pm.PackageSetting> makeCache()\npublic com.android.server.pm.PackageSetting snapshot()\npublic void dumpDebug(android.util.proto.ProtoOutputStream,long,java.util.List<android.content.pm.UserInfo>,com.android.server.pm.permission.LegacyPermissionDataProvider)\npublic java.util.List<java.lang.String> getMimeGroup(java.lang.String)\nprivate java.util.Set<java.lang.String> getMimeGroupInternal(java.lang.String)\npublic boolean isSharedUser()\npublic com.android.server.pm.PackageSetting setAppId(int)\npublic com.android.server.pm.PackageSetting setCpuAbiOverride(java.lang.String)\npublic com.android.server.pm.PackageSetting setFirstInstallTime(long)\npublic com.android.server.pm.PackageSetting setForceQueryableOverride(boolean)\npublic com.android.server.pm.PackageSetting setInstallerPackageName(java.lang.String)\npublic com.android.server.pm.PackageSetting setInstallSource(com.android.server.pm.InstallSource)\n com.android.server.pm.PackageSetting removeInstallerPackage(java.lang.String)\npublic com.android.server.pm.PackageSetting setIsOrphaned(boolean)\npublic com.android.server.pm.PackageSetting setKeySetData(com.android.server.pm.PackageKeySetData)\npublic com.android.server.pm.PackageSetting setLastModifiedTime(long)\npublic com.android.server.pm.PackageSetting setLastUpdateTime(long)\npublic com.android.server.pm.PackageSetting setLongVersionCode(long)\npublic boolean setMimeGroup(java.lang.String,java.util.List<java.lang.String>)\npublic com.android.server.pm.PackageSetting setPkg(com.android.server.pm.parsing.pkg.AndroidPackage)\npublic com.android.server.pm.PackageSetting setPrimaryCpuAbi(java.lang.String)\npublic com.android.server.pm.PackageSetting setSecondaryCpuAbi(java.lang.String)\npublic com.android.server.pm.PackageSetting setSignatures(com.android.server.pm.PackageSignatures)\npublic com.android.server.pm.PackageSetting setVolumeUuid(java.lang.String)\npublic @java.lang.Override boolean isExternalStorage()\npublic com.android.server.pm.PackageSetting setUpdateAvailable(boolean)\npublic int getSharedUserIdInt()\npublic @java.lang.Override java.lang.String toString()\nprotected void copyMimeGroups(java.util.Map<java.lang.String,java.util.Set<java.lang.String>>)\npublic void updateFrom(com.android.server.pm.PackageSetting)\n com.android.server.pm.PackageSetting updateMimeGroups(java.util.Set<java.lang.String>)\npublic @java.lang.Deprecated @java.lang.Override com.android.server.pm.permission.LegacyPermissionState getLegacyPermissionState()\npublic com.android.server.pm.PackageSetting setInstallPermissionsFixed(boolean)\npublic boolean isPrivileged()\npublic boolean isOem()\npublic boolean isVendor()\npublic boolean isProduct()\npublic @java.lang.Override boolean isRequiredForSystemUser()\npublic boolean isSystemExt()\npublic boolean isOdm()\npublic boolean isSystem()\npublic android.content.pm.SigningDetails getSigningDetails()\npublic com.android.server.pm.PackageSetting setSigningDetails(android.content.pm.SigningDetails)\npublic void copyPackageSetting(com.android.server.pm.PackageSetting)\n @com.android.internal.annotations.VisibleForTesting com.android.server.pm.pkg.PackageUserStateImpl modifyUserState(int)\npublic @android.annotation.NonNull com.android.server.pm.pkg.PackageUserStateInternal readUserState(int)\n void setEnabled(int,int,java.lang.String)\n int getEnabled(int)\n java.lang.String getLastDisabledAppCaller(int)\n void setInstalled(boolean,int)\n boolean getInstalled(int)\n int getInstallReason(int)\n void setInstallReason(int,int)\n int getUninstallReason(int)\n void setUninstallReason(int,int)\n boolean setOverlayPaths(android.content.pm.overlay.OverlayPaths,int)\n @android.annotation.NonNull android.content.pm.overlay.OverlayPaths getOverlayPaths(int)\n boolean setOverlayPathsForLibrary(java.lang.String,android.content.pm.overlay.OverlayPaths,int)\n @android.annotation.NonNull java.util.Map<java.lang.String,android.content.pm.overlay.OverlayPaths> getOverlayPathsForLibrary(int)\n boolean isAnyInstalled(int[])\n int[] queryInstalledUsers(int[],boolean)\n long getCeDataInode(int)\n void setCeDataInode(long,int)\n boolean getStopped(int)\n void setStopped(boolean,int)\n boolean getNotLaunched(int)\n void setNotLaunched(boolean,int)\n boolean getHidden(int)\n void setHidden(boolean,int)\n int getDistractionFlags(int)\n void setDistractionFlags(int,int)\n boolean getSuspended(int)\n boolean addOrUpdateSuspension(java.lang.String,android.content.pm.SuspendDialogInfo,android.os.PersistableBundle,android.os.PersistableBundle,int)\n boolean removeSuspension(java.lang.String,int)\n void removeSuspension(java.util.function.Predicate<java.lang.String>,int)\npublic boolean getInstantApp(int)\n void setInstantApp(boolean,int)\n boolean getVirtualPreload(int)\n void setVirtualPreload(boolean,int)\n void setUserState(int,long,int,boolean,boolean,boolean,boolean,int,boolean,android.util.ArrayMap<java.lang.String,com.android.server.pm.pkg.SuspendParams>,boolean,boolean,java.lang.String,android.util.ArraySet<java.lang.String>,android.util.ArraySet<java.lang.String>,int,int,java.lang.String,java.lang.String)\n void setUserState(int,com.android.server.pm.pkg.PackageUserStateInternal)\n android.util.ArraySet<java.lang.String> getEnabledComponents(int)\n android.util.ArraySet<java.lang.String> getDisabledComponents(int)\n void setEnabledComponents(android.util.ArraySet<java.lang.String>,int)\n void setDisabledComponents(android.util.ArraySet<java.lang.String>,int)\n void setEnabledComponentsCopy(android.util.ArraySet<java.lang.String>,int)\n void setDisabledComponentsCopy(android.util.ArraySet<java.lang.String>,int)\n com.android.server.pm.pkg.PackageUserStateImpl modifyUserStateComponents(int,boolean,boolean)\n void addDisabledComponent(java.lang.String,int)\n void addEnabledComponent(java.lang.String,int)\n boolean enableComponentLPw(java.lang.String,int)\n boolean disableComponentLPw(java.lang.String,int)\n boolean restoreComponentLPw(java.lang.String,int)\n int getCurrentEnabledStateLPr(java.lang.String,int)\n void removeUser(int)\npublic int[] getNotInstalledUserIds()\n void writePackageUserPermissionsProto(android.util.proto.ProtoOutputStream,long,java.util.List<android.content.pm.UserInfo>,com.android.server.pm.permission.LegacyPermissionDataProvider)\nprotected void writeUsersInfoToProto(android.util.proto.ProtoOutputStream,long)\n void setHarmfulAppWarning(int,java.lang.String)\n java.lang.String getHarmfulAppWarning(int)\n com.android.server.pm.PackageSetting setPath(java.io.File)\npublic @com.android.internal.annotations.VisibleForTesting boolean overrideNonLocalizedLabelAndIcon(android.content.ComponentName,java.lang.String,java.lang.Integer,int)\npublic void resetOverrideComponentLabelIcon(int)\npublic void setSplashScreenTheme(int,java.lang.String)\npublic @android.annotation.Nullable java.lang.String getSplashScreenTheme(int)\npublic boolean isLoading()\npublic com.android.server.pm.PackageSetting setLoadingProgress(float)\npublic @android.annotation.NonNull @java.lang.Override long getVersionCode()\npublic @android.annotation.Nullable @java.lang.Override java.util.Map<java.lang.String,java.util.Set<java.lang.String>> getMimeGroups()\npublic @android.annotation.NonNull @java.lang.Override java.lang.String getPackageName()\npublic @android.annotation.Nullable @java.lang.Override com.android.server.pm.pkg.AndroidPackageApi getAndroidPackage()\npublic @android.annotation.Nullable @java.lang.Override java.lang.Integer getSharedUserId()\npublic @android.annotation.NonNull android.content.pm.SigningInfo getSigningInfo()\npublic @android.annotation.NonNull @java.lang.Override java.lang.String[] getUsesStaticLibraries()\npublic @android.annotation.NonNull @java.lang.Override long[] getUsesStaticLibrariesVersions()\npublic @android.annotation.NonNull @java.lang.Override java.util.List<android.content.pm.SharedLibraryInfo> getUsesLibraryInfos()\npublic @android.annotation.NonNull @java.lang.Override java.util.List<java.lang.String> getUsesLibraryFiles()\npublic @java.lang.Override boolean isHiddenUntilInstalled()\npublic @android.annotation.NonNull @java.lang.Override long[] getLastPackageUsageTime()\npublic @java.lang.Override boolean isUpdatedSystemApp()\npublic com.android.server.pm.PackageSetting setDomainSetId(java.util.UUID)\npublic com.android.server.pm.PackageSetting setSharedUser(com.android.server.pm.SharedUserSetting)\npublic com.android.server.pm.PackageSetting setCategoryOverride(int)\npublic com.android.server.pm.PackageSetting setLegacyNativeLibraryPath(java.lang.String)\npublic com.android.server.pm.PackageSetting setMimeGroups(java.util.Map<java.lang.String,java.util.Set<java.lang.String>>)\npublic com.android.server.pm.PackageSetting setOldCodePaths(java.util.Set<java.lang.String>)\npublic com.android.server.pm.PackageSetting setUsesStaticLibraries(java.lang.String[])\npublic com.android.server.pm.PackageSetting setUsesStaticLibrariesVersions(long[])\npublic @android.annotation.NonNull @java.lang.Override com.android.server.pm.pkg.PackageStateUnserialized getTransientState()\npublic @android.annotation.NonNull android.util.SparseArray<? extends PackageUserStateInternal> getUserStates()\nclass PackageSetting extends com.android.server.pm.SettingBase implements [com.android.server.pm.pkg.PackageStateInternal]\n@com.android.internal.util.DataClass(genGetters=true, genConstructor=false, genSetters=false, genBuilder=false)")
+ inputSignatures = "private int sharedUserId\nprivate @android.annotation.Nullable java.util.Map<java.lang.String,java.util.Set<java.lang.String>> mimeGroups\nprivate @java.lang.Deprecated @android.annotation.Nullable java.util.Set<java.lang.String> mOldCodePaths\nprivate @android.annotation.Nullable java.lang.String[] usesStaticLibraries\nprivate @android.annotation.Nullable long[] usesStaticLibrariesVersions\nprivate @android.annotation.Nullable @java.lang.Deprecated java.lang.String legacyNativeLibraryPath\nprivate @android.annotation.NonNull java.lang.String mName\nprivate @android.annotation.Nullable java.lang.String mRealName\nprivate int mAppId\nprivate @android.annotation.Nullable com.android.server.pm.parsing.pkg.AndroidPackage pkg\nprivate @android.annotation.Nullable com.android.server.pm.SharedUserSetting sharedUser\nprivate @android.annotation.NonNull java.io.File mPath\nprivate @android.annotation.NonNull java.lang.String mPathString\nprivate float mLoadingProgress\nprivate @android.annotation.Nullable java.lang.String mPrimaryCpuAbi\nprivate @android.annotation.Nullable java.lang.String mSecondaryCpuAbi\nprivate @android.annotation.Nullable java.lang.String mCpuAbiOverride\nprivate long mLastModifiedTime\nprivate long firstInstallTime\nprivate long lastUpdateTime\nprivate long versionCode\nprivate @android.annotation.NonNull com.android.server.pm.PackageSignatures signatures\nprivate boolean installPermissionsFixed\nprivate @android.annotation.NonNull com.android.server.pm.PackageKeySetData keySetData\nprivate final @android.annotation.NonNull android.util.SparseArray<com.android.server.pm.pkg.PackageUserStateImpl> mUserStates\nprivate @android.annotation.NonNull com.android.server.pm.InstallSource installSource\nprivate @android.annotation.Nullable java.lang.String volumeUuid\nprivate int categoryOverride\nprivate boolean updateAvailable\nprivate boolean forceQueryableOverride\nprivate @android.annotation.NonNull com.android.server.pm.pkg.PackageStateUnserialized pkgState\nprivate @android.annotation.NonNull java.util.UUID mDomainSetId\nprivate final @android.annotation.NonNull com.android.server.utils.SnapshotCache<com.android.server.pm.PackageSetting> mSnapshot\nprivate com.android.server.utils.SnapshotCache<com.android.server.pm.PackageSetting> makeCache()\npublic com.android.server.pm.PackageSetting snapshot()\npublic void dumpDebug(android.util.proto.ProtoOutputStream,long,java.util.List<android.content.pm.UserInfo>,com.android.server.pm.permission.LegacyPermissionDataProvider)\npublic boolean isSharedUser()\npublic com.android.server.pm.PackageSetting setAppId(int)\npublic com.android.server.pm.PackageSetting setCpuAbiOverride(java.lang.String)\npublic com.android.server.pm.PackageSetting setFirstInstallTime(long)\npublic com.android.server.pm.PackageSetting setForceQueryableOverride(boolean)\npublic com.android.server.pm.PackageSetting setInstallerPackageName(java.lang.String)\npublic com.android.server.pm.PackageSetting setInstallSource(com.android.server.pm.InstallSource)\n com.android.server.pm.PackageSetting removeInstallerPackage(java.lang.String)\npublic com.android.server.pm.PackageSetting setIsOrphaned(boolean)\npublic com.android.server.pm.PackageSetting setKeySetData(com.android.server.pm.PackageKeySetData)\npublic com.android.server.pm.PackageSetting setLastModifiedTime(long)\npublic com.android.server.pm.PackageSetting setLastUpdateTime(long)\npublic com.android.server.pm.PackageSetting setLongVersionCode(long)\npublic boolean setMimeGroup(java.lang.String,java.util.List<java.lang.String>)\npublic com.android.server.pm.PackageSetting setPkg(com.android.server.pm.parsing.pkg.AndroidPackage)\npublic com.android.server.pm.PackageSetting setPrimaryCpuAbi(java.lang.String)\npublic com.android.server.pm.PackageSetting setSecondaryCpuAbi(java.lang.String)\npublic com.android.server.pm.PackageSetting setSignatures(com.android.server.pm.PackageSignatures)\npublic com.android.server.pm.PackageSetting setVolumeUuid(java.lang.String)\npublic @java.lang.Override boolean isExternalStorage()\npublic com.android.server.pm.PackageSetting setUpdateAvailable(boolean)\npublic int getSharedUserIdInt()\npublic @java.lang.Override java.lang.String toString()\nprotected void copyMimeGroups(java.util.Map<java.lang.String,java.util.Set<java.lang.String>>)\npublic void updateFrom(com.android.server.pm.PackageSetting)\n com.android.server.pm.PackageSetting updateMimeGroups(java.util.Set<java.lang.String>)\npublic @java.lang.Deprecated @java.lang.Override com.android.server.pm.permission.LegacyPermissionState getLegacyPermissionState()\npublic com.android.server.pm.PackageSetting setInstallPermissionsFixed(boolean)\npublic boolean isPrivileged()\npublic boolean isOem()\npublic boolean isVendor()\npublic boolean isProduct()\npublic @java.lang.Override boolean isRequiredForSystemUser()\npublic boolean isSystemExt()\npublic boolean isOdm()\npublic boolean isSystem()\npublic android.content.pm.SigningDetails getSigningDetails()\npublic com.android.server.pm.PackageSetting setSigningDetails(android.content.pm.SigningDetails)\npublic void copyPackageSetting(com.android.server.pm.PackageSetting)\n @com.android.internal.annotations.VisibleForTesting com.android.server.pm.pkg.PackageUserStateImpl modifyUserState(int)\npublic com.android.server.pm.pkg.PackageUserStateImpl getOrCreateUserState(int)\npublic @android.annotation.NonNull com.android.server.pm.pkg.PackageUserStateInternal readUserState(int)\n void setEnabled(int,int,java.lang.String)\n int getEnabled(int)\n java.lang.String getLastDisabledAppCaller(int)\n void setInstalled(boolean,int)\n boolean getInstalled(int)\n int getInstallReason(int)\n void setInstallReason(int,int)\n int getUninstallReason(int)\n void setUninstallReason(int,int)\n boolean setOverlayPaths(android.content.pm.overlay.OverlayPaths,int)\n @android.annotation.NonNull android.content.pm.overlay.OverlayPaths getOverlayPaths(int)\n boolean setOverlayPathsForLibrary(java.lang.String,android.content.pm.overlay.OverlayPaths,int)\n @android.annotation.NonNull java.util.Map<java.lang.String,android.content.pm.overlay.OverlayPaths> getOverlayPathsForLibrary(int)\n boolean isAnyInstalled(int[])\n int[] queryInstalledUsers(int[],boolean)\n long getCeDataInode(int)\n void setCeDataInode(long,int)\n boolean getStopped(int)\n void setStopped(boolean,int)\n boolean getNotLaunched(int)\n void setNotLaunched(boolean,int)\n boolean getHidden(int)\n void setHidden(boolean,int)\n int getDistractionFlags(int)\n void setDistractionFlags(int,int)\npublic boolean getInstantApp(int)\n void setInstantApp(boolean,int)\n boolean getVirtualPreload(int)\n void setVirtualPreload(boolean,int)\n void setUserState(int,long,int,boolean,boolean,boolean,boolean,int,android.util.ArrayMap<java.lang.String,com.android.server.pm.pkg.SuspendParams>,boolean,boolean,java.lang.String,android.util.ArraySet<java.lang.String>,android.util.ArraySet<java.lang.String>,int,int,java.lang.String,java.lang.String)\n void setUserState(int,com.android.server.pm.pkg.PackageUserStateInternal)\n android.util.ArraySet<java.lang.String> getEnabledComponents(int)\n android.util.ArraySet<java.lang.String> getDisabledComponents(int)\n void setEnabledComponents(android.util.ArraySet<java.lang.String>,int)\n void setDisabledComponents(android.util.ArraySet<java.lang.String>,int)\n void setEnabledComponentsCopy(android.util.ArraySet<java.lang.String>,int)\n void setDisabledComponentsCopy(android.util.ArraySet<java.lang.String>,int)\n com.android.server.pm.pkg.PackageUserStateImpl modifyUserStateComponents(int,boolean,boolean)\n void addDisabledComponent(java.lang.String,int)\n void addEnabledComponent(java.lang.String,int)\n boolean enableComponentLPw(java.lang.String,int)\n boolean disableComponentLPw(java.lang.String,int)\n boolean restoreComponentLPw(java.lang.String,int)\n int getCurrentEnabledStateLPr(java.lang.String,int)\n void removeUser(int)\npublic int[] getNotInstalledUserIds()\n void writePackageUserPermissionsProto(android.util.proto.ProtoOutputStream,long,java.util.List<android.content.pm.UserInfo>,com.android.server.pm.permission.LegacyPermissionDataProvider)\nprotected void writeUsersInfoToProto(android.util.proto.ProtoOutputStream,long)\n void setHarmfulAppWarning(int,java.lang.String)\n java.lang.String getHarmfulAppWarning(int)\n com.android.server.pm.PackageSetting setPath(java.io.File)\npublic @com.android.internal.annotations.VisibleForTesting boolean overrideNonLocalizedLabelAndIcon(android.content.ComponentName,java.lang.String,java.lang.Integer,int)\npublic void resetOverrideComponentLabelIcon(int)\npublic void setSplashScreenTheme(int,java.lang.String)\npublic @android.annotation.Nullable java.lang.String getSplashScreenTheme(int)\npublic boolean isLoading()\npublic com.android.server.pm.PackageSetting setLoadingProgress(float)\npublic @android.annotation.NonNull @java.lang.Override long getVersionCode()\npublic @android.annotation.Nullable @java.lang.Override java.util.Map<java.lang.String,java.util.Set<java.lang.String>> getMimeGroups()\npublic @android.annotation.NonNull @java.lang.Override java.lang.String getPackageName()\npublic @android.annotation.Nullable @java.lang.Override com.android.server.pm.pkg.AndroidPackageApi getAndroidPackage()\npublic @android.annotation.Nullable @java.lang.Override java.lang.Integer getSharedUserId()\npublic @android.annotation.NonNull android.content.pm.SigningInfo getSigningInfo()\npublic @android.annotation.NonNull @java.lang.Override java.lang.String[] getUsesStaticLibraries()\npublic @android.annotation.NonNull @java.lang.Override long[] getUsesStaticLibrariesVersions()\npublic @android.annotation.NonNull @java.lang.Override java.util.List<android.content.pm.SharedLibraryInfo> getUsesLibraryInfos()\npublic @android.annotation.NonNull @java.lang.Override java.util.List<java.lang.String> getUsesLibraryFiles()\npublic @java.lang.Override boolean isHiddenUntilInstalled()\npublic @android.annotation.NonNull @java.lang.Override long[] getLastPackageUsageTime()\npublic @java.lang.Override boolean isUpdatedSystemApp()\npublic com.android.server.pm.PackageSetting setDomainSetId(java.util.UUID)\npublic com.android.server.pm.PackageSetting setSharedUser(com.android.server.pm.SharedUserSetting)\npublic com.android.server.pm.PackageSetting setCategoryOverride(int)\npublic com.android.server.pm.PackageSetting setLegacyNativeLibraryPath(java.lang.String)\npublic com.android.server.pm.PackageSetting setMimeGroups(java.util.Map<java.lang.String,java.util.Set<java.lang.String>>)\npublic com.android.server.pm.PackageSetting setOldCodePaths(java.util.Set<java.lang.String>)\npublic com.android.server.pm.PackageSetting setUsesStaticLibraries(java.lang.String[])\npublic com.android.server.pm.PackageSetting setUsesStaticLibrariesVersions(long[])\npublic @android.annotation.NonNull @java.lang.Override com.android.server.pm.pkg.PackageStateUnserialized getTransientState()\npublic @android.annotation.NonNull android.util.SparseArray<? extends PackageUserStateInternal> getUserStates()\nclass PackageSetting extends com.android.server.pm.SettingBase implements [com.android.server.pm.pkg.PackageStateInternal]\n@com.android.internal.util.DataClass(genGetters=true, genConstructor=false, genSetters=false, genBuilder=false)")
@Deprecated
private void __metadata() {}
diff --git a/services/core/java/com/android/server/pm/SettingBase.java b/services/core/java/com/android/server/pm/SettingBase.java
index ed85ff9..4345d51 100644
--- a/services/core/java/com/android/server/pm/SettingBase.java
+++ b/services/core/java/com/android/server/pm/SettingBase.java
@@ -22,6 +22,7 @@
import com.android.internal.annotations.VisibleForTesting;
import com.android.server.pm.permission.LegacyPermissionState;
+import com.android.server.pm.pkg.mutate.PackageStateMutator;
import com.android.server.utils.Snappable;
import com.android.server.utils.Watchable;
import com.android.server.utils.WatchableImpl;
@@ -88,6 +89,7 @@
* Notify listeners that this object has changed.
*/
protected void onChanged() {
+ PackageStateMutator.onPackageStateChanged();
dispatchChange(this);
}
@@ -122,7 +124,7 @@
return mLegacyPermissionsState;
}
- SettingBase setFlags(int pkgFlags) {
+ public SettingBase setFlags(int pkgFlags) {
this.mPkgFlags = pkgFlags
& (ApplicationInfo.FLAG_SYSTEM
| ApplicationInfo.FLAG_EXTERNAL_STORAGE
@@ -131,7 +133,7 @@
return this;
}
- SettingBase setPrivateFlags(int pkgPrivateFlags) {
+ public SettingBase setPrivateFlags(int pkgPrivateFlags) {
this.mPkgPrivateFlags = pkgPrivateFlags
& (ApplicationInfo.PRIVATE_FLAG_PRIVILEGED
| ApplicationInfo.PRIVATE_FLAG_OEM
diff --git a/services/core/java/com/android/server/pm/Settings.java b/services/core/java/com/android/server/pm/Settings.java
index b1ce6a2..6c47eb0 100644
--- a/services/core/java/com/android/server/pm/Settings.java
+++ b/services/core/java/com/android/server/pm/Settings.java
@@ -114,6 +114,7 @@
import com.android.server.pm.permission.LegacyPermissionSettings;
import com.android.server.pm.permission.LegacyPermissionState;
import com.android.server.pm.permission.LegacyPermissionState.PermissionState;
+import com.android.server.pm.pkg.PackageStateInternal;
import com.android.server.pm.pkg.PackageUserState;
import com.android.server.pm.pkg.PackageUserStateInternal;
import com.android.server.pm.pkg.SuspendParams;
@@ -162,6 +163,7 @@
import java.util.Objects;
import java.util.Set;
import java.util.UUID;
+import java.util.function.Consumer;
/**
* Holds information about dynamic settings.
@@ -257,7 +259,7 @@
public static final int SIGNATURE_MALFORMED_RECOVER = 3;
}
- private static final boolean DEBUG_STOPPED = false;
+ static final boolean DEBUG_STOPPED = false;
private static final boolean DEBUG_MU = false;
private static final boolean DEBUG_KERNEL = false;
private static final boolean DEBUG_PARSER = false;
@@ -625,7 +627,15 @@
mOtherAppIds = new WatchedSparseArray<>();
mPermissions = new LegacyPermissionSettings(lock);
mRuntimePermissionsPersistence = new RuntimePermissionPersistence(
- runtimePermissionsPersistence);
+ runtimePermissionsPersistence, new Consumer<Integer>() {
+ @Override
+ public void accept(Integer userId) {
+ synchronized (mLock) {
+ mRuntimePermissionsPersistence.writeStateForUserSync(userId,
+ mPermissionDataProvider, mPackages, mSharedUsers);
+ }
+ }
+ });
mPermissionDataProvider = permissionDataProvider;
mSystemDir = new File(dataDir, "system");
@@ -989,7 +999,6 @@
true /*notLaunched*/,
false /*hidden*/,
0 /*distractionFlags*/,
- false /*suspended*/,
null /*suspendParams*/,
instantApp,
virtualPreload,
@@ -1423,7 +1432,7 @@
void writeAllRuntimePermissionsLPr() {
for (int userId : UserManagerService.getInstance().getUserIds()) {
- mRuntimePermissionsPersistence.writeStateForUserAsyncLPr(userId);
+ mRuntimePermissionsPersistence.writeStateForUserAsync(userId);
}
}
@@ -1432,15 +1441,15 @@
}
void updateRuntimePermissionsFingerprintLPr(@UserIdInt int userId) {
- mRuntimePermissionsPersistence.updateRuntimePermissionsFingerprintLPr(userId);
+ mRuntimePermissionsPersistence.updateRuntimePermissionsFingerprint(userId);
}
int getDefaultRuntimePermissionsVersionLPr(int userId) {
- return mRuntimePermissionsPersistence.getVersionLPr(userId);
+ return mRuntimePermissionsPersistence.getVersion(userId);
}
void setDefaultRuntimePermissionsVersionLPr(int version, int userId) {
- mRuntimePermissionsPersistence.setVersionLPr(version, userId);
+ mRuntimePermissionsPersistence.setVersion(version, userId);
}
void setPermissionControllerVersion(long version) {
@@ -1646,7 +1655,6 @@
false /*notLaunched*/,
false /*hidden*/,
0 /*distractionFlags*/,
- false /*suspended*/,
null /*suspendParams*/,
false /*instantApp*/,
false /*virtualPreload*/,
@@ -1816,10 +1824,9 @@
setBlockUninstallLPw(userId, name, true);
}
ps.setUserState(userId, ceDataInode, enabled, installed, stopped, notLaunched,
- hidden, distractionFlags, suspended, suspendParamsMap,
- instantApp, virtualPreload, enabledCaller, enabledComponents,
- disabledComponents, installReason, uninstallReason, harmfulAppWarning,
- splashScreenTheme);
+ hidden, distractionFlags, suspendParamsMap, instantApp, virtualPreload,
+ enabledCaller, enabledComponents, disabledComponents, installReason,
+ uninstallReason, harmfulAppWarning, splashScreenTheme);
mDomainVerificationManager.setLegacyUserState(name, userId, verifState);
} else if (tagName.equals("preferred-activities")) {
@@ -3100,7 +3107,8 @@
}
for (UserInfo user : users) {
- mRuntimePermissionsPersistence.readStateForUserSyncLPr(user.id);
+ mRuntimePermissionsPersistence.readStateForUserSync(user.id, getInternalVersion(),
+ mPackages, mSharedUsers, getUserRuntimePermissionsFile(user.id));
}
/*
@@ -3124,7 +3132,8 @@
}
void readPermissionStateForUserSyncLPr(@UserIdInt int userId) {
- mRuntimePermissionsPersistence.readStateForUserSyncLPr(userId);
+ mRuntimePermissionsPersistence.readStateForUserSync(userId, getInternalVersion(),
+ mPackages, mSharedUsers, getUserRuntimePermissionsFile(userId));
}
void applyDefaultPreferredAppsLPw(int userId) {
@@ -4120,7 +4129,7 @@
file.delete();
removeCrossProfileIntentFiltersLPw(userId);
- mRuntimePermissionsPersistence.onUserRemovedLPw(userId);
+ mRuntimePermissionsPersistence.onUserRemoved(userId);
mDomainVerificationManager.clearUser(userId);
writePackageListLPr();
@@ -4485,24 +4494,25 @@
}
}
for (UserInfo user : users) {
+ final PackageUserStateInternal userState = ps.getUserStateOrDefault(user.id);
pw.print(checkinTag);
pw.print("-");
pw.print("usr");
pw.print(",");
pw.print(user.id);
pw.print(",");
- pw.print(ps.getInstalled(user.id) ? "I" : "i");
- pw.print(ps.getHidden(user.id) ? "B" : "b");
- pw.print(ps.getSuspended(user.id) ? "SU" : "su");
- pw.print(ps.getStopped(user.id) ? "S" : "s");
- pw.print(ps.getNotLaunched(user.id) ? "l" : "L");
- pw.print(ps.getInstantApp(user.id) ? "IA" : "ia");
- pw.print(ps.getVirtualPreload(user.id) ? "VPI" : "vpi");
- String harmfulAppWarning = ps.getHarmfulAppWarning(user.id);
+ pw.print(userState.isInstalled() ? "I" : "i");
+ pw.print(userState.isHidden() ? "B" : "b");
+ pw.print(userState.isSuspended() ? "SU" : "su");
+ pw.print(userState.isStopped() ? "S" : "s");
+ pw.print(userState.isNotLaunched() ? "l" : "L");
+ pw.print(userState.isInstantApp() ? "IA" : "ia");
+ pw.print(userState.isVirtualPreload() ? "VPI" : "vpi");
+ String harmfulAppWarning = userState.getHarmfulAppWarning();
pw.print(harmfulAppWarning != null ? "HA" : "ha");
pw.print(",");
- pw.print(ps.getEnabled(user.id));
- String lastDisabledAppCaller = ps.getLastDisabledAppCaller(user.id);
+ pw.print(userState.getEnabledState());
+ String lastDisabledAppCaller = userState.getLastDisableAppCaller();
pw.print(",");
pw.print(lastDisabledAppCaller != null ? lastDisabledAppCaller : "?");
pw.print(",");
@@ -4819,48 +4829,48 @@
}
for (UserInfo user : users) {
+ final PackageUserStateInternal userState = ps.getUserStateOrDefault(user.id);
pw.print(prefix); pw.print(" User "); pw.print(user.id); pw.print(": ");
pw.print("ceDataInode=");
- pw.print(ps.getCeDataInode(user.id));
+ pw.print(userState.getCeDataInode());
pw.print(" installed=");
- pw.print(ps.getInstalled(user.id));
+ pw.print(userState.isInstalled());
pw.print(" hidden=");
- pw.print(ps.getHidden(user.id));
+ pw.print(userState.isHidden());
pw.print(" suspended=");
- pw.print(ps.getSuspended(user.id));
+ pw.print(userState.isSuspended());
pw.print(" distractionFlags=");
- pw.print(ps.getDistractionFlags(user.id));
+ pw.print(userState.getDistractionFlags());
pw.print(" stopped=");
- pw.print(ps.getStopped(user.id));
+ pw.print(userState.isStopped());
pw.print(" notLaunched=");
- pw.print(ps.getNotLaunched(user.id));
+ pw.print(userState.isNotLaunched());
pw.print(" enabled=");
- pw.print(ps.getEnabled(user.id));
+ pw.print(userState.getEnabledState());
pw.print(" instant=");
- pw.print(ps.getInstantApp(user.id));
+ pw.print(userState.isInstantApp());
pw.print(" virtual=");
- pw.print(ps.getVirtualPreload(user.id));
+ pw.println(userState.isVirtualPreload());
pw.print(" installReason=");
- pw.println(ps.getInstallReason(user.id));
+ pw.println(userState.getInstallReason());
- if (ps.getSuspended(user.id)) {
+ if (userState.isSuspended()) {
pw.print(prefix);
pw.println(" Suspend params:");
- final PackageUserStateInternal pus = ps.readUserState(user.id);
- for (int i = 0; i < pus.getSuspendParams().size(); i++) {
+ for (int i = 0; i < userState.getSuspendParams().size(); i++) {
pw.print(prefix);
pw.print(" suspendingPackage=");
- pw.print(pus.getSuspendParams().keyAt(i));
- final SuspendParams params = pus.getSuspendParams().valueAt(i);
+ pw.print(userState.getSuspendParams().keyAt(i));
+ final SuspendParams params = userState.getSuspendParams().valueAt(i);
if (params != null) {
pw.print(" dialogInfo=");
- pw.print(params.dialogInfo);
+ pw.print(params.getDialogInfo());
}
pw.println();
}
}
- final OverlayPaths overlayPaths = ps.getOverlayPaths(user.id);
+ final OverlayPaths overlayPaths = userState.getOverlayPaths();
if (overlayPaths != null) {
if (!overlayPaths.getOverlayPaths().isEmpty()) {
pw.print(prefix);
@@ -4883,7 +4893,7 @@
}
final Map<String, OverlayPaths> sharedLibraryOverlayPaths =
- ps.getOverlayPathsForLibrary(user.id);
+ userState.getSharedLibraryOverlayPaths();
if (sharedLibraryOverlayPaths != null) {
for (Map.Entry<String, OverlayPaths> libOverlayPaths :
sharedLibraryOverlayPaths.entrySet()) {
@@ -4916,7 +4926,7 @@
}
}
- String lastDisabledAppCaller = ps.getLastDisabledAppCaller(user.id);
+ String lastDisabledAppCaller = userState.getLastDisableAppCaller();
if (lastDisabledAppCaller != null) {
pw.print(prefix); pw.print(" lastDisabledCaller: ");
pw.println(lastDisabledAppCaller);
@@ -4929,21 +4939,21 @@
.getPermissionStates(user.id), dumpAll);
}
- String harmfulAppWarning = ps.getHarmfulAppWarning(user.id);
+ String harmfulAppWarning = userState.getHarmfulAppWarning();
if (harmfulAppWarning != null) {
pw.print(prefix); pw.print(" harmfulAppWarning: ");
pw.println(harmfulAppWarning);
}
if (permissionNames == null) {
- ArraySet<String> cmp = ps.getDisabledComponents(user.id);
+ Set<String> cmp = userState.getDisabledComponents();
if (cmp != null && cmp.size() > 0) {
pw.print(prefix); pw.println(" disabledComponents:");
for (String s : cmp) {
pw.print(prefix); pw.print(" "); pw.println(s);
}
}
- cmp = ps.getEnabledComponents(user.id);
+ cmp = userState.getEnabledComponents();
if (cmp != null && cmp.size() > 0) {
pw.print(prefix); pw.println(" enabledComponents:");
for (String s : cmp) {
@@ -5291,9 +5301,10 @@
public void writePermissionStateForUserLPr(int userId, boolean sync) {
if (sync) {
- mRuntimePermissionsPersistence.writeStateForUserSyncLPr(userId);
+ mRuntimePermissionsPersistence.writeStateForUserSync(userId, mPermissionDataProvider,
+ mPackages, mSharedUsers);
} else {
- mRuntimePermissionsPersistence.writeStateForUserAsyncLPr(userId);
+ mRuntimePermissionsPersistence.writeStateForUserAsync(userId);
}
}
@@ -5368,7 +5379,7 @@
}
}
- private final class RuntimePermissionPersistence {
+ private static final class RuntimePermissionPersistence {
private static final long WRITE_PERMISSIONS_DELAY_MILLIS = 200;
private static final long MAX_WRITE_PERMISSIONS_DELAY_MILLIS = 2000;
@@ -5381,6 +5392,8 @@
private final Handler mHandler = new MyHandler();
+ private final Object mLock = new Object();
+
@GuardedBy("mLock")
private final SparseBooleanArray mWriteScheduled = new SparseBooleanArray();
@@ -5400,45 +5413,58 @@
// The mapping keys are user ids.
private final SparseBooleanArray mPermissionUpgradeNeeded = new SparseBooleanArray();
- public RuntimePermissionPersistence(RuntimePermissionsPersistence persistence) {
+ // This is a hack to allow this class to invoke a write using Settings's data structures,
+ // to facilitate moving to a finer scoped lock without a significant refactor.
+ private final Consumer<Integer> mInvokeWriteUserStateAsyncCallback;
+
+ public RuntimePermissionPersistence(RuntimePermissionsPersistence persistence,
+ Consumer<Integer> invokeWriteUserStateAsyncCallback) {
mPersistence = persistence;
+ mInvokeWriteUserStateAsyncCallback = invokeWriteUserStateAsyncCallback;
}
- @GuardedBy("Settings.this.mLock")
- int getVersionLPr(int userId) {
- return mVersions.get(userId, INITIAL_VERSION);
- }
-
- @GuardedBy("Settings.this.mLock")
- void setVersionLPr(int version, int userId) {
- mVersions.put(userId, version);
- writeStateForUserAsyncLPr(userId);
- }
-
- @GuardedBy("Settings.this.mLock")
- public boolean isPermissionUpgradeNeeded(int userId) {
- return mPermissionUpgradeNeeded.get(userId, true);
- }
-
- @GuardedBy("Settings.this.mLock")
- public void updateRuntimePermissionsFingerprintLPr(@UserIdInt int userId) {
- if (mExtendedFingerprint == null) {
- throw new RuntimeException("The version of the permission controller hasn't been "
- + "set before trying to update the fingerprint.");
+ int getVersion(int userId) {
+ synchronized (mLock) {
+ return mVersions.get(userId, INITIAL_VERSION);
}
- mFingerprints.put(userId, mExtendedFingerprint);
- writeStateForUserAsyncLPr(userId);
+ }
+
+ void setVersion(int version, int userId) {
+ synchronized (mLock) {
+ mVersions.put(userId, version);
+ writeStateForUserAsync(userId);
+ }
+ }
+
+ public boolean isPermissionUpgradeNeeded(int userId) {
+ synchronized (mLock) {
+ return mPermissionUpgradeNeeded.get(userId, true);
+ }
+ }
+
+ public void updateRuntimePermissionsFingerprint(@UserIdInt int userId) {
+ synchronized (mLock) {
+ if (mExtendedFingerprint == null) {
+ throw new RuntimeException(
+ "The version of the permission controller hasn't been "
+ + "set before trying to update the fingerprint.");
+ }
+ mFingerprints.put(userId, mExtendedFingerprint);
+ writeStateForUserAsync(userId);
+ }
}
public void setPermissionControllerVersion(long version) {
- int numUser = mFingerprints.size();
- mExtendedFingerprint = getExtendedFingerprint(version);
+ synchronized (mLock) {
+ int numUser = mFingerprints.size();
+ mExtendedFingerprint = getExtendedFingerprint(version);
- for (int i = 0; i < numUser; i++) {
- int userId = mFingerprints.keyAt(i);
- String fingerprint = mFingerprints.valueAt(i);
- mPermissionUpgradeNeeded.put(userId,
- !TextUtils.equals(mExtendedFingerprint, fingerprint));
+ for (int i = 0; i < numUser; i++) {
+ int userId = mFingerprints.keyAt(i);
+ String fingerprint = mFingerprints.valueAt(i);
+ mPermissionUpgradeNeeded.put(userId,
+ !TextUtils.equals(mExtendedFingerprint, fingerprint));
+ }
}
}
@@ -5446,84 +5472,92 @@
return PackagePartitions.FINGERPRINT + "?pc_version=" + version;
}
- public void writeStateForUserAsyncLPr(int userId) {
- final long currentTimeMillis = SystemClock.uptimeMillis();
+ public void writeStateForUserAsync(int userId) {
+ synchronized (mLock) {
+ final long currentTimeMillis = SystemClock.uptimeMillis();
- if (mWriteScheduled.get(userId)) {
- mHandler.removeMessages(userId);
+ if (mWriteScheduled.get(userId)) {
+ mHandler.removeMessages(userId);
- // If enough time passed, write without holding off anymore.
- final long lastNotWrittenMutationTimeMillis = mLastNotWrittenMutationTimesMillis
- .get(userId);
- final long timeSinceLastNotWrittenMutationMillis = currentTimeMillis
- - lastNotWrittenMutationTimeMillis;
- if (timeSinceLastNotWrittenMutationMillis >= MAX_WRITE_PERMISSIONS_DELAY_MILLIS) {
- mHandler.obtainMessage(userId).sendToTarget();
- return;
+ // If enough time passed, write without holding off anymore.
+ final long lastNotWrittenMutationTimeMillis = mLastNotWrittenMutationTimesMillis
+ .get(userId);
+ final long timeSinceLastNotWrittenMutationMillis = currentTimeMillis
+ - lastNotWrittenMutationTimeMillis;
+ if (timeSinceLastNotWrittenMutationMillis
+ >= MAX_WRITE_PERMISSIONS_DELAY_MILLIS) {
+ mHandler.obtainMessage(userId).sendToTarget();
+ return;
+ }
+
+ // Hold off a bit more as settings are frequently changing.
+ final long maxDelayMillis = Math.max(lastNotWrittenMutationTimeMillis
+ + MAX_WRITE_PERMISSIONS_DELAY_MILLIS - currentTimeMillis, 0);
+ final long writeDelayMillis = Math.min(WRITE_PERMISSIONS_DELAY_MILLIS,
+ maxDelayMillis);
+
+ Message message = mHandler.obtainMessage(userId);
+ mHandler.sendMessageDelayed(message, writeDelayMillis);
+ } else {
+ mLastNotWrittenMutationTimesMillis.put(userId, currentTimeMillis);
+ Message message = mHandler.obtainMessage(userId);
+ mHandler.sendMessageDelayed(message, WRITE_PERMISSIONS_DELAY_MILLIS);
+ mWriteScheduled.put(userId, true);
}
-
- // Hold off a bit more as settings are frequently changing.
- final long maxDelayMillis = Math.max(lastNotWrittenMutationTimeMillis
- + MAX_WRITE_PERMISSIONS_DELAY_MILLIS - currentTimeMillis, 0);
- final long writeDelayMillis = Math.min(WRITE_PERMISSIONS_DELAY_MILLIS,
- maxDelayMillis);
-
- Message message = mHandler.obtainMessage(userId);
- mHandler.sendMessageDelayed(message, writeDelayMillis);
- } else {
- mLastNotWrittenMutationTimesMillis.put(userId, currentTimeMillis);
- Message message = mHandler.obtainMessage(userId);
- mHandler.sendMessageDelayed(message, WRITE_PERMISSIONS_DELAY_MILLIS);
- mWriteScheduled.put(userId, true);
}
}
- public void writeStateForUserSyncLPr(int userId) {
- mHandler.removeMessages(userId);
- mWriteScheduled.delete(userId);
+ public void writeStateForUserSync(int userId, @NonNull LegacyPermissionDataProvider
+ legacyPermissionDataProvider,
+ @NonNull WatchedArrayMap<String, ? extends PackageStateInternal> packageStates,
+ @NonNull WatchedArrayMap<String, SharedUserSetting> sharedUsers) {
+ synchronized (mLock) {
+ mHandler.removeMessages(userId);
+ mWriteScheduled.delete(userId);
- mPermissionDataProvider.writeLegacyPermissionStateTEMP();
+ legacyPermissionDataProvider.writeLegacyPermissionStateTEMP();
- int version = mVersions.get(userId, INITIAL_VERSION);
+ int version = mVersions.get(userId, INITIAL_VERSION);
- String fingerprint = mFingerprints.get(userId);
+ String fingerprint = mFingerprints.get(userId);
- Map<String, List<RuntimePermissionsState.PermissionState>> packagePermissions =
- new ArrayMap<>();
- int packagesSize = mPackages.size();
- for (int i = 0; i < packagesSize; i++) {
- String packageName = mPackages.keyAt(i);
- PackageSetting packageSetting = mPackages.valueAt(i);
- if (packageSetting.getSharedUser() == null) {
+ Map<String, List<RuntimePermissionsState.PermissionState>> packagePermissions =
+ new ArrayMap<>();
+ int packagesSize = packageStates.size();
+ for (int i = 0; i < packagesSize; i++) {
+ String packageName = packageStates.keyAt(i);
+ PackageStateInternal packageState = packageStates.valueAt(i);
+ if (packageState.getSharedUser() == null) {
+ List<RuntimePermissionsState.PermissionState> permissions =
+ getPermissionsFromPermissionsState(
+ packageState.getLegacyPermissionState(), userId);
+ if (permissions.isEmpty() && !packageState.isInstallPermissionsFixed()) {
+ // Storing an empty state means the package is known to the system and
+ // its install permissions have been granted and fixed. If this is not
+ // the case, we should not store anything.
+ continue;
+ }
+ packagePermissions.put(packageName, permissions);
+ }
+ }
+
+ Map<String, List<RuntimePermissionsState.PermissionState>> sharedUserPermissions =
+ new ArrayMap<>();
+ final int sharedUsersSize = sharedUsers.size();
+ for (int i = 0; i < sharedUsersSize; i++) {
+ String sharedUserName = sharedUsers.keyAt(i);
+ SharedUserSetting sharedUserSetting = sharedUsers.valueAt(i);
List<RuntimePermissionsState.PermissionState> permissions =
getPermissionsFromPermissionsState(
- packageSetting.getLegacyPermissionState(), userId);
- if (permissions.isEmpty() && !packageSetting.isInstallPermissionsFixed()) {
- // Storing an empty state means the package is known to the system and its
- // install permissions have been granted and fixed. If this is not the case,
- // we should not store anything.
- continue;
- }
- packagePermissions.put(packageName, permissions);
+ sharedUserSetting.getLegacyPermissionState(), userId);
+ sharedUserPermissions.put(sharedUserName, permissions);
}
+
+ RuntimePermissionsState runtimePermissions = new RuntimePermissionsState(version,
+ fingerprint, packagePermissions, sharedUserPermissions);
+
+ mPersistence.writeForUser(runtimePermissions, UserHandle.of(userId));
}
-
- Map<String, List<RuntimePermissionsState.PermissionState>> sharedUserPermissions =
- new ArrayMap<>();
- final int sharedUsersSize = mSharedUsers.size();
- for (int i = 0; i < sharedUsersSize; i++) {
- String sharedUserName = mSharedUsers.keyAt(i);
- SharedUserSetting sharedUserSetting = mSharedUsers.valueAt(i);
- List<RuntimePermissionsState.PermissionState> permissions =
- getPermissionsFromPermissionsState(
- sharedUserSetting.getLegacyPermissionState(), userId);
- sharedUserPermissions.put(sharedUserName, permissions);
- }
-
- RuntimePermissionsState runtimePermissions = new RuntimePermissionsState(version,
- fingerprint, packagePermissions, sharedUserPermissions);
-
- mPersistence.writeForUser(runtimePermissions, UserHandle.of(userId));
}
@NonNull
@@ -5541,82 +5575,91 @@
return permissions;
}
- @GuardedBy("Settings.this.mLock")
- private void onUserRemovedLPw(int userId) {
- // Make sure we do not
- mHandler.removeMessages(userId);
+ private void onUserRemoved(int userId) {
+ synchronized (mLock) {
+ // Make sure we do not
+ mHandler.removeMessages(userId);
- mPermissionUpgradeNeeded.delete(userId);
- mVersions.delete(userId);
- mFingerprints.remove(userId);
+ mPermissionUpgradeNeeded.delete(userId);
+ mVersions.delete(userId);
+ mFingerprints.remove(userId);
+ }
}
public void deleteUserRuntimePermissionsFile(int userId) {
- mPersistence.deleteForUser(UserHandle.of(userId));
+ synchronized (mLock) {
+ mPersistence.deleteForUser(UserHandle.of(userId));
+ }
}
- @GuardedBy("Settings.this.mLock")
- public void readStateForUserSyncLPr(int userId) {
- RuntimePermissionsState runtimePermissions = mPersistence.readForUser(UserHandle.of(
- userId));
- if (runtimePermissions == null) {
- readLegacyStateForUserSyncLPr(userId);
- writeStateForUserAsyncLPr(userId);
- return;
- }
-
- // If the runtime permissions file exists but the version is not set this is
- // an upgrade from P->Q. Hence mark it with the special UPGRADE_VERSION.
- int version = runtimePermissions.getVersion();
- if (version == RuntimePermissionsState.NO_VERSION) {
- version = UPGRADE_VERSION;
- }
- mVersions.put(userId, version);
-
- String fingerprint = runtimePermissions.getFingerprint();
- mFingerprints.put(userId, fingerprint);
-
- boolean isUpgradeToR = getInternalVersion().sdkVersion < Build.VERSION_CODES.R;
-
- Map<String, List<RuntimePermissionsState.PermissionState>> packagePermissions =
- runtimePermissions.getPackagePermissions();
- int packagesSize = mPackages.size();
- for (int i = 0; i < packagesSize; i++) {
- String packageName = mPackages.keyAt(i);
- PackageSetting packageSetting = mPackages.valueAt(i);
-
- List<RuntimePermissionsState.PermissionState> permissions =
- packagePermissions.get(packageName);
- if (permissions != null) {
- readPermissionsStateLpr(permissions, packageSetting.getLegacyPermissionState(),
- userId);
- packageSetting.setInstallPermissionsFixed(true);
- } else if (packageSetting.getSharedUser() == null && !isUpgradeToR) {
- Slog.w(TAG, "Missing permission state for package: " + packageName);
- packageSetting.getLegacyPermissionState().setMissing(true, userId);
+ public void readStateForUserSync(int userId, @NonNull VersionInfo internalVersion,
+ @NonNull WatchedArrayMap<String, PackageSetting> packageSettings,
+ @NonNull WatchedArrayMap<String, SharedUserSetting> sharedUsers,
+ @NonNull File userRuntimePermissionsFile) {
+ synchronized (mLock) {
+ RuntimePermissionsState runtimePermissions = mPersistence.readForUser(UserHandle.of(
+ userId));
+ if (runtimePermissions == null) {
+ readLegacyStateForUserSync(userId, userRuntimePermissionsFile, packageSettings,
+ sharedUsers);
+ writeStateForUserAsync(userId);
+ return;
}
- }
- Map<String, List<RuntimePermissionsState.PermissionState>> sharedUserPermissions =
- runtimePermissions.getSharedUserPermissions();
- int sharedUsersSize = mSharedUsers.size();
- for (int i = 0; i < sharedUsersSize; i++) {
- String sharedUserName = mSharedUsers.keyAt(i);
- SharedUserSetting sharedUserSetting = mSharedUsers.valueAt(i);
+ // If the runtime permissions file exists but the version is not set this is
+ // an upgrade from P->Q. Hence mark it with the special UPGRADE_VERSION.
+ int version = runtimePermissions.getVersion();
+ if (version == RuntimePermissionsState.NO_VERSION) {
+ version = UPGRADE_VERSION;
+ }
+ mVersions.put(userId, version);
- List<RuntimePermissionsState.PermissionState> permissions =
- sharedUserPermissions.get(sharedUserName);
- if (permissions != null) {
- readPermissionsStateLpr(permissions,
- sharedUserSetting.getLegacyPermissionState(), userId);
- } else if (!isUpgradeToR) {
- Slog.w(TAG, "Missing permission state for shared user: " + sharedUserName);
- sharedUserSetting.getLegacyPermissionState().setMissing(true, userId);
+ String fingerprint = runtimePermissions.getFingerprint();
+ mFingerprints.put(userId, fingerprint);
+
+ boolean isUpgradeToR = internalVersion.sdkVersion < Build.VERSION_CODES.R;
+
+ Map<String, List<RuntimePermissionsState.PermissionState>> packagePermissions =
+ runtimePermissions.getPackagePermissions();
+ int packagesSize = packageSettings.size();
+ for (int i = 0; i < packagesSize; i++) {
+ String packageName = packageSettings.keyAt(i);
+ PackageSetting packageSetting = packageSettings.valueAt(i);
+
+ List<RuntimePermissionsState.PermissionState> permissions =
+ packagePermissions.get(packageName);
+ if (permissions != null) {
+ readPermissionsState(permissions,
+ packageSetting.getLegacyPermissionState(),
+ userId);
+ packageSetting.setInstallPermissionsFixed(true);
+ } else if (packageSetting.getSharedUser() == null && !isUpgradeToR) {
+ Slog.w(TAG, "Missing permission state for package: " + packageName);
+ packageSetting.getLegacyPermissionState().setMissing(true, userId);
+ }
+ }
+
+ Map<String, List<RuntimePermissionsState.PermissionState>> sharedUserPermissions =
+ runtimePermissions.getSharedUserPermissions();
+ int sharedUsersSize = sharedUsers.size();
+ for (int i = 0; i < sharedUsersSize; i++) {
+ String sharedUserName = sharedUsers.keyAt(i);
+ SharedUserSetting sharedUserSetting = sharedUsers.valueAt(i);
+
+ List<RuntimePermissionsState.PermissionState> permissions =
+ sharedUserPermissions.get(sharedUserName);
+ if (permissions != null) {
+ readPermissionsState(permissions,
+ sharedUserSetting.getLegacyPermissionState(), userId);
+ } else if (!isUpgradeToR) {
+ Slog.w(TAG, "Missing permission state for shared user: " + sharedUserName);
+ sharedUserSetting.getLegacyPermissionState().setMissing(true, userId);
+ }
}
}
}
- private void readPermissionsStateLpr(
+ private void readPermissionsState(
@NonNull List<RuntimePermissionsState.PermissionState> permissions,
@NonNull LegacyPermissionState permissionsState, @UserIdInt int userId) {
int permissionsSize = permissions.size();
@@ -5630,77 +5673,86 @@
}
}
- @GuardedBy("Settings.this.mLock")
- private void readLegacyStateForUserSyncLPr(int userId) {
- File permissionsFile = getUserRuntimePermissionsFile(userId);
- if (!permissionsFile.exists()) {
- return;
- }
+ private void readLegacyStateForUserSync(int userId, @NonNull File permissionsFile,
+ @NonNull WatchedArrayMap<String, ? extends PackageStateInternal> packageStates,
+ @NonNull WatchedArrayMap<String, SharedUserSetting> sharedUsers) {
+ synchronized (mLock) {
+ if (!permissionsFile.exists()) {
+ return;
+ }
- FileInputStream in;
- try {
- in = new AtomicFile(permissionsFile).openRead();
- } catch (FileNotFoundException fnfe) {
- Slog.i(PackageManagerService.TAG, "No permissions state");
- return;
- }
+ FileInputStream in;
+ try {
+ in = new AtomicFile(permissionsFile).openRead();
+ } catch (FileNotFoundException fnfe) {
+ Slog.i(PackageManagerService.TAG, "No permissions state");
+ return;
+ }
- try {
- final TypedXmlPullParser parser = Xml.resolvePullParser(in);
- parseLegacyRuntimePermissionsLPr(parser, userId);
+ try {
+ final TypedXmlPullParser parser = Xml.resolvePullParser(in);
+ parseLegacyRuntimePermissions(parser, userId, packageStates, sharedUsers);
- } catch (XmlPullParserException | IOException e) {
- throw new IllegalStateException("Failed parsing permissions file: "
- + permissionsFile, e);
- } finally {
- IoUtils.closeQuietly(in);
+ } catch (XmlPullParserException | IOException e) {
+ throw new IllegalStateException("Failed parsing permissions file: "
+ + permissionsFile, e);
+ } finally {
+ IoUtils.closeQuietly(in);
+ }
}
}
- // Private internals
-
- @GuardedBy("Settings.this.mLock")
- private void parseLegacyRuntimePermissionsLPr(TypedXmlPullParser parser, int userId)
+ private void parseLegacyRuntimePermissions(TypedXmlPullParser parser, int userId,
+ @NonNull WatchedArrayMap<String, ? extends PackageStateInternal> packageStates,
+ @NonNull WatchedArrayMap<String, SharedUserSetting> sharedUsers)
throws IOException, XmlPullParserException {
- final int outerDepth = parser.getDepth();
- int type;
- while ((type = parser.next()) != XmlPullParser.END_DOCUMENT
- && (type != XmlPullParser.END_TAG || parser.getDepth() > outerDepth)) {
- if (type == XmlPullParser.END_TAG || type == XmlPullParser.TEXT) {
- continue;
- }
+ synchronized (mLock) {
+ final int outerDepth = parser.getDepth();
+ int type;
+ while ((type = parser.next()) != XmlPullParser.END_DOCUMENT
+ && (type != XmlPullParser.END_TAG || parser.getDepth() > outerDepth)) {
+ if (type == XmlPullParser.END_TAG || type == XmlPullParser.TEXT) {
+ continue;
+ }
- switch (parser.getName()) {
- case TAG_RUNTIME_PERMISSIONS: {
- // If the permisions settings file exists but the version is not set this is
- // an upgrade from P->Q. Hence mark it with the special UPGRADE_VERSION
- int version = parser.getAttributeInt(null, ATTR_VERSION, UPGRADE_VERSION);
- mVersions.put(userId, version);
- String fingerprint = parser.getAttributeValue(null, ATTR_FINGERPRINT);
- mFingerprints.put(userId, fingerprint);
- } break;
-
- case TAG_PACKAGE: {
- String name = parser.getAttributeValue(null, ATTR_NAME);
- PackageSetting ps = mPackages.get(name);
- if (ps == null) {
- Slog.w(PackageManagerService.TAG, "Unknown package:" + name);
- XmlUtils.skipCurrentTag(parser);
- continue;
+ switch (parser.getName()) {
+ case TAG_RUNTIME_PERMISSIONS: {
+ // If the permisions settings file exists but the version is not set this is
+ // an upgrade from P->Q. Hence mark it with the special UPGRADE_VERSION
+ int version = parser.getAttributeInt(null, ATTR_VERSION,
+ UPGRADE_VERSION);
+ mVersions.put(userId, version);
+ String fingerprint = parser.getAttributeValue(null, ATTR_FINGERPRINT);
+ mFingerprints.put(userId, fingerprint);
}
- parseLegacyPermissionsLPr(parser, ps.getLegacyPermissionState(), userId);
- } break;
+ break;
- case TAG_SHARED_USER: {
- String name = parser.getAttributeValue(null, ATTR_NAME);
- SharedUserSetting sus = mSharedUsers.get(name);
- if (sus == null) {
- Slog.w(PackageManagerService.TAG, "Unknown shared user:" + name);
- XmlUtils.skipCurrentTag(parser);
- continue;
+ case TAG_PACKAGE: {
+ String name = parser.getAttributeValue(null, ATTR_NAME);
+ PackageStateInternal ps = packageStates.get(name);
+ if (ps == null) {
+ Slog.w(PackageManagerService.TAG, "Unknown package:" + name);
+ XmlUtils.skipCurrentTag(parser);
+ continue;
+ }
+ parseLegacyPermissionsLPr(parser, ps.getLegacyPermissionState(),
+ userId);
}
- parseLegacyPermissionsLPr(parser, sus.getLegacyPermissionState(), userId);
- } break;
+ break;
+
+ case TAG_SHARED_USER: {
+ String name = parser.getAttributeValue(null, ATTR_NAME);
+ SharedUserSetting sus = sharedUsers.get(name);
+ if (sus == null) {
+ Slog.w(PackageManagerService.TAG, "Unknown shared user:" + name);
+ XmlUtils.skipCurrentTag(parser);
+ continue;
+ }
+ parseLegacyPermissionsLPr(parser, sus.getLegacyPermissionState(),
+ userId);
+ }
+ break;
+ }
}
}
}
@@ -5708,25 +5760,27 @@
private void parseLegacyPermissionsLPr(TypedXmlPullParser parser,
LegacyPermissionState permissionsState, int userId)
throws IOException, XmlPullParserException {
- final int outerDepth = parser.getDepth();
- int type;
- while ((type = parser.next()) != XmlPullParser.END_DOCUMENT
- && (type != XmlPullParser.END_TAG || parser.getDepth() > outerDepth)) {
- if (type == XmlPullParser.END_TAG || type == XmlPullParser.TEXT) {
- continue;
- }
-
- switch (parser.getName()) {
- case TAG_ITEM: {
- String name = parser.getAttributeValue(null, ATTR_NAME);
- final boolean granted =
- parser.getAttributeBoolean(null, ATTR_GRANTED, true);
- final int flags =
- parser.getAttributeIntHex(null, ATTR_FLAGS, 0);
- permissionsState.putPermissionState(new PermissionState(name, true,
- granted, flags), userId);
+ synchronized (mLock) {
+ final int outerDepth = parser.getDepth();
+ int type;
+ while ((type = parser.next()) != XmlPullParser.END_DOCUMENT
+ && (type != XmlPullParser.END_TAG || parser.getDepth() > outerDepth)) {
+ if (type == XmlPullParser.END_TAG || type == XmlPullParser.TEXT) {
+ continue;
}
- break;
+
+ switch (parser.getName()) {
+ case TAG_ITEM: {
+ String name = parser.getAttributeValue(null, ATTR_NAME);
+ final boolean granted =
+ parser.getAttributeBoolean(null, ATTR_GRANTED, true);
+ final int flags =
+ parser.getAttributeIntHex(null, ATTR_FLAGS, 0);
+ permissionsState.putPermissionState(new PermissionState(name, true,
+ granted, flags), userId);
+ }
+ break;
+ }
}
}
}
@@ -5740,9 +5794,7 @@
public void handleMessage(Message message) {
final int userId = message.what;
Runnable callback = (Runnable) message.obj;
- synchronized (mLock) {
- writeStateForUserSyncLPr(userId);
- }
+ mInvokeWriteUserStateAsyncCallback.accept(userId);
if (callback != null) {
callback.run();
}
diff --git a/services/core/java/com/android/server/pm/pkg/PackageStateInternal.java b/services/core/java/com/android/server/pm/pkg/PackageStateInternal.java
index 5460afa..56f62ab 100644
--- a/services/core/java/com/android/server/pm/pkg/PackageStateInternal.java
+++ b/services/core/java/com/android/server/pm/pkg/PackageStateInternal.java
@@ -23,6 +23,7 @@
import android.util.SparseArray;
import com.android.server.pm.InstallSource;
+import com.android.server.pm.PackageKeySetData;
import com.android.server.pm.SharedUserSetting;
import com.android.server.pm.parsing.pkg.AndroidPackage;
import com.android.server.pm.permission.LegacyPermissionState;
@@ -82,4 +83,7 @@
String getPathString();
float getLoadingProgress();
+
+ @NonNull
+ PackageKeySetData getKeySetData();
}
diff --git a/services/core/java/com/android/server/pm/pkg/PackageUserStateImpl.java b/services/core/java/com/android/server/pm/pkg/PackageUserStateImpl.java
index 32a9cf1..6fafb24 100644
--- a/services/core/java/com/android/server/pm/pkg/PackageUserStateImpl.java
+++ b/services/core/java/com/android/server/pm/pkg/PackageUserStateImpl.java
@@ -28,6 +28,7 @@
import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.util.ArrayUtils;
+import com.android.internal.util.CollectionUtils;
import com.android.internal.util.DataClass;
import java.util.Objects;
@@ -49,7 +50,6 @@
private boolean mNotLaunched;
private boolean mHidden; // Is the app restricted by owner / admin
private int mDistractionFlags;
- private boolean mSuspended;
private boolean mInstantApp;
private boolean mVirtualPreload;
private int mEnabledState = PackageManager.COMPONENT_ENABLED_STATE_DEFAULT;
@@ -101,7 +101,6 @@
mNotLaunched = other.mNotLaunched;
mHidden = other.mHidden;
mDistractionFlags = other.mDistractionFlags;
- mSuspended = other.mSuspended;
mInstantApp = other.mInstantApp;
mVirtualPreload = other.mVirtualPreload;
mEnabledState = other.mEnabledState;
@@ -256,6 +255,28 @@
return mComponentLabelIconOverrideMap.get(componentName);
}
+ @Override
+ public boolean isSuspended() {
+ return !CollectionUtils.isEmpty(mSuspendParams);
+ }
+
+ public PackageUserStateImpl putSuspendParams(@NonNull String suspendingPackage,
+ @NonNull SuspendParams suspendParams) {
+ if (mSuspendParams == null) {
+ mSuspendParams = new ArrayMap<>();
+ }
+ mSuspendParams.put(suspendingPackage, suspendParams);
+ return this;
+ }
+
+ public PackageUserStateImpl removeSuspension(@NonNull String suspendingPackage) {
+ if (mSuspendParams != null) {
+ mSuspendParams.remove(suspendingPackage);
+ }
+ return this;
+ }
+
+
// Code below generated by codegen v1.0.23.
@@ -312,11 +333,6 @@
}
@DataClass.Generated.Member
- public boolean isSuspended() {
- return mSuspended;
- }
-
- @DataClass.Generated.Member
public boolean isInstantApp() {
return mInstantApp;
}
@@ -433,12 +449,6 @@
}
@DataClass.Generated.Member
- public @NonNull PackageUserStateImpl setSuspended( boolean value) {
- mSuspended = value;
- return this;
- }
-
- @DataClass.Generated.Member
public @NonNull PackageUserStateImpl setInstantApp( boolean value) {
mInstantApp = value;
return this;
@@ -532,7 +542,6 @@
&& mNotLaunched == that.mNotLaunched
&& mHidden == that.mHidden
&& mDistractionFlags == that.mDistractionFlags
- && mSuspended == that.mSuspended
&& mInstantApp == that.mInstantApp
&& mVirtualPreload == that.mVirtualPreload
&& mEnabledState == that.mEnabledState
@@ -563,7 +572,6 @@
_hash = 31 * _hash + Boolean.hashCode(mNotLaunched);
_hash = 31 * _hash + Boolean.hashCode(mHidden);
_hash = 31 * _hash + mDistractionFlags;
- _hash = 31 * _hash + Boolean.hashCode(mSuspended);
_hash = 31 * _hash + Boolean.hashCode(mInstantApp);
_hash = 31 * _hash + Boolean.hashCode(mVirtualPreload);
_hash = 31 * _hash + mEnabledState;
@@ -581,10 +589,10 @@
}
@DataClass.Generated(
- time = 1633983318771L,
+ time = 1636145886996L,
codegenVersion = "1.0.23",
sourceFile = "frameworks/base/services/core/java/com/android/server/pm/pkg/PackageUserStateImpl.java",
- inputSignatures = "protected @android.annotation.Nullable android.util.ArraySet<java.lang.String> mDisabledComponents\nprotected @android.annotation.Nullable android.util.ArraySet<java.lang.String> mEnabledComponents\nprivate long mCeDataInode\nprivate boolean mInstalled\nprivate boolean mStopped\nprivate boolean mNotLaunched\nprivate boolean mHidden\nprivate int mDistractionFlags\nprivate boolean mSuspended\nprivate boolean mInstantApp\nprivate boolean mVirtualPreload\nprivate int mEnabledState\nprivate @android.content.pm.PackageManager.InstallReason int mInstallReason\nprivate @android.content.pm.PackageManager.UninstallReason int mUninstallReason\nprivate @android.annotation.Nullable java.lang.String mHarmfulAppWarning\nprivate @android.annotation.Nullable java.lang.String mLastDisableAppCaller\nprotected @android.annotation.Nullable android.content.pm.overlay.OverlayPaths mOverlayPaths\nprotected @android.annotation.Nullable android.util.ArrayMap<java.lang.String,android.content.pm.overlay.OverlayPaths> mSharedLibraryOverlayPaths\nprivate @android.annotation.Nullable java.lang.String mSplashScreenTheme\nprivate @android.annotation.Nullable android.util.ArrayMap<java.lang.String,com.android.server.pm.pkg.SuspendParams> mSuspendParams\nprivate @android.annotation.Nullable android.content.pm.overlay.OverlayPaths mCachedOverlayPaths\nprivate @android.annotation.Nullable android.util.ArrayMap<android.content.ComponentName,android.util.Pair<java.lang.String,java.lang.Integer>> mComponentLabelIconOverrideMap\npublic @android.annotation.Nullable boolean setOverlayPaths(android.content.pm.overlay.OverlayPaths)\npublic boolean setSharedLibraryOverlayPaths(java.lang.String,android.content.pm.overlay.OverlayPaths)\npublic @android.annotation.Nullable @java.lang.Override android.util.ArraySet<java.lang.String> getDisabledComponentsNoCopy()\npublic @android.annotation.Nullable @java.lang.Override android.util.ArraySet<java.lang.String> getEnabledComponentsNoCopy()\npublic @java.lang.Override boolean isComponentEnabled(java.lang.String)\npublic @java.lang.Override boolean isComponentDisabled(java.lang.String)\npublic @java.lang.Override android.content.pm.overlay.OverlayPaths getAllOverlayPaths()\npublic @com.android.internal.annotations.VisibleForTesting boolean overrideLabelAndIcon(android.content.ComponentName,java.lang.String,java.lang.Integer)\npublic void resetOverrideComponentLabelIcon()\npublic @android.annotation.Nullable android.util.Pair<java.lang.String,java.lang.Integer> getOverrideLabelIconForComponent(android.content.ComponentName)\nclass PackageUserStateImpl extends java.lang.Object implements [com.android.server.pm.pkg.PackageUserStateInternal]\n@com.android.internal.util.DataClass(genConstructor=false, genBuilder=false, genEqualsHashCode=true)")
+ inputSignatures = "protected @android.annotation.Nullable android.util.ArraySet<java.lang.String> mDisabledComponents\nprotected @android.annotation.Nullable android.util.ArraySet<java.lang.String> mEnabledComponents\nprivate long mCeDataInode\nprivate boolean mInstalled\nprivate boolean mStopped\nprivate boolean mNotLaunched\nprivate boolean mHidden\nprivate int mDistractionFlags\nprivate boolean mInstantApp\nprivate boolean mVirtualPreload\nprivate int mEnabledState\nprivate @android.content.pm.PackageManager.InstallReason int mInstallReason\nprivate @android.content.pm.PackageManager.UninstallReason int mUninstallReason\nprivate @android.annotation.Nullable java.lang.String mHarmfulAppWarning\nprivate @android.annotation.Nullable java.lang.String mLastDisableAppCaller\nprotected @android.annotation.Nullable android.content.pm.overlay.OverlayPaths mOverlayPaths\nprotected @android.annotation.Nullable android.util.ArrayMap<java.lang.String,android.content.pm.overlay.OverlayPaths> mSharedLibraryOverlayPaths\nprivate @android.annotation.Nullable java.lang.String mSplashScreenTheme\nprivate @android.annotation.Nullable android.util.ArrayMap<java.lang.String,com.android.server.pm.pkg.SuspendParams> mSuspendParams\nprivate @android.annotation.Nullable android.content.pm.overlay.OverlayPaths mCachedOverlayPaths\nprivate @android.annotation.Nullable android.util.ArrayMap<android.content.ComponentName,android.util.Pair<java.lang.String,java.lang.Integer>> mComponentLabelIconOverrideMap\npublic @android.annotation.Nullable boolean setOverlayPaths(android.content.pm.overlay.OverlayPaths)\npublic boolean setSharedLibraryOverlayPaths(java.lang.String,android.content.pm.overlay.OverlayPaths)\npublic @android.annotation.Nullable @java.lang.Override android.util.ArraySet<java.lang.String> getDisabledComponentsNoCopy()\npublic @android.annotation.Nullable @java.lang.Override android.util.ArraySet<java.lang.String> getEnabledComponentsNoCopy()\npublic @java.lang.Override boolean isComponentEnabled(java.lang.String)\npublic @java.lang.Override boolean isComponentDisabled(java.lang.String)\npublic @java.lang.Override android.content.pm.overlay.OverlayPaths getAllOverlayPaths()\npublic @com.android.internal.annotations.VisibleForTesting boolean overrideLabelAndIcon(android.content.ComponentName,java.lang.String,java.lang.Integer)\npublic void resetOverrideComponentLabelIcon()\npublic @android.annotation.Nullable android.util.Pair<java.lang.String,java.lang.Integer> getOverrideLabelIconForComponent(android.content.ComponentName)\npublic @java.lang.Override boolean isSuspended()\npublic com.android.server.pm.pkg.PackageUserStateImpl putSuspendParams(java.lang.String,com.android.server.pm.pkg.SuspendParams)\npublic com.android.server.pm.pkg.PackageUserStateImpl removeSuspension(java.lang.String)\nclass PackageUserStateImpl extends java.lang.Object implements [com.android.server.pm.pkg.PackageUserStateInternal]\n@com.android.internal.util.DataClass(genConstructor=false, genBuilder=false, genEqualsHashCode=true)")
@Deprecated
private void __metadata() {}
diff --git a/services/core/java/com/android/server/pm/pkg/PackageUserStateInternal.java b/services/core/java/com/android/server/pm/pkg/PackageUserStateInternal.java
index 6f33312..bd8b3ab 100644
--- a/services/core/java/com/android/server/pm/pkg/PackageUserStateInternal.java
+++ b/services/core/java/com/android/server/pm/pkg/PackageUserStateInternal.java
@@ -32,6 +32,7 @@
PackageUserStateInternal DEFAULT = new PackageUserStateDefault();
+ // TODO: Make non-null with emptyMap()
@Nullable
ArrayMap<String, SuspendParams> getSuspendParams();
diff --git a/services/core/java/com/android/server/pm/pkg/SuspendParams.java b/services/core/java/com/android/server/pm/pkg/SuspendParams.java
index 71512dc..d24ce96 100644
--- a/services/core/java/com/android/server/pm/pkg/SuspendParams.java
+++ b/services/core/java/com/android/server/pm/pkg/SuspendParams.java
@@ -42,11 +42,15 @@
private static final String TAG_APP_EXTRAS = "app-extras";
private static final String TAG_LAUNCHER_EXTRAS = "launcher-extras";
- public SuspendDialogInfo dialogInfo;
- public PersistableBundle appExtras;
- public PersistableBundle launcherExtras;
+ private final SuspendDialogInfo dialogInfo;
+ private final PersistableBundle appExtras;
+ private final PersistableBundle launcherExtras;
- private SuspendParams() {
+ private SuspendParams(SuspendDialogInfo dialogInfo, PersistableBundle appExtras,
+ PersistableBundle launcherExtras) {
+ this.dialogInfo = dialogInfo;
+ this.appExtras = appExtras;
+ this.launcherExtras = launcherExtras;
}
/**
@@ -60,11 +64,7 @@
if (dialogInfo == null && appExtras == null && launcherExtras == null) {
return null;
}
- final SuspendParams instance = new SuspendParams();
- instance.dialogInfo = dialogInfo;
- instance.appExtras = appExtras;
- instance.launcherExtras = launcherExtras;
- return instance;
+ return new SuspendParams(dialogInfo, appExtras, launcherExtras);
}
@Override
@@ -172,4 +172,16 @@
}
return getInstanceOrNull(readDialogInfo, readAppExtras, readLauncherExtras);
}
+
+ public SuspendDialogInfo getDialogInfo() {
+ return dialogInfo;
+ }
+
+ public PersistableBundle getAppExtras() {
+ return appExtras;
+ }
+
+ public PersistableBundle getLauncherExtras() {
+ return launcherExtras;
+ }
}
diff --git a/services/core/java/com/android/server/pm/pkg/mutate/PackageStateMutator.java b/services/core/java/com/android/server/pm/pkg/mutate/PackageStateMutator.java
new file mode 100644
index 0000000..35d4d9e
--- /dev/null
+++ b/services/core/java/com/android/server/pm/pkg/mutate/PackageStateMutator.java
@@ -0,0 +1,333 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.pm.pkg.mutate;
+
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.content.pm.ApplicationInfo;
+import android.content.pm.PackageManager;
+import android.content.pm.overlay.OverlayPaths;
+import android.util.ArraySet;
+
+import com.android.server.pm.PackageSetting;
+import com.android.server.pm.pkg.PackageUserStateImpl;
+import com.android.server.pm.pkg.SuspendParams;
+
+import java.util.concurrent.atomic.AtomicLong;
+import java.util.function.Function;
+
+public class PackageStateMutator {
+
+ private static final AtomicLong sStateChangeSequence = new AtomicLong();
+
+ private final StateWriteWrapper mStateWrite = new StateWriteWrapper();
+
+ private final Function<String, PackageSetting> mActiveStateFunction;
+ private final Function<String, PackageSetting> mDisabledStateFunction;
+
+ public PackageStateMutator(@NonNull Function<String, PackageSetting> activeStateFunction,
+ @NonNull Function<String, PackageSetting> disabledStateFunction) {
+ mActiveStateFunction = activeStateFunction;
+ mDisabledStateFunction = disabledStateFunction;
+ }
+
+ public static void onPackageStateChanged() {
+ sStateChangeSequence.incrementAndGet();
+ }
+
+ @NonNull
+ public PackageStateWrite forPackage(@NonNull String packageName) {
+ return mStateWrite.setState(mActiveStateFunction.apply(packageName));
+ }
+
+ @Nullable
+ public PackageStateWrite forPackageNullable(@NonNull String packageName) {
+ final PackageSetting packageState = mActiveStateFunction.apply(packageName);
+ mStateWrite.setState(packageState);
+ if (packageState == null) {
+ return null;
+ }
+
+ return mStateWrite.setState(packageState);
+ }
+
+ @NonNull
+ public PackageStateWrite forDisabledSystemPackage(@NonNull String packageName) {
+ return mStateWrite.setState(mDisabledStateFunction.apply(packageName));
+ }
+
+ @Nullable
+ public PackageStateWrite forDisabledSystemPackageNullable(@NonNull String packageName) {
+ final PackageSetting packageState = mDisabledStateFunction.apply(packageName);
+ if (packageState == null) {
+ return null;
+ }
+
+ return mStateWrite.setState(packageState);
+ }
+
+ @NonNull
+ public InitialState initialState(int changedPackagesSequenceNumber) {
+ return new InitialState(changedPackagesSequenceNumber, sStateChangeSequence.get());
+ }
+
+ /**
+ * @return null if initial state is null or if nothing has changed, otherwise return result
+ * with what changed
+ */
+ @Nullable
+ public Result generateResult(@Nullable InitialState state, int changedPackagesSequenceNumber) {
+ if (state == null) {
+ return Result.SUCCESS;
+ }
+
+ boolean packagesChanged = changedPackagesSequenceNumber != state.mPackageSequence;
+ boolean stateChanged = sStateChangeSequence.get() != state.mStateSequence;
+ if (packagesChanged && stateChanged) {
+ return Result.PACKAGES_AND_STATE_CHANGED;
+ } else if (packagesChanged) {
+ return Result.PACKAGES_CHANGED;
+ } else if (stateChanged) {
+ return Result.STATE_CHANGED;
+ } else {
+ return Result.SUCCESS;
+ }
+ }
+
+ public static class InitialState {
+
+ private final int mPackageSequence;
+ private final long mStateSequence;
+
+ public InitialState(int packageSequence, long stateSequence) {
+ mPackageSequence = packageSequence;
+ mStateSequence = stateSequence;
+ }
+ }
+
+ public static class Result {
+
+ public static final Result SUCCESS = new Result(true, false, false, false);
+ public static final Result PACKAGES_CHANGED = new Result(false, true, false, false);
+ public static final Result STATE_CHANGED = new Result(false, false, true, false);
+ public static final Result PACKAGES_AND_STATE_CHANGED = new Result(false, true, true, false);
+ public static final Result SPECIFIC_PACKAGE_NULL = new Result(false, false, true, true);
+
+ private final boolean mCommitted;
+ private final boolean mPackagesChanged;
+ private final boolean mStateChanged;
+ private final boolean mSpecificPackageNull;
+
+ public Result(boolean committed, boolean packagesChanged, boolean stateChanged,
+ boolean specificPackageNull) {
+ mCommitted = committed;
+ mPackagesChanged = packagesChanged;
+ mStateChanged = stateChanged;
+ mSpecificPackageNull = specificPackageNull;
+ }
+
+ public boolean isCommitted() {
+ return mCommitted;
+ }
+
+ public boolean isPackagesChanged() {
+ return mPackagesChanged;
+ }
+
+ public boolean isStateChanged() {
+ return mStateChanged;
+ }
+
+ public boolean isSpecificPackageNull() {
+ return mSpecificPackageNull;
+ }
+ }
+
+ private static class StateWriteWrapper implements PackageStateWrite {
+
+ private final UserStateWriteWrapper mUserStateWrite = new UserStateWriteWrapper();
+
+ @NonNull
+ private PackageSetting mState;
+
+ public StateWriteWrapper setState(PackageSetting state) {
+ this.mState = state;
+ return this;
+ }
+
+ @NonNull
+ @Override
+ public PackageUserStateWrite userState(int userId) {
+ return mUserStateWrite.setStates(
+ mState == null ? null : mState.getOrCreateUserState(userId));
+ }
+
+ @Override
+ public PackageStateWrite setLastPackageUsageTime(int reason, long timeInMillis) {
+ if (mState != null) {
+ mState.getTransientState().setLastPackageUsageTimeInMills(reason, timeInMillis);
+ }
+ return this;
+ }
+
+ @Override
+ public PackageStateWrite setHiddenUntilInstalled(boolean value) {
+ if (mState != null) {
+ mState.getTransientState().setHiddenUntilInstalled(value);
+ }
+ return this;
+ }
+
+ @NonNull
+ @Override
+ public PackageStateWrite setRequiredForSystemUser(boolean requiredForSystemUser) {
+ if (mState != null) {
+ if (requiredForSystemUser) {
+ mState.setPrivateFlags(mState.getPrivateFlags()
+ | ApplicationInfo.PRIVATE_FLAG_REQUIRED_FOR_SYSTEM_USER);
+ } else {
+ mState.setPrivateFlags(mState.getPrivateFlags()
+ & ~ApplicationInfo.PRIVATE_FLAG_REQUIRED_FOR_SYSTEM_USER);
+ }
+ }
+ return this;
+ }
+
+ @NonNull
+ @Override
+ public PackageStateWrite setMimeGroup(@NonNull String mimeGroup,
+ @NonNull ArraySet<String> mimeTypes) {
+ if (mState != null) {
+ mState.setMimeGroup(mimeGroup, mimeTypes);
+ }
+ return this;
+ }
+
+ private static class UserStateWriteWrapper implements PackageUserStateWrite {
+
+ @Nullable
+ private PackageUserStateImpl mUserState;
+
+ public UserStateWriteWrapper setStates(@Nullable PackageUserStateImpl userState) {
+ mUserState = userState;
+ return this;
+ }
+
+ @NonNull
+ @Override
+ public PackageUserStateWrite setInstalled(boolean installed) {
+ if (mUserState != null) {
+ mUserState.setInstalled(installed);
+ }
+ return this;
+ }
+
+ @NonNull
+ @Override
+ public PackageUserStateWrite setUninstallReason(int reason) {
+ if (mUserState != null) {
+ mUserState.setUninstallReason(reason);
+ }
+ return this;
+ }
+
+ @NonNull
+ @Override
+ public PackageUserStateWrite setDistractionFlags(
+ @PackageManager.DistractionRestriction int restrictionFlags) {
+ if (mUserState != null) {
+ mUserState.setDistractionFlags(restrictionFlags);
+ }
+ return this;
+ }
+
+ @NonNull
+ @Override
+ public PackageUserStateWrite putSuspendParams(@NonNull String suspendingPackage,
+ @Nullable SuspendParams suspendParams) {
+ if (mUserState != null) {
+ mUserState.putSuspendParams(suspendingPackage, suspendParams);
+ }
+ return this;
+ }
+
+ @NonNull
+ @Override
+ public PackageUserStateWrite removeSuspension(@NonNull String suspendingPackage) {
+ if (mUserState != null) {
+ mUserState.removeSuspension(suspendingPackage);
+ }
+ return this;
+ }
+
+ @NonNull
+ @Override
+ public PackageUserStateWrite setHidden(boolean hidden) {
+ if (mUserState != null) {
+ mUserState.setHidden(hidden);
+ }
+ return this;
+ }
+
+ @NonNull
+ @Override
+ public PackageUserStateWrite setStopped(boolean stopped) {
+ if (mUserState != null) {
+ mUserState.setStopped(stopped);
+ }
+ return this;
+ }
+
+ @NonNull
+ @Override
+ public PackageUserStateWrite setNotLaunched(boolean notLaunched) {
+ if (mUserState != null) {
+ mUserState.setNotLaunched(notLaunched);
+ }
+ return this;
+ }
+
+ @NonNull
+ @Override
+ public PackageUserStateWrite setOverlayPaths(@NonNull OverlayPaths overlayPaths) {
+ if (mUserState != null) {
+ mUserState.setOverlayPaths(overlayPaths);
+ }
+ return this;
+ }
+
+ @NonNull
+ @Override
+ public PackageUserStateWrite setOverlayPathsForLibrary(@NonNull String libraryName,
+ @Nullable OverlayPaths overlayPaths) {
+ if (mUserState != null) {
+ mUserState.setSharedLibraryOverlayPaths(libraryName, overlayPaths);
+ }
+ return this;
+ }
+
+ @NonNull
+ @Override
+ public PackageUserStateWrite setHarmfulAppWarning(@Nullable String warning) {
+ if (mUserState != null) {
+ mUserState.setHarmfulAppWarning(warning);
+ }
+ return this;
+ }
+ }
+ }
+}
diff --git a/services/core/java/com/android/server/pm/pkg/mutate/PackageStateWrite.java b/services/core/java/com/android/server/pm/pkg/mutate/PackageStateWrite.java
new file mode 100644
index 0000000..585bece
--- /dev/null
+++ b/services/core/java/com/android/server/pm/pkg/mutate/PackageStateWrite.java
@@ -0,0 +1,41 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.pm.pkg.mutate;
+
+import android.annotation.NonNull;
+import android.annotation.UserIdInt;
+import android.content.pm.PackageManager;
+import android.util.ArraySet;
+
+public interface PackageStateWrite {
+
+ @NonNull
+ PackageUserStateWrite userState(@UserIdInt int userId);
+
+ @NonNull
+ PackageStateWrite setLastPackageUsageTime(@PackageManager.NotifyReason int reason,
+ long timeInMillis);
+
+ @NonNull
+ PackageStateWrite setHiddenUntilInstalled(boolean hiddenUntilInstalled);
+
+ @NonNull
+ PackageStateWrite setRequiredForSystemUser(boolean requiredForSystemUser);
+
+ @NonNull
+ PackageStateWrite setMimeGroup(@NonNull String mimeGroup, @NonNull ArraySet<String> mimeTypes);
+}
diff --git a/services/core/java/com/android/server/pm/pkg/mutate/PackageUserStateWrite.java b/services/core/java/com/android/server/pm/pkg/mutate/PackageUserStateWrite.java
new file mode 100644
index 0000000..e23a1b6
--- /dev/null
+++ b/services/core/java/com/android/server/pm/pkg/mutate/PackageUserStateWrite.java
@@ -0,0 +1,63 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.pm.pkg.mutate;
+
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.content.pm.PackageManager;
+import android.content.pm.overlay.OverlayPaths;
+
+import com.android.server.pm.pkg.SuspendParams;
+
+public interface PackageUserStateWrite {
+
+ @NonNull
+ PackageUserStateWrite setInstalled(boolean installed);
+
+ @NonNull
+ PackageUserStateWrite setUninstallReason(@PackageManager.UninstallReason int reason);
+
+ @NonNull
+ PackageUserStateWrite setDistractionFlags(
+ @PackageManager.DistractionRestriction int restrictionFlags);
+
+ @NonNull
+ PackageUserStateWrite putSuspendParams(@NonNull String suspendingPackage,
+ @Nullable SuspendParams suspendParams);
+
+ @NonNull
+ PackageUserStateWrite removeSuspension(@NonNull String suspendingPackage);
+
+ @NonNull
+ PackageUserStateWrite setHidden(boolean hidden);
+
+ @NonNull
+ PackageUserStateWrite setStopped(boolean stopped);
+
+ @NonNull
+ PackageUserStateWrite setNotLaunched(boolean notLaunched);
+
+ @NonNull
+ PackageUserStateWrite setOverlayPaths(@Nullable OverlayPaths overlayPaths);
+
+ @NonNull
+ PackageUserStateWrite setOverlayPathsForLibrary(@NonNull String libraryName,
+ @Nullable OverlayPaths overlayPaths);
+
+ @NonNull
+ PackageUserStateWrite setHarmfulAppWarning(@Nullable String warning);
+}
diff --git a/services/core/java/com/android/server/policy/PhoneWindowManager.java b/services/core/java/com/android/server/policy/PhoneWindowManager.java
index 298f102..4cced17 100644
--- a/services/core/java/com/android/server/policy/PhoneWindowManager.java
+++ b/services/core/java/com/android/server/policy/PhoneWindowManager.java
@@ -363,6 +363,12 @@
public static final int TOAST_WINDOW_TIMEOUT = 3500 + TOAST_WINDOW_ANIM_BUFFER;
/**
+ * Action for launching assistant in retail mode
+ */
+ private static final String ACTION_VOICE_ASSIST_RETAIL =
+ "android.intent.action.VOICE_ASSIST_RETAIL";
+
+ /**
* Lock protecting internal state. Must not call out into window
* manager with lock held. (This lock will be acquired in places
* where the window manager is calling in with its own lock held.)
@@ -1090,29 +1096,35 @@
mPowerManager.boostScreenBrightness(eventTime);
break;
case MULTI_PRESS_POWER_LAUNCH_TARGET_ACTIVITY:
- if (DEBUG_INPUT) {
- Slog.d(TAG, "Executing the double press power action.");
- }
+ launchTargetActivityOnMultiPressPower();
+ break;
+ }
+ }
+
+ private void launchTargetActivityOnMultiPressPower() {
+ if (DEBUG_INPUT) {
+ Slog.d(TAG, "Executing the double press power action.");
+ }
+ if (mPowerDoublePressTargetActivity != null) {
+ Intent intent = new Intent();
+ intent.setComponent(mPowerDoublePressTargetActivity);
+ ResolveInfo resolveInfo = mContext.getPackageManager().resolveActivity(
+ intent, /* flags= */0);
+ if (resolveInfo != null) {
final boolean keyguardActive =
mKeyguardDelegate != null && mKeyguardDelegate.isShowing();
+ intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK
+ | Intent.FLAG_ACTIVITY_RESET_TASK_IF_NEEDED);
if (!keyguardActive) {
- Intent intent = new Intent();
- if (mPowerDoublePressTargetActivity != null) {
- intent.setComponent(mPowerDoublePressTargetActivity);
- ResolveInfo resolveInfo = mContext.getPackageManager().resolveActivity(
- intent, /* flags= */0);
- if (resolveInfo != null) {
- intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK
- | Intent.FLAG_ACTIVITY_RESET_TASK_IF_NEEDED);
- startActivityAsUser(intent, UserHandle.CURRENT_OR_SELF);
- } else {
- Slog.e(TAG, "Could not resolve activity with : "
- + mPowerDoublePressTargetActivity.flattenToString()
- + " name.");
- }
- }
+ startActivityAsUser(intent, UserHandle.CURRENT_OR_SELF);
+ } else {
+ mKeyguardDelegate.dismissKeyguardToLaunch(intent);
}
- break;
+ } else {
+ Slog.e(TAG, "Could not resolve activity with : "
+ + mPowerDoublePressTargetActivity.flattenToString()
+ + " name.");
+ }
}
}
@@ -1242,6 +1254,12 @@
return LONG_PRESS_POWER_GLOBAL_ACTIONS;
}
+ // If long press to launch assistant is disabled in settings, do nothing.
+ if (mLongPressOnPowerBehavior == LONG_PRESS_POWER_GO_TO_VOICE_ASSIST
+ && !isLongPressToAssistantEnabled(mContext)) {
+ return LONG_PRESS_POWER_NOTHING;
+ }
+
return mLongPressOnPowerBehavior;
}
@@ -1273,6 +1291,9 @@
intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK
| Intent.FLAG_ACTIVITY_RESET_TASK_IF_NEEDED);
startActivityAsUser(intent, UserHandle.CURRENT_OR_SELF);
+ } else {
+ // If keyguarded then notify the keyguard.
+ mKeyguardDelegate.onSystemKeyPressed(KeyEvent.KEYCODE_STEM_PRIMARY);
}
break;
}
@@ -3218,17 +3239,50 @@
.getSystemService(Context.SEARCH_SERVICE)).launchAssist(args);
}
- /** Launches ACTION_VOICE_ASSIST. Does nothing on keyguard. */
+ /**
+ * Launches ACTION_VOICE_ASSIST_RETAIL if in retail mode, or ACTION_VOICE_ASSIST otherwise
+ * Does nothing on keyguard except for watches. Delegates it to keyguard if present on watch.
+ */
private void launchVoiceAssist(boolean allowDuringSetup) {
- final boolean keyguardActive = mKeyguardDelegate == null
- ? false
- : mKeyguardDelegate.isShowing();
+ final boolean keyguardActive =
+ mKeyguardDelegate != null && mKeyguardDelegate.isShowing();
if (!keyguardActive) {
- Intent intent = new Intent(Intent.ACTION_VOICE_ASSIST);
- startActivityAsUser(intent, null, UserHandle.CURRENT_OR_SELF,
- allowDuringSetup);
+ if (mHasFeatureWatch && isInRetailMode()) {
+ launchRetailVoiceAssist(allowDuringSetup);
+ } else {
+ startVoiceAssistIntent(allowDuringSetup);
+ }
+ } else {
+ mKeyguardDelegate.dismissKeyguardToLaunch(new Intent(Intent.ACTION_VOICE_ASSIST));
}
+ }
+ private void launchRetailVoiceAssist(boolean allowDuringSetup) {
+ Intent retailIntent = new Intent(ACTION_VOICE_ASSIST_RETAIL);
+ ResolveInfo resolveInfo = mContext.getPackageManager().resolveActivity(
+ retailIntent, /* flags= */0);
+ if (resolveInfo != null) {
+ retailIntent.setComponent(
+ new ComponentName(resolveInfo.activityInfo.packageName,
+ resolveInfo.activityInfo.name));
+ startActivityAsUser(retailIntent, null, UserHandle.CURRENT_OR_SELF,
+ allowDuringSetup);
+ } else {
+ Slog.w(TAG, "Couldn't find an app to process " + ACTION_VOICE_ASSIST_RETAIL
+ + ". Fall back to start " + Intent.ACTION_VOICE_ASSIST);
+ startVoiceAssistIntent(allowDuringSetup);
+ }
+ }
+
+ private void startVoiceAssistIntent(boolean allowDuringSetup) {
+ Intent intent = new Intent(Intent.ACTION_VOICE_ASSIST);
+ startActivityAsUser(intent, null, UserHandle.CURRENT_OR_SELF,
+ allowDuringSetup);
+ }
+
+ private boolean isInRetailMode() {
+ return Settings.Global.getInt(mContext.getContentResolver(),
+ Settings.Global.DEVICE_DEMO_MODE, 0) == 1;
}
private void startActivityAsUser(Intent intent, UserHandle handle) {
@@ -5824,6 +5878,18 @@
}
}
+ public static boolean isLongPressToAssistantEnabled(Context context) {
+ ContentResolver resolver = context.getContentResolver();
+ int longPressToAssistant = Settings.System.getIntForUser(resolver,
+ Settings.Global.Wearable.CLOCKWORK_LONG_PRESS_TO_ASSISTANT_ENABLED,
+ /* def= */ 1,
+ UserHandle.USER_CURRENT);
+ if(Log.isLoggable(TAG, Log.DEBUG)) {
+ Log.d(TAG, "longPressToAssistant = " + longPressToAssistant);
+ }
+ return (longPressToAssistant == 1);
+ }
+
private class HdmiVideoExtconUEventObserver extends ExtconStateObserver<Boolean> {
private static final String HDMI_EXIST = "HDMI=1";
private static final String NAME = "hdmi";
diff --git a/services/core/java/com/android/server/policy/keyguard/KeyguardServiceDelegate.java b/services/core/java/com/android/server/policy/keyguard/KeyguardServiceDelegate.java
index 0080ec6..97a57e0 100644
--- a/services/core/java/com/android/server/policy/keyguard/KeyguardServiceDelegate.java
+++ b/services/core/java/com/android/server/policy/keyguard/KeyguardServiceDelegate.java
@@ -415,6 +415,17 @@
}
}
+ public void dismissKeyguardToLaunch(Intent intentToLaunch) {
+ if (mKeyguardService != null) {
+ mKeyguardService.dismissKeyguardToLaunch(intentToLaunch);
+ }
+ }
+ public void onSystemKeyPressed(int keycode) {
+ if (mKeyguardService != null) {
+ mKeyguardService.onSystemKeyPressed(keycode);
+ }
+ }
+
public void dumpDebug(ProtoOutputStream proto, long fieldId) {
final long token = proto.start(fieldId);
proto.write(SHOWING, mKeyguardState.showing);
diff --git a/services/core/java/com/android/server/policy/keyguard/KeyguardServiceWrapper.java b/services/core/java/com/android/server/policy/keyguard/KeyguardServiceWrapper.java
index 2029f86..774e261 100644
--- a/services/core/java/com/android/server/policy/keyguard/KeyguardServiceWrapper.java
+++ b/services/core/java/com/android/server/policy/keyguard/KeyguardServiceWrapper.java
@@ -17,6 +17,7 @@
package com.android.server.policy.keyguard;
import android.content.Context;
+import android.content.Intent;
import android.os.Bundle;
import android.os.IBinder;
import android.os.PowerManager;
@@ -254,6 +255,24 @@
}
}
+ @Override
+ public void dismissKeyguardToLaunch(Intent intentToLaunch) {
+ try {
+ mService.dismissKeyguardToLaunch(intentToLaunch);
+ } catch (RemoteException e) {
+ Slog.w(TAG , "Remote Exception", e);
+ }
+ }
+
+ @Override
+ public void onSystemKeyPressed(int keycode) {
+ try {
+ mService.onSystemKeyPressed(keycode);
+ } catch (RemoteException e) {
+ Slog.w(TAG , "Remote Exception", e);
+ }
+ }
+
@Override // Binder interface
public IBinder asBinder() {
return mService.asBinder();
diff --git a/services/core/java/com/android/server/tv/TvInputManagerService.java b/services/core/java/com/android/server/tv/TvInputManagerService.java
index 332fed7..568e4b8 100755
--- a/services/core/java/com/android/server/tv/TvInputManagerService.java
+++ b/services/core/java/com/android/server/tv/TvInputManagerService.java
@@ -44,6 +44,8 @@
import android.hardware.hdmi.HdmiControlManager;
import android.hardware.hdmi.HdmiDeviceInfo;
import android.media.PlaybackParams;
+import android.media.tv.AdRequest;
+import android.media.tv.AdResponse;
import android.media.tv.AitInfo;
import android.media.tv.BroadcastInfoRequest;
import android.media.tv.BroadcastInfoResponse;
@@ -2494,6 +2496,28 @@
}
@Override
+ public void requestAd(IBinder sessionToken, AdRequest request, int userId) {
+ final int callingUid = Binder.getCallingUid();
+ final int callingPid = Binder.getCallingPid();
+ final int resolvedUserId = resolveCallingUserId(callingPid, callingUid,
+ userId, "requestAd");
+ final long identity = Binder.clearCallingIdentity();
+ try {
+ synchronized (mLock) {
+ try {
+ SessionState sessionState = getSessionStateLocked(sessionToken, callingUid,
+ resolvedUserId);
+ getSessionLocked(sessionState).requestAd(request);
+ } catch (RemoteException | SessionNotFoundException e) {
+ Slog.e(TAG, "error in requestAd", e);
+ }
+ }
+ } finally {
+ Binder.restoreCallingIdentity(identity);
+ };
+ }
+
+ @Override
public int getClientPid(String sessionId) {
ensureTunerResourceAccessPermission();
final long identity = Binder.clearCallingIdentity();
@@ -3557,6 +3581,23 @@
}
}
}
+
+ @Override
+ public void onAdResponse (AdResponse response) {
+ synchronized (mLock) {
+ if (DEBUG) {
+ Slog.d(TAG, "onAdResponse()");
+ }
+ if (mSessionState.session == null || mSessionState.client == null) {
+ return;
+ }
+ try {
+ mSessionState.client.onAdResponse(response, mSessionState.seq);
+ } catch (RemoteException e) {
+ Slog.e(TAG, "error in onAdResponse", e);
+ }
+ }
+ }
}
@VisibleForTesting
diff --git a/services/core/java/com/android/server/tv/interactive/TvIAppManagerService.java b/services/core/java/com/android/server/tv/interactive/TvIAppManagerService.java
index ef36f51..a2bf2fe 100644
--- a/services/core/java/com/android/server/tv/interactive/TvIAppManagerService.java
+++ b/services/core/java/com/android/server/tv/interactive/TvIAppManagerService.java
@@ -29,6 +29,8 @@
import android.content.pm.ServiceInfo;
import android.content.pm.UserInfo;
import android.graphics.Rect;
+import android.media.tv.AdRequest;
+import android.media.tv.AdResponse;
import android.media.tv.BroadcastInfoRequest;
import android.media.tv.BroadcastInfoResponse;
import android.media.tv.TvTrackInfo;
@@ -1178,6 +1180,28 @@
}
@Override
+ public void notifyAdResponse(IBinder sessionToken, AdResponse response, int userId) {
+ final int callingUid = Binder.getCallingUid();
+ final int callingPid = Binder.getCallingPid();
+ final int resolvedUserId = resolveCallingUserId(callingPid, callingUid, userId,
+ "notifyAdResponse");
+ final long identity = Binder.clearCallingIdentity();
+ try {
+ synchronized (mLock) {
+ try {
+ SessionState sessionState = getSessionStateLocked(sessionToken, callingUid,
+ resolvedUserId);
+ getSessionLocked(sessionState).notifyAdResponse(response);
+ } catch (RemoteException | SessionNotFoundException e) {
+ Slogf.e(TAG, "error in notifyAdResponse", e);
+ }
+ }
+ } finally {
+ Binder.restoreCallingIdentity(identity);
+ }
+ }
+
+ @Override
public void registerCallback(final ITvIAppManagerCallback callback, int userId) {
int callingPid = Binder.getCallingPid();
int callingUid = Binder.getCallingUid();
@@ -1908,6 +1932,23 @@
}
@Override
+ public void onAdRequest(AdRequest request) {
+ synchronized (mLock) {
+ if (DEBUG) {
+ Slogf.d(TAG, "onAdRequest (id=" + request.getId() + ")");
+ }
+ if (mSessionState.mSession == null || mSessionState.mClient == null) {
+ return;
+ }
+ try {
+ mSessionState.mClient.onAdRequest(request, mSessionState.mSeq);
+ } catch (RemoteException e) {
+ Slogf.e(TAG, "error in onAdRequest", e);
+ }
+ }
+ }
+
+ @Override
public void onSessionStateChanged(int state) {
synchronized (mLock) {
if (DEBUG) {
diff --git a/services/core/java/com/android/server/wm/ActivityRecord.java b/services/core/java/com/android/server/wm/ActivityRecord.java
index fcc2bd6..30465af 100644
--- a/services/core/java/com/android/server/wm/ActivityRecord.java
+++ b/services/core/java/com/android/server/wm/ActivityRecord.java
@@ -6337,7 +6337,6 @@
}
} else if (w.isDrawn()) {
// The starting window for this container is drawn.
- mTaskSupervisor.getActivityMetricsLogger().notifyStartingWindowDrawn(this);
startingDisplayed = true;
}
}
diff --git a/services/core/java/com/android/server/wm/ActivityTaskSupervisor.java b/services/core/java/com/android/server/wm/ActivityTaskSupervisor.java
index 51c8daf..3cecce2 100644
--- a/services/core/java/com/android/server/wm/ActivityTaskSupervisor.java
+++ b/services/core/java/com/android/server/wm/ActivityTaskSupervisor.java
@@ -641,22 +641,35 @@
intent.setComponent(new ComponentName(
aInfo.applicationInfo.packageName, aInfo.name));
- // Don't debug things in the system process
- if (!aInfo.processName.equals("system")) {
- if ((startFlags & (START_FLAG_DEBUG | START_FLAG_NATIVE_DEBUGGING
- | START_FLAG_TRACK_ALLOCATION)) != 0 || profilerInfo != null) {
-
+ final boolean requestDebug = (startFlags & (START_FLAG_DEBUG
+ | START_FLAG_NATIVE_DEBUGGING | START_FLAG_TRACK_ALLOCATION)) != 0;
+ final boolean requestProfile = profilerInfo != null;
+ if (requestDebug || requestProfile) {
+ final boolean debuggable = (Build.IS_DEBUGGABLE
+ || (aInfo.applicationInfo.flags & ApplicationInfo.FLAG_DEBUGGABLE) != 0)
+ && !aInfo.processName.equals("system");
+ if ((requestDebug && !debuggable) || (requestProfile
+ && (!debuggable && !aInfo.applicationInfo.isProfileableByShell()))) {
+ Slog.w(TAG, "Ignore debugging for non-debuggable app: " + aInfo.packageName);
+ } else {
// Mimic an AMS synchronous call by passing a message to AMS and wait for AMS
// to notify us that the task has completed.
// TODO(b/80414790) look into further untangling for the situation where the
// caller is on the same thread as the handler we are posting to.
synchronized (mService.mGlobalLock) {
// Post message to AMS.
- final Message msg = PooledLambda.obtainMessage(
- ActivityManagerInternal::setDebugFlagsForStartingActivity,
- mService.mAmInternal, aInfo, startFlags, profilerInfo,
- mService.mGlobalLock);
- mService.mH.sendMessage(msg);
+ mService.mH.post(() -> {
+ try {
+ mService.mAmInternal.setDebugFlagsForStartingActivity(aInfo,
+ startFlags, profilerInfo, mService.mGlobalLock);
+ } catch (Throwable e) {
+ // Simply ignore it because the debugging doesn't take effect.
+ Slog.w(TAG, e);
+ synchronized (mService.mGlobalLockWithoutBoost) {
+ mService.mGlobalLockWithoutBoost.notifyAll();
+ }
+ }
+ });
try {
mService.mGlobalLock.wait();
} catch (InterruptedException ignore) {
diff --git a/services/core/java/com/android/server/wm/FadeRotationAnimationController.java b/services/core/java/com/android/server/wm/FadeRotationAnimationController.java
index bbda577..c85e04d 100644
--- a/services/core/java/com/android/server/wm/FadeRotationAnimationController.java
+++ b/services/core/java/com/android/server/wm/FadeRotationAnimationController.java
@@ -59,8 +59,13 @@
/** The list to store the drawn tokens before the rotation animation starts. */
private ArrayList<WindowToken> mPendingShowTokens;
- /** It is used when the display has rotated, but some windows fade out in old rotation. */
- private SeamlessRotator mRotator;
+ /**
+ * The sync transactions of the target windows. It is used when the display has rotated but
+ * the windows need to fade out in previous rotation. These transactions will be applied with
+ * fade-in animation, so there won't be a flickering such as the windows have redrawn during
+ * fading out.
+ */
+ private ArrayMap<WindowState, SurfaceControl.Transaction> mCapturedDrawTransactions;
private final int mOriginalRotation;
private final boolean mHasScreenRotationAnimation;
@@ -110,16 +115,36 @@
mTargetWindowTokens.put(w.mToken, null);
}
}, true /* traverseTopToBottom */);
+
+ // The transition sync group may be finished earlier because it doesn't wait for these
+ // target windows. But the windows still need to use sync transaction to keep the appearance
+ // in previous rotation, so request a no-op sync to keep the state.
+ if (!mIsChangeTransition && transitionType != WindowManager.TRANSIT_NONE) {
+ for (int i = mTargetWindowTokens.size() - 1; i >= 0; i--) {
+ final WindowToken token = mTargetWindowTokens.keyAt(i);
+ for (int j = token.getChildCount() - 1; j >= 0; j--) {
+ token.getChildAt(j).applyWithNextDraw(t -> {});
+ }
+ }
+ }
}
@Override
public void fadeWindowToken(boolean show, WindowToken windowToken, int animationType) {
if (show) {
- final SurfaceControl leash = mTargetWindowTokens.remove(windowToken);
- if (leash != null && mRotator != null) {
- // The leash was unrotated by start transaction of transition. Clear the transform
- // to reshow the window in current rotation.
- mRotator.setIdentityMatrix(mDisplayContent.getPendingTransaction(), leash);
+ // The previous animation leash will be dropped when preparing fade-in animation, so
+ // simply remove it without restoring the transformation.
+ mTargetWindowTokens.remove(windowToken);
+ if (mCapturedDrawTransactions != null) {
+ // Unblock the window to draw its latest content with fade-in animation.
+ final SurfaceControl.Transaction t = mDisplayContent.getPendingTransaction();
+ for (int i = windowToken.getChildCount() - 1; i >= 0; i--) {
+ final SurfaceControl.Transaction drawT =
+ mCapturedDrawTransactions.remove(windowToken.getChildAt(i));
+ if (drawT != null) {
+ t.merge(drawT);
+ }
+ }
}
}
super.fadeWindowToken(show, windowToken, animationType);
@@ -225,14 +250,14 @@
// Take OPEN/CLOSE transition type as the example, the non-activity windows need to
// fade out in previous rotation while display has rotated to the new rotation, so
// their leashes are unrotated with the start transaction.
- mRotator = new SeamlessRotator(mOriginalRotation,
+ final SeamlessRotator rotator = new SeamlessRotator(mOriginalRotation,
mDisplayContent.getWindowConfiguration().getRotation(),
mDisplayContent.getDisplayInfo(),
false /* applyFixedTransformationHint */);
for (int i = mTargetWindowTokens.size() - 1; i >= 0; i--) {
final SurfaceControl leash = mTargetWindowTokens.valueAt(i);
if (leash != null) {
- mRotator.applyTransform(t, leash);
+ rotator.applyTransform(t, leash);
}
}
return;
@@ -280,6 +305,25 @@
}
}
+ /** Captures the post draw transaction if the window should update with fade-in animation. */
+ boolean handleFinishDrawing(WindowState w, SurfaceControl.Transaction postDrawTransaction) {
+ if (mIsChangeTransition || !isTargetToken(w.mToken)) return false;
+ if (postDrawTransaction != null && w.mTransitionController.inTransition()) {
+ if (mCapturedDrawTransactions == null) {
+ mCapturedDrawTransactions = new ArrayMap<>();
+ }
+ final SurfaceControl.Transaction t = mCapturedDrawTransactions.get(w);
+ if (t == null) {
+ mCapturedDrawTransactions.put(w, postDrawTransaction);
+ } else {
+ t.merge(postDrawTransaction);
+ }
+ return true;
+ }
+ mDisplayContent.finishFadeRotationAnimation(w.mToken);
+ return false;
+ }
+
@Override
public Animation getFadeInAnimation() {
if (mHasScreenRotationAnimation) {
diff --git a/services/core/java/com/android/server/wm/RootWindowContainer.java b/services/core/java/com/android/server/wm/RootWindowContainer.java
index 0826904d..535bbb7 100644
--- a/services/core/java/com/android/server/wm/RootWindowContainer.java
+++ b/services/core/java/com/android/server/wm/RootWindowContainer.java
@@ -1037,13 +1037,7 @@
}
}
- final boolean curDisplayInTransitNotAnimate =
- // legacy transition
- (curDisplay.mAppTransition.isRunning() && !curDisplay.isAppTransitioning())
- // shell transition
- || (curDisplay.mTransitionController.isShellTransitionsEnabled()
- && !curDisplay.mTransitionController.isPlaying());
- if (curDisplayInTransitNotAnimate) {
+ if (curDisplay.mAppTransition.isRunning() && !curDisplay.isAppTransitioning()) {
// We have finished the animation of an app transition. To do this, we have
// delayed a lot of operations like showing and hiding apps, moving apps in
// Z-order, etc.
diff --git a/services/core/java/com/android/server/wm/SplashScreenExceptionList.java b/services/core/java/com/android/server/wm/SplashScreenExceptionList.java
index e815a0e..9ca49fe 100644
--- a/services/core/java/com/android/server/wm/SplashScreenExceptionList.java
+++ b/services/core/java/com/android/server/wm/SplashScreenExceptionList.java
@@ -64,7 +64,8 @@
mOnPropertiesChangedListener);
}
- private void updateDeviceConfig(String values) {
+ @VisibleForTesting
+ void updateDeviceConfig(String values) {
parseDeviceConfigPackageList(values);
}
diff --git a/services/core/java/com/android/server/wm/Transition.java b/services/core/java/com/android/server/wm/Transition.java
index 1c40044..a477108 100644
--- a/services/core/java/com/android/server/wm/Transition.java
+++ b/services/core/java/com/android/server/wm/Transition.java
@@ -497,6 +497,11 @@
if (fadeRotationController != null) {
fadeRotationController.onTransitionFinished();
}
+ // Transient-launch activities cannot be IME target (WindowState#canBeImeTarget),
+ // so re-compute in case the IME target is changed after transition.
+ if (mTransientLaunches != null) {
+ mTargetDisplay.computeImeTarget(true /* updateImeTarget */);
+ }
}
void abort() {
diff --git a/services/core/java/com/android/server/wm/WindowContainer.java b/services/core/java/com/android/server/wm/WindowContainer.java
index bef7810..61acb97 100644
--- a/services/core/java/com/android/server/wm/WindowContainer.java
+++ b/services/core/java/com/android/server/wm/WindowContainer.java
@@ -3354,8 +3354,12 @@
for (int i = mChildren.size() - 1; i >= 0; --i) {
mChildren.get(i).finishSync(outMergedTransaction, cancel);
}
- mSyncState = SYNC_STATE_NONE;
if (cancel && mSyncGroup != null) mSyncGroup.onCancelSync(this);
+ clearSyncState();
+ }
+
+ void clearSyncState() {
+ mSyncState = SYNC_STATE_NONE;
mSyncGroup = null;
}
diff --git a/services/core/java/com/android/server/wm/WindowManagerService.java b/services/core/java/com/android/server/wm/WindowManagerService.java
index fc1fd92..7be128b 100644
--- a/services/core/java/com/android/server/wm/WindowManagerService.java
+++ b/services/core/java/com/android/server/wm/WindowManagerService.java
@@ -2548,10 +2548,8 @@
if (win.mAttrs.type == TYPE_APPLICATION_STARTING) {
transit = WindowManagerPolicy.TRANSIT_PREVIEW_DONE;
}
- if (win.inTransition()) {
- focusMayChange = true;
- win.mAnimatingExit = true;
- } else if (win.isWinVisibleLw() && winAnimator.applyAnimationLocked(transit, false)) {
+
+ if (win.isWinVisibleLw() && winAnimator.applyAnimationLocked(transit, false)) {
focusMayChange = true;
win.mAnimatingExit = true;
} else if (win.mDisplayContent.okToAnimate() && win.isAnimating(TRANSITION | PARENTS,
diff --git a/services/core/java/com/android/server/wm/WindowState.java b/services/core/java/com/android/server/wm/WindowState.java
index f6729c5..863e3ca 100644
--- a/services/core/java/com/android/server/wm/WindowState.java
+++ b/services/core/java/com/android/server/wm/WindowState.java
@@ -5427,7 +5427,7 @@
@Override
void assignLayer(Transaction t, int layer) {
- if (isStartingWindowAssociatedToTask()) {
+ if (mStartingData != null) {
// The starting window should cover the task.
t.setLayer(mSurfaceControl, Integer.MAX_VALUE);
return;
@@ -5747,29 +5747,36 @@
Slog.i(TAG, "finishDrawing of relaunch: " + this + " " + duration + "ms");
mActivityRecord.mRelaunchStartTime = 0;
}
-
- executeDrawHandlers(postDrawTransaction);
-
- final boolean applyPostDrawNow = mClientWasDrawingForSync && postDrawTransaction != null;
- mClientWasDrawingForSync = false;
- if (!onSyncFinishedDrawing()) {
- return mWinAnimator.finishDrawingLocked(postDrawTransaction, applyPostDrawNow);
- }
-
- if (mActivityRecord != null
- && mTransitionController.isShellTransitionsEnabled()
- && mAttrs.type == TYPE_APPLICATION_STARTING) {
+ if (mActivityRecord != null && mAttrs.type == TYPE_APPLICATION_STARTING) {
mWmService.mAtmService.mTaskSupervisor.getActivityMetricsLogger()
.notifyStartingWindowDrawn(mActivityRecord);
}
- if (postDrawTransaction != null) {
+ final boolean hasSyncHandlers = executeDrawHandlers(postDrawTransaction);
+
+ boolean skipLayout = false;
+ // Control the timing to switch the appearance of window with different rotations.
+ final FadeRotationAnimationController fadeRotationController =
+ mDisplayContent.getFadeRotationAnimationController();
+ if (fadeRotationController != null
+ && fadeRotationController.handleFinishDrawing(this, postDrawTransaction)) {
+ // Consume the transaction because the controller will apply it with fade animation.
+ // Layout is not needed because the window will be hidden by the fade leash. Clear
+ // sync state because its sync transaction doesn't need to be merged to sync group.
+ postDrawTransaction = null;
+ skipLayout = true;
+ clearSyncState();
+ } else if (onSyncFinishedDrawing() && postDrawTransaction != null) {
mSyncTransaction.merge(postDrawTransaction);
+ // Consume the transaction because the sync group will merge it.
+ postDrawTransaction = null;
}
- mWinAnimator.finishDrawingLocked(null, false /* forceApplyNow */);
+ final boolean layoutNeeded =
+ mWinAnimator.finishDrawingLocked(postDrawTransaction, mClientWasDrawingForSync);
+ mClientWasDrawingForSync = false;
// We always want to force a traversal after a finish draw for blast sync.
- return true;
+ return !skipLayout && (hasSyncHandlers || layoutNeeded);
}
void immediatelyNotifyBlastSync() {
diff --git a/services/tests/servicestests/src/com/android/server/net/NetworkPolicyManagerServiceTest.java b/services/tests/servicestests/src/com/android/server/net/NetworkPolicyManagerServiceTest.java
index eda05bf..b811e28 100644
--- a/services/tests/servicestests/src/com/android/server/net/NetworkPolicyManagerServiceTest.java
+++ b/services/tests/servicestests/src/com/android/server/net/NetworkPolicyManagerServiceTest.java
@@ -129,6 +129,7 @@
import android.net.NetworkStatsHistory;
import android.net.NetworkTemplate;
import android.net.TelephonyNetworkSpecifier;
+import android.net.wifi.WifiInfo;
import android.os.Binder;
import android.os.Handler;
import android.os.INetworkManagementService;
@@ -226,13 +227,13 @@
private static final long TEST_START = 1194220800000L;
private static final String TEST_IFACE = "test0";
- private static final String TEST_SSID = "AndroidAP";
+ private static final String TEST_WIFI_NETWORK_KEY = "TestWifiNetworkKey";
private static final String TEST_IMSI = "310210";
private static final int TEST_SUB_ID = 42;
private static final Network TEST_NETWORK = mock(Network.class, CALLS_REAL_METHODS);
- private static NetworkTemplate sTemplateWifi = buildTemplateWifi(TEST_SSID);
+ private static NetworkTemplate sTemplateWifi = buildTemplateWifi(TEST_WIFI_NETWORK_KEY);
private static NetworkTemplate sTemplateCarrierMetered =
buildTemplateCarrierMetered(TEST_IMSI);
@@ -2096,10 +2097,13 @@
}
private static NetworkStateSnapshot buildWifi() {
+ WifiInfo mockWifiInfo = mock(WifiInfo.class);
+ when(mockWifiInfo.makeCopy(anyLong())).thenReturn(mockWifiInfo);
+ when(mockWifiInfo.getCurrentNetworkKey()).thenReturn(TEST_WIFI_NETWORK_KEY);
final LinkProperties prop = new LinkProperties();
prop.setInterfaceName(TEST_IFACE);
final NetworkCapabilities networkCapabilities = new NetworkCapabilities.Builder()
- .addTransportType(TRANSPORT_WIFI).setSsid(TEST_SSID).build();
+ .addTransportType(TRANSPORT_WIFI).setTransportInfo(mockWifiInfo).build();
return new NetworkStateSnapshot(TEST_NETWORK, networkCapabilities, prop,
null /*subscriberId*/, TYPE_WIFI);
}
diff --git a/services/tests/servicestests/src/com/android/server/pm/PackageManagerSettingsTests.java b/services/tests/servicestests/src/com/android/server/pm/PackageManagerSettingsTests.java
index 6c9f8fe..1b898fa 100644
--- a/services/tests/servicestests/src/com/android/server/pm/PackageManagerSettingsTests.java
+++ b/services/tests/servicestests/src/com/android/server/pm/PackageManagerSettingsTests.java
@@ -264,17 +264,17 @@
final SuspendParams params = packageUserState1.getSuspendParams().valueAt(0);
watcher.verifyNoChangeReported("fetch user state");
assertThat(params, is(notNullValue()));
- assertThat(params.appExtras.size(), is(1));
- assertThat(params.appExtras.getString("app_extra_string"), is("value"));
- assertThat(params.launcherExtras.size(), is(1));
- assertThat(params.launcherExtras.getLong("launcher_extra_long"), is(4L));
- assertThat(params.dialogInfo, is(notNullValue()));
- assertThat(params.dialogInfo.getDialogMessage(), is("Dialog Message"));
- assertThat(params.dialogInfo.getTitleResId(), is(ID_NULL));
- assertThat(params.dialogInfo.getIconResId(), is(TEST_RESOURCE_ID));
- assertThat(params.dialogInfo.getNeutralButtonTextResId(), is(ID_NULL));
- assertThat(params.dialogInfo.getNeutralButtonAction(), is(BUTTON_ACTION_MORE_DETAILS));
- assertThat(params.dialogInfo.getDialogMessageResId(), is(ID_NULL));
+ assertThat(params.getAppExtras().size(), is(1));
+ assertThat(params.getAppExtras().getString("app_extra_string"), is("value"));
+ assertThat(params.getLauncherExtras().size(), is(1));
+ assertThat(params.getLauncherExtras().getLong("launcher_extra_long"), is(4L));
+ assertThat(params.getDialogInfo(), is(notNullValue()));
+ assertThat(params.getDialogInfo().getDialogMessage(), is("Dialog Message"));
+ assertThat(params.getDialogInfo().getTitleResId(), is(ID_NULL));
+ assertThat(params.getDialogInfo().getIconResId(), is(TEST_RESOURCE_ID));
+ assertThat(params.getDialogInfo().getNeutralButtonTextResId(), is(ID_NULL));
+ assertThat(params.getDialogInfo().getNeutralButtonAction(), is(BUTTON_ACTION_MORE_DETAILS));
+ assertThat(params.getDialogInfo().getDialogMessageResId(), is(ID_NULL));
}
@Test
@@ -351,18 +351,18 @@
final SuspendParams params11 = readPus1.getSuspendParams().valueAt(0);
watcher.verifyNoChangeReported("read package param");
assertThat(params11, is(notNullValue()));
- assertThat(params11.dialogInfo, is(dialogInfo1));
- assertThat(BaseBundle.kindofEquals(params11.appExtras, appExtras1), is(true));
- assertThat(BaseBundle.kindofEquals(params11.launcherExtras, launcherExtras1),
+ assertThat(params11.getDialogInfo(), is(dialogInfo1));
+ assertThat(BaseBundle.kindofEquals(params11.getAppExtras(), appExtras1), is(true));
+ assertThat(BaseBundle.kindofEquals(params11.getLauncherExtras(), launcherExtras1),
is(true));
watcher.verifyNoChangeReported("read package param");
assertThat(readPus1.getSuspendParams().keyAt(1), is("suspendingPackage2"));
final SuspendParams params12 = readPus1.getSuspendParams().valueAt(1);
assertThat(params12, is(notNullValue()));
- assertThat(params12.dialogInfo, is(dialogInfo2));
- assertThat(BaseBundle.kindofEquals(params12.appExtras, appExtras2), is(true));
- assertThat(BaseBundle.kindofEquals(params12.launcherExtras, launcherExtras2),
+ assertThat(params12.getDialogInfo(), is(dialogInfo2));
+ assertThat(BaseBundle.kindofEquals(params12.getAppExtras(), appExtras2), is(true));
+ assertThat(BaseBundle.kindofEquals(params12.getLauncherExtras(), launcherExtras2),
is(true));
watcher.verifyNoChangeReported("read package param");
@@ -373,9 +373,9 @@
assertThat(readPus2.getSuspendParams().keyAt(0), is("suspendingPackage3"));
final SuspendParams params21 = readPus2.getSuspendParams().valueAt(0);
assertThat(params21, is(notNullValue()));
- assertThat(params21.dialogInfo, is(nullValue()));
- assertThat(BaseBundle.kindofEquals(params21.appExtras, appExtras1), is(true));
- assertThat(params21.launcherExtras, is(nullValue()));
+ assertThat(params21.getDialogInfo(), is(nullValue()));
+ assertThat(BaseBundle.kindofEquals(params21.getAppExtras(), appExtras1), is(true));
+ assertThat(params21.getLauncherExtras(), is(nullValue()));
watcher.verifyNoChangeReported("read package param");
final PackageUserStateInternal readPus3 = settingsUnderTest.mPackages.get(PACKAGE_NAME_3)
@@ -388,7 +388,7 @@
@Test
public void testPackageRestrictionsSuspendedDefault() {
final PackageSetting defaultSetting = createPackageSetting(PACKAGE_NAME_1);
- assertThat(defaultSetting.getSuspended(0), is(false));
+ assertThat(defaultSetting.getUserStateOrDefault(0).isSuspended(), is(false));
}
@Test
diff --git a/services/tests/servicestests/src/com/android/server/pm/PackageUserStateTest.java b/services/tests/servicestests/src/com/android/server/pm/PackageUserStateTest.java
index 828d419c..1e4134e 100644
--- a/services/tests/servicestests/src/com/android/server/pm/PackageUserStateTest.java
+++ b/services/tests/servicestests/src/com/android/server/pm/PackageUserStateTest.java
@@ -82,7 +82,8 @@
assertThat(testUserState.equals(oldUserState), is(false));
oldUserState = new PackageUserStateImpl();
- oldUserState.setSuspended(true);
+ oldUserState.putSuspendParams("suspendingPackage",
+ SuspendParams.getInstanceOrNull(null, new PersistableBundle(), null));
assertThat(testUserState.equals(oldUserState), is(false));
oldUserState = new PackageUserStateImpl();
@@ -231,7 +232,6 @@
final PackageUserStateImpl testUserState1 = new PackageUserStateImpl();
- testUserState1.setSuspended(true);
testUserState1.setSuspendParams(paramsMap1);
PackageUserStateImpl testUserState2 =
diff --git a/services/tests/wmtests/src/com/android/server/wm/SplashScreenExceptionListTest.java b/services/tests/wmtests/src/com/android/server/wm/SplashScreenExceptionListTest.java
index 3714d99..f5d915d 100644
--- a/services/tests/wmtests/src/com/android/server/wm/SplashScreenExceptionListTest.java
+++ b/services/tests/wmtests/src/com/android/server/wm/SplashScreenExceptionListTest.java
@@ -39,6 +39,7 @@
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.TimeUnit;
+import java.util.function.Consumer;
/**
* Test for the splash screen exception list
@@ -55,7 +56,16 @@
private DeviceConfig.Properties mInitialWindowManagerProperties;
private final HandlerExecutor mExecutor = new HandlerExecutor(
new Handler(Looper.getMainLooper()));
- private final SplashScreenExceptionList mList = new SplashScreenExceptionList(mExecutor);
+ private final SplashScreenExceptionList mList = new SplashScreenExceptionList(mExecutor) {
+ @Override
+ void updateDeviceConfig(String rawList) {
+ super.updateDeviceConfig(rawList);
+ if (mOnUpdateDeviceConfig != null) {
+ mOnUpdateDeviceConfig.accept(rawList);
+ }
+ }
+ };
+ private Consumer<String> mOnUpdateDeviceConfig;
@Before
public void setUp() throws Exception {
@@ -91,13 +101,11 @@
private void setExceptionListAndWaitForCallback(String commaSeparatedList) {
CountDownLatch latch = new CountDownLatch(1);
- DeviceConfig.OnPropertiesChangedListener onPropertiesChangedListener = (p) -> {
- if (commaSeparatedList.equals(p.getString(KEY_SPLASH_SCREEN_EXCEPTION_LIST, null))) {
+ mOnUpdateDeviceConfig = rawList -> {
+ if (commaSeparatedList.equals(rawList)) {
latch.countDown();
}
};
- DeviceConfig.addOnPropertiesChangedListener(DeviceConfig.NAMESPACE_WINDOW_MANAGER,
- mExecutor, onPropertiesChangedListener);
DeviceConfig.setProperty(DeviceConfig.NAMESPACE_WINDOW_MANAGER,
KEY_SPLASH_SCREEN_EXCEPTION_LIST, commaSeparatedList, false);
try {
@@ -105,8 +113,6 @@
latch.await(1, TimeUnit.SECONDS));
} catch (InterruptedException e) {
Assert.fail(e.getMessage());
- } finally {
- DeviceConfig.removeOnPropertiesChangedListener(onPropertiesChangedListener);
}
}
diff --git a/services/tests/wmtests/src/com/android/server/wm/TaskStackChangedListenerTest.java b/services/tests/wmtests/src/com/android/server/wm/TaskStackChangedListenerTest.java
index a1d0eb8..790b154 100644
--- a/services/tests/wmtests/src/com/android/server/wm/TaskStackChangedListenerTest.java
+++ b/services/tests/wmtests/src/com/android/server/wm/TaskStackChangedListenerTest.java
@@ -25,11 +25,10 @@
import static com.android.server.wm.utils.CommonUtils.runWithShellPermissionIdentity;
import static org.junit.Assert.assertEquals;
-import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertNotNull;
-import static org.junit.Assert.assertTrue;
import android.app.Activity;
+import android.app.ActivityManager.RunningTaskInfo;
import android.app.ActivityManager.TaskDescription;
import android.app.ActivityOptions;
import android.app.ActivityTaskManager;
@@ -57,7 +56,6 @@
import androidx.test.filters.MediumTest;
import org.junit.After;
-import org.junit.Before;
import org.junit.Test;
import java.util.Arrays;
@@ -73,49 +71,42 @@
@MediumTest
public class TaskStackChangedListenerTest {
- private static final int VIRTUAL_DISPLAY_WIDTH = 800;
- private static final int VIRTUAL_DISPLAY_HEIGHT = 600;
- private static final int VIRTUAL_DISPLAY_DENSITY = 160;
-
private ITaskStackListener mTaskStackListener;
- private DisplayManager mDisplayManager;
private VirtualDisplay mVirtualDisplay;
+ private ImageReader mImageReader;
private static final int WAIT_TIMEOUT_MS = 5000;
private static final Object sLock = new Object();
- @Before
- public void setUp() {
- mDisplayManager = getInstrumentation().getContext().getSystemService(
- DisplayManager.class);
- mVirtualDisplay = createVirtualDisplay(
- getClass().getSimpleName() + "_virtualDisplay",
- VIRTUAL_DISPLAY_WIDTH, VIRTUAL_DISPLAY_HEIGHT, VIRTUAL_DISPLAY_DENSITY);
- }
-
@After
public void tearDown() throws Exception {
- ActivityTaskManager.getService().unregisterTaskStackListener(mTaskStackListener);
- mTaskStackListener = null;
- mVirtualDisplay.release();
+ if (mTaskStackListener != null) {
+ ActivityTaskManager.getService().unregisterTaskStackListener(mTaskStackListener);
+ }
+ if (mVirtualDisplay != null) {
+ mVirtualDisplay.release();
+ mImageReader.close();
+ }
}
- private VirtualDisplay createVirtualDisplay(String name, int width, int height, int density) {
- VirtualDisplay virtualDisplay = null;
- try (ImageReader reader = ImageReader.newInstance(width, height,
- /* format= */ PixelFormat.RGBA_8888, /* maxImages= */ 2)) {
- int flags = VIRTUAL_DISPLAY_FLAG_PRESENTATION | VIRTUAL_DISPLAY_FLAG_OWN_CONTENT_ONLY
- | VIRTUAL_DISPLAY_FLAG_PUBLIC;
- virtualDisplay = mDisplayManager.createVirtualDisplay(
- name, width, height, density, reader.getSurface(), flags);
- virtualDisplay.setSurface(reader.getSurface());
- }
- assertTrue("display id must be unique",
- virtualDisplay.getDisplay().getDisplayId() != Display.DEFAULT_DISPLAY);
+ private VirtualDisplay createVirtualDisplay() {
+ final int width = 800;
+ final int height = 600;
+ final int density = 160;
+ final DisplayManager displayManager = getInstrumentation().getContext().getSystemService(
+ DisplayManager.class);
+ mImageReader = ImageReader.newInstance(width, height, PixelFormat.RGBA_8888,
+ 2 /* maxImages */);
+ final int flags = VIRTUAL_DISPLAY_FLAG_PRESENTATION | VIRTUAL_DISPLAY_FLAG_OWN_CONTENT_ONLY
+ | VIRTUAL_DISPLAY_FLAG_PUBLIC;
+ final String name = getClass().getSimpleName() + "_VirtualDisplay";
+ mVirtualDisplay = displayManager.createVirtualDisplay(name, width, height, density,
+ mImageReader.getSurface(), flags);
+ mVirtualDisplay.setSurface(mImageReader.getSurface());
assertNotNull("display must be registered",
- Arrays.asList(mDisplayManager.getDisplays()).stream().filter(
+ Arrays.stream(displayManager.getDisplays()).filter(
d -> d.getName().equals(name)).findAny());
- return virtualDisplay;
+ return mVirtualDisplay;
}
@Test
@@ -163,11 +154,10 @@
mTaskId = taskId;
}
@Override
- public void onTaskDescriptionChanged(int taskId, TaskDescription td)
- throws RemoteException {
- if (mTaskId == taskId && !TextUtils.isEmpty(td.getLabel())) {
- params[0] = taskId;
- params[1] = td;
+ public void onTaskDescriptionChanged(RunningTaskInfo info) {
+ if (mTaskId == info.taskId && !TextUtils.isEmpty(info.taskDescription.getLabel())) {
+ params[0] = info.taskId;
+ params[1] = info.taskDescription;
latch.countDown();
}
}
@@ -211,75 +201,71 @@
@Test
@Presubmit
public void testTaskChangeCallBacks() throws Exception {
- final Object[] params = new Object[2];
final CountDownLatch taskCreatedLaunchLatch = new CountDownLatch(1);
final CountDownLatch taskMovedToFrontLatch = new CountDownLatch(1);
final CountDownLatch taskRemovedLatch = new CountDownLatch(1);
final CountDownLatch taskRemovalStartedLatch = new CountDownLatch(1);
- final CountDownLatch onDetachedFromWindowLatch = new CountDownLatch(1);
+ final int[] expectedTaskId = { -1 };
+ final int[] receivedTaskId = { -1 };
+ final ComponentName expectedName = new ComponentName(getInstrumentation().getContext(),
+ ActivityTaskChangeCallbacks.class);
registerTaskStackChangedListener(new TaskStackListener() {
@Override
- public void onTaskCreated(int taskId, ComponentName componentName)
- throws RemoteException {
- params[0] = taskId;
- params[1] = componentName;
- taskCreatedLaunchLatch.countDown();
+ public void onTaskCreated(int taskId, ComponentName componentName) {
+ receivedTaskId[0] = taskId;
+ if (expectedName.equals(componentName)) {
+ taskCreatedLaunchLatch.countDown();
+ }
}
@Override
- public void onTaskMovedToFront(int taskId) throws RemoteException {
- params[0] = taskId;
+ public void onTaskMovedToFront(RunningTaskInfo info) {
+ receivedTaskId[0] = info.taskId;
taskMovedToFrontLatch.countDown();
}
@Override
- public void onTaskRemovalStarted(int taskId) {
- params[0] = taskId;
- taskRemovalStartedLatch.countDown();
+ public void onTaskRemovalStarted(RunningTaskInfo info) {
+ if (expectedTaskId[0] == info.taskId) {
+ taskRemovalStartedLatch.countDown();
+ }
}
@Override
- public void onTaskRemoved(int taskId) throws RemoteException {
- if (taskCreatedLaunchLatch.getCount() == 1) {
- // The test activity hasn't started. Ignore the noise from previous test.
- return;
+ public void onTaskRemoved(int taskId) {
+ if (expectedTaskId[0] == taskId) {
+ taskRemovedLatch.countDown();
}
- params[0] = taskId;
- taskRemovedLatch.countDown();
}
});
final ActivityTaskChangeCallbacks activity =
(ActivityTaskChangeCallbacks) startTestActivity(ActivityTaskChangeCallbacks.class);
- activity.setDetachedFromWindowLatch(onDetachedFromWindowLatch);
- final int id = activity.getTaskId();
+ expectedTaskId[0] = activity.getTaskId();
// Test for onTaskCreated and onTaskMovedToFront
waitForCallback(taskMovedToFrontLatch);
assertEquals(0, taskCreatedLaunchLatch.getCount());
- assertEquals(id, params[0]);
- ComponentName componentName = (ComponentName) params[1];
- assertEquals(ActivityTaskChangeCallbacks.class.getName(), componentName.getClassName());
+ assertEquals(expectedTaskId[0], receivedTaskId[0]);
+ // Ensure that the window is attached before removal so there will be a detached callback.
+ waitForCallback(activity.mOnAttachedToWindowCountDownLatch);
// Test for onTaskRemovalStarted.
assertEquals(1, taskRemovalStartedLatch.getCount());
assertEquals(1, taskRemovedLatch.getCount());
activity.finishAndRemoveTask();
waitForCallback(taskRemovalStartedLatch);
// onTaskRemovalStarted happens before the activity's window is removed.
- assertFalse(activity.mOnDetachedFromWindowCalled);
- assertEquals(id, params[0]);
+ assertEquals(1, activity.mOnDetachedFromWindowCountDownLatch.getCount());
// Test for onTaskRemoved.
waitForCallback(taskRemovedLatch);
- assertEquals(id, params[0]);
- waitForCallback(onDetachedFromWindowLatch);
- assertTrue(activity.mOnDetachedFromWindowCalled);
+ waitForCallback(activity.mOnDetachedFromWindowCountDownLatch);
}
@Test
public void testTaskDisplayChanged() throws Exception {
- int virtualDisplayId = mVirtualDisplay.getDisplay().getDisplayId();
+ final int virtualDisplayId = createVirtualDisplay().getDisplay().getDisplayId();
// Launch a Activity inside VirtualDisplay
CountDownLatch displayChangedLatch1 = new CountDownLatch(1);
@@ -498,18 +484,18 @@
}
public static class ActivityTaskChangeCallbacks extends TestActivity {
- public boolean mOnDetachedFromWindowCalled = false;
- private CountDownLatch mOnDetachedFromWindowCountDownLatch;
+ final CountDownLatch mOnAttachedToWindowCountDownLatch = new CountDownLatch(1);
+ final CountDownLatch mOnDetachedFromWindowCountDownLatch = new CountDownLatch(1);
+
+ @Override
+ public void onAttachedToWindow() {
+ mOnAttachedToWindowCountDownLatch.countDown();
+ }
@Override
public void onDetachedFromWindow() {
- mOnDetachedFromWindowCalled = true;
mOnDetachedFromWindowCountDownLatch.countDown();
}
-
- void setDetachedFromWindowLatch(CountDownLatch countDownLatch) {
- mOnDetachedFromWindowCountDownLatch = countDownLatch;
- }
}
public static class ActivityInVirtualDisplay extends TestActivity {
diff --git a/services/tests/wmtests/src/com/android/server/wm/TransitionTests.java b/services/tests/wmtests/src/com/android/server/wm/TransitionTests.java
index ec6cd92..ed3888c 100644
--- a/services/tests/wmtests/src/com/android/server/wm/TransitionTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/TransitionTests.java
@@ -556,8 +556,15 @@
// The redrawn window will be faded in when the transition finishes. And because this test
// only use one non-activity window, the fade rotation controller should also be cleared.
- statusBar.mWinAnimator.mDrawState = WindowStateAnimator.HAS_DRAWN;
+ statusBar.mWinAnimator.mDrawState = WindowStateAnimator.DRAW_PENDING;
+ final SurfaceControl.Transaction postDrawTransaction =
+ mock(SurfaceControl.Transaction.class);
+ final boolean layoutNeeded = statusBar.finishDrawing(postDrawTransaction);
+ assertFalse(layoutNeeded);
player.finish();
+ // The controller should capture the draw transaction and merge it when preparing to run
+ // fade-in animation.
+ verify(mDisplayContent.getPendingTransaction()).merge(eq(postDrawTransaction));
assertNull(mDisplayContent.getFadeRotationAnimationController());
}
diff --git a/telephony/java/android/telephony/CarrierConfigManager.java b/telephony/java/android/telephony/CarrierConfigManager.java
index 7d17894..82113f2 100644
--- a/telephony/java/android/telephony/CarrierConfigManager.java
+++ b/telephony/java/android/telephony/CarrierConfigManager.java
@@ -4181,6 +4181,56 @@
"gba_ua_tls_cipher_suite_int";
/**
+ * The data stall recovery timers array in milliseconds, each element is the delay before
+ * performining next recovery action.
+ *
+ * The default value of timers array are: [180000ms, 180000ms, 180000ms] (3 minutes)
+ * Array[0]: It's the timer between RECOVERY_ACTION GET_DATA_CALL_LIST and CLEANUP, if data
+ * stall symptom still occurred, it will perform next recovery action after 180000ms.
+ * Array[1]: It's the timer between RECOVERY_ACTION CLEANUP and RADIO_RESTART, if data stall
+ * symptom still occurred, it will perform next recovery action after 180000ms.
+ * Array[2]: It's the timer between RECOVERY_ACTION RADIO_RESTART and RESET_MODEM, if data stall
+ * symptom still occurred, it will perform next recovery action after 180000ms.
+ *
+ * See the {@code RECOVERY_ACTION_*} constants in
+ * {@link com.android.internal.telephony.data.DataStallRecoveryManager}
+ * @hide
+ */
+ public static final String KEY_DATA_STALL_RECOVERY_TIMERS_LONG_ARRAY =
+ "data_stall_recovery_timers_long_array";
+
+ /**
+ * The data stall recovery action boolean array, we use this array to determine if the
+ * data stall recovery action needs to be skipped.
+ *
+ * For example, if the carrier use the same APN for both of IA and default type,
+ * the data call will not disconnect in modem side (so the RECOVERY_ACTION_CLEANUP
+ * did not effect). In this case, we can config the boolean variable of action
+ * RECOVERY_ACTION_CLEANUP to true, then it can be ignored to speed up the recovery
+ * action procedure.
+ *
+ * The default value of boolean array are: [false, false, false, false]
+ * Array[0]: When performing the recovery action, we can use this boolean value to determine
+ * if we need to perform RECOVERY_ACTION_GET_DATA_CALL_LIST.
+ * Array[1]: If data stall symptom still occurred, we can use this boolean value to determine
+ * if we need to perform RECOVERY_ACTION_CLEANUP. For example, if the carrier use the same APN
+ * for both of IA and default type, the data call will not disconnect in modem side
+ * (so the RECOVERY_ACTION_CLEANUP did not effect). In this case, we can config the boolean
+ * variable of action RECOVERY_ACTION_CLEANUP to true, then it can be ignored to speed up the
+ * recovery action procedure.
+ * Array[2]: If data stall symptom still occurred, we can use this boolean value to determine
+ * if we need to perform RECOVERY_ACTION_RADIO_RESTART.
+ * Array[3]: If data stall symptom still occurred, we can use this boolean value to determine
+ * if we need to perform RECOVERY_ACTION_MODEM_RESET.
+ *
+ * See the {@code RECOVERY_ACTION_*} constants in
+ * {@link com.android.internal.telephony.data.DataStallRecoveryManager}
+ * @hide
+ */
+ public static final String KEY_DATA_STALL_RECOVERY_SHOULD_SKIP_BOOL_ARRAY =
+ "data_stall_recovery_should_skip_bool_array";
+
+ /**
* Configs used by ImsServiceEntitlement.
*/
public static final class ImsServiceEntitlement {
@@ -6172,6 +6222,11 @@
+ "target=GERAN|UTRAN|EUTRAN|NGRAN|IWLAN, type=allowed"});
sDefaults.putInt(KEY_CELLULAR_USAGE_SETTING_INT,
SubscriptionManager.USAGE_SETTING_UNKNOWN);
+ // Default data stall recovery configurations.
+ sDefaults.putLongArray(KEY_DATA_STALL_RECOVERY_TIMERS_LONG_ARRAY,
+ new long[] {180000, 180000, 180000});
+ sDefaults.putBooleanArray(KEY_DATA_STALL_RECOVERY_SHOULD_SKIP_BOOL_ARRAY,
+ new boolean[] {false, false, false, false});
}
/**