Merge changes I1bada812,Ia50e462d
* changes:
Automatically run LocaleManager CTS cases for relevant changes.
Automatically run LocaleManager CTS cases for relevant changes.
diff --git a/cmds/idmap2/OWNERS b/cmds/idmap2/OWNERS
index 69dfcc9..062ffd4 100644
--- a/cmds/idmap2/OWNERS
+++ b/cmds/idmap2/OWNERS
@@ -1,4 +1,4 @@
set noparent
toddke@google.com
-rtmitchell@google.com
-patb@google.com
\ No newline at end of file
+patb@google.com
+zyy@google.com
diff --git a/core/api/current.txt b/core/api/current.txt
index 7370af3..44af059 100644
--- a/core/api/current.txt
+++ b/core/api/current.txt
@@ -31636,6 +31636,8 @@
method @Nullable public double[] createDoubleArray();
method @Nullable public float[] createFloatArray();
method @Nullable public int[] createIntArray();
+ method @Nullable public <T extends android.os.IInterface> T[] createInterfaceArray(@NonNull java.util.function.IntFunction<T[]>, @NonNull java.util.function.Function<android.os.IBinder,T>);
+ method @Nullable public <T extends android.os.IInterface> java.util.ArrayList<T> createInterfaceArrayList(@NonNull java.util.function.Function<android.os.IBinder,T>);
method @Nullable public long[] createLongArray();
method @Nullable public String[] createStringArray();
method @Nullable public java.util.ArrayList<java.lang.String> createStringArrayList();
@@ -31676,6 +31678,8 @@
method @Nullable public java.util.HashMap readHashMap(@Nullable ClassLoader);
method public int readInt();
method public void readIntArray(@NonNull int[]);
+ method public <T extends android.os.IInterface> void readInterfaceArray(@NonNull T[], @NonNull java.util.function.Function<android.os.IBinder,T>);
+ method public <T extends android.os.IInterface> void readInterfaceList(@NonNull java.util.List<T>, @NonNull java.util.function.Function<android.os.IBinder,T>);
method public void readList(@NonNull java.util.List, @Nullable ClassLoader);
method public <T> void readList(@NonNull java.util.List<? super T>, @Nullable ClassLoader, @NonNull Class<T>);
method public long readLong();
@@ -31728,6 +31732,8 @@
method public void writeFloatArray(@Nullable float[]);
method public void writeInt(int);
method public void writeIntArray(@Nullable int[]);
+ method public <T extends android.os.IInterface> void writeInterfaceArray(@Nullable T[]);
+ method public <T extends android.os.IInterface> void writeInterfaceList(@Nullable java.util.List<T>);
method public void writeInterfaceToken(@NonNull String);
method public void writeList(@Nullable java.util.List);
method public void writeLong(long);
@@ -40972,11 +40978,11 @@
}
public class CarrierConfigManager {
- method @Nullable public android.os.PersistableBundle getConfig();
- method @Nullable public android.os.PersistableBundle getConfigByComponentForSubId(@NonNull String, int);
- method @Nullable public android.os.PersistableBundle getConfigForSubId(int);
+ method @Nullable @RequiresPermission(android.Manifest.permission.READ_PHONE_STATE) public android.os.PersistableBundle getConfig();
+ method @Nullable @RequiresPermission(android.Manifest.permission.READ_PHONE_STATE) public android.os.PersistableBundle getConfigByComponentForSubId(@NonNull String, int);
+ method @Nullable @RequiresPermission(android.Manifest.permission.READ_PHONE_STATE) public android.os.PersistableBundle getConfigForSubId(int);
method public static boolean isConfigForIdentifiedCarrier(android.os.PersistableBundle);
- method public void notifyConfigChangedForSubId(int);
+ method @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public void notifyConfigChangedForSubId(int);
field public static final String ACTION_CARRIER_CONFIG_CHANGED = "android.telephony.action.CARRIER_CONFIG_CHANGED";
field public static final int CARRIER_NR_AVAILABILITY_NSA = 1; // 0x1
field public static final int CARRIER_NR_AVAILABILITY_SA = 2; // 0x2
diff --git a/core/api/test-current.txt b/core/api/test-current.txt
index da9fd3e..591fe55 100644
--- a/core/api/test-current.txt
+++ b/core/api/test-current.txt
@@ -114,7 +114,7 @@
method @RequiresPermission(android.Manifest.permission.RESET_APP_ERRORS) public void resetAppErrors();
method public static void resumeAppSwitches() throws android.os.RemoteException;
method @RequiresPermission(android.Manifest.permission.CHANGE_CONFIGURATION) public void scheduleApplicationInfoChanged(java.util.List<java.lang.String>, int);
- method @RequiresPermission(anyOf={android.Manifest.permission.MANAGE_USERS, android.Manifest.permission.INTERACT_ACROSS_USERS_FULL}) public void setStopBackgroundUsersOnSwitch(int);
+ method @RequiresPermission(anyOf={android.Manifest.permission.MANAGE_USERS, android.Manifest.permission.INTERACT_ACROSS_USERS_FULL}) public void setStopUserOnSwitch(int);
method @RequiresPermission(android.Manifest.permission.INTERACT_ACROSS_USERS_FULL) public boolean stopUser(int, boolean);
method @RequiresPermission(android.Manifest.permission.CHANGE_CONFIGURATION) public boolean updateMccMncConfiguration(@NonNull String, @NonNull String);
method @RequiresPermission(android.Manifest.permission.DUMP) public void waitForBroadcastIdle();
@@ -129,9 +129,9 @@
field public static final int PROCESS_CAPABILITY_NONE = 0; // 0x0
field public static final int PROCESS_STATE_FOREGROUND_SERVICE = 4; // 0x4
field public static final int PROCESS_STATE_TOP = 2; // 0x2
- field public static final int STOP_BG_USERS_ON_SWITCH_DEFAULT = -1; // 0xffffffff
- field public static final int STOP_BG_USERS_ON_SWITCH_FALSE = 0; // 0x0
- field public static final int STOP_BG_USERS_ON_SWITCH_TRUE = 1; // 0x1
+ field public static final int STOP_USER_ON_SWITCH_DEFAULT = -1; // 0xffffffff
+ field public static final int STOP_USER_ON_SWITCH_FALSE = 0; // 0x0
+ field public static final int STOP_USER_ON_SWITCH_TRUE = 1; // 0x1
}
public static class ActivityManager.RunningAppProcessInfo implements android.os.Parcelable {
@@ -1198,7 +1198,7 @@
public final class DisplayManager {
method public boolean areUserDisabledHdrTypesAllowed();
- method @RequiresPermission(android.Manifest.permission.WRITE_SECURE_SETTINGS) public void clearUserPreferredDisplayMode();
+ method @RequiresPermission(android.Manifest.permission.MODIFY_USER_PREFERRED_DISPLAY_MODE) public void clearUserPreferredDisplayMode();
method @NonNull public int[] getUserDisabledHdrTypes();
method @Nullable public android.view.Display.Mode getUserPreferredDisplayMode();
method public boolean isMinimalPostProcessingRequested(int);
@@ -1206,7 +1206,7 @@
method @RequiresPermission(android.Manifest.permission.MODIFY_REFRESH_RATE_SWITCHING_TYPE) public void setRefreshRateSwitchingType(int);
method @RequiresPermission(android.Manifest.permission.OVERRIDE_DISPLAY_MODE_REQUESTS) public void setShouldAlwaysRespectAppRequestedMode(boolean);
method @RequiresPermission(android.Manifest.permission.WRITE_SECURE_SETTINGS) public void setUserDisabledHdrTypes(@NonNull int[]);
- method @RequiresPermission(android.Manifest.permission.WRITE_SECURE_SETTINGS) public void setUserPreferredDisplayMode(@NonNull android.view.Display.Mode);
+ method @RequiresPermission(android.Manifest.permission.MODIFY_USER_PREFERRED_DISPLAY_MODE) public void setUserPreferredDisplayMode(@NonNull android.view.Display.Mode);
method @RequiresPermission(android.Manifest.permission.OVERRIDE_DISPLAY_MODE_REQUESTS) public boolean shouldAlwaysRespectAppRequestedMode();
field public static final int SWITCHING_TYPE_ACROSS_AND_WITHIN_GROUPS = 2; // 0x2
field public static final int SWITCHING_TYPE_NONE = 0; // 0x0
diff --git a/core/java/android/app/ActivityManager.java b/core/java/android/app/ActivityManager.java
index d77227b..d7ce3b8 100644
--- a/core/java/android/app/ActivityManager.java
+++ b/core/java/android/app/ActivityManager.java
@@ -4087,45 +4087,46 @@
* @hide
*/
@TestApi
- public static final int STOP_BG_USERS_ON_SWITCH_DEFAULT = -1;
+ public static final int STOP_USER_ON_SWITCH_DEFAULT = -1;
/**
- * Overrides value defined by the platform and stop background users on switch.
+ * Overrides value defined by the platform and stop user on switch.
*
* @hide
*/
@TestApi
- public static final int STOP_BG_USERS_ON_SWITCH_TRUE = 1;
+ public static final int STOP_USER_ON_SWITCH_TRUE = 1;
/**
- * Overrides value defined by the platform and don't stop background users on switch.
+ * Overrides value defined by the platform and don't stop user on switch.
*
* @hide
*/
@TestApi
- public static final int STOP_BG_USERS_ON_SWITCH_FALSE = 0;
+ public static final int STOP_USER_ON_SWITCH_FALSE = 0;
/** @hide */
- @IntDef(prefix = { "STOP_BG_USERS_ON_SWITCH_" }, value = {
- STOP_BG_USERS_ON_SWITCH_DEFAULT,
- STOP_BG_USERS_ON_SWITCH_TRUE,
- STOP_BG_USERS_ON_SWITCH_FALSE
+ @IntDef(prefix = { "STOP_USER_ON_SWITCH_" }, value = {
+ STOP_USER_ON_SWITCH_DEFAULT,
+ STOP_USER_ON_SWITCH_TRUE,
+ STOP_USER_ON_SWITCH_FALSE
})
- public @interface StopBgUsersOnSwitch {}
+ public @interface StopUserOnSwitch {}
/**
- * Sets whether background users should be stopped when the current user is switched.
+ * Sets whether the current foreground user (and its profiles) should be stopped after switched
+ * out.
*
- * <p>Should only be used on tests.
+ * <p>Should only be used on tests. Doesn't apply to {@link UserHandle#SYSTEM system user}.
*
* @hide
*/
@TestApi
@RequiresPermission(anyOf = {android.Manifest.permission.MANAGE_USERS,
android.Manifest.permission.INTERACT_ACROSS_USERS_FULL})
- public void setStopBackgroundUsersOnSwitch(@StopBgUsersOnSwitch int value) {
+ public void setStopUserOnSwitch(@StopUserOnSwitch int value) {
try {
- getService().setStopBackgroundUsersOnSwitch(value);
+ getService().setStopUserOnSwitch(value);
} catch (RemoteException re) {
throw re.rethrowFromSystemServer();
}
diff --git a/core/java/android/app/ActivityManagerInternal.java b/core/java/android/app/ActivityManagerInternal.java
index 7dbcd5f..324e1ae 100644
--- a/core/java/android/app/ActivityManagerInternal.java
+++ b/core/java/android/app/ActivityManagerInternal.java
@@ -16,7 +16,7 @@
package android.app;
-import static android.app.ActivityManager.StopBgUsersOnSwitch;
+import static android.app.ActivityManager.StopUserOnSwitch;
import android.annotation.NonNull;
import android.annotation.Nullable;
@@ -688,9 +688,10 @@
@Nullable VoiceInteractionManagerProvider provider);
/**
- * Sets whether background users should be stopped when the current user is switched.
+ * Sets whether the current foreground user (and its profiles) should be stopped after switched
+ * out.
*/
- public abstract void setStopBackgroundUsersOnSwitch(@StopBgUsersOnSwitch int value);
+ public abstract void setStopUserOnSwitch(@StopUserOnSwitch int value);
/**
* Provides the interface to communicate between voice interaction manager service and
diff --git a/core/java/android/app/IActivityManager.aidl b/core/java/android/app/IActivityManager.aidl
index de79a64..853d5e8 100644
--- a/core/java/android/app/IActivityManager.aidl
+++ b/core/java/android/app/IActivityManager.aidl
@@ -26,6 +26,7 @@
import android.app.IApplicationThread;
import android.app.IActivityController;
import android.app.IAppTask;
+import android.app.IForegroundServiceObserver;
import android.app.IInstrumentationWatcher;
import android.app.IProcessObserver;
import android.app.IServiceConnection;
@@ -279,6 +280,8 @@
boolean clearApplicationUserData(in String packageName, boolean keepState,
in IPackageDataObserver observer, int userId);
void makeServicesNonForeground(in String packageName, int userId);
+ /** Returns {@code false} if the callback could not be registered, {@true} otherwise. */
+ boolean registerForegroundServiceObserver(in IForegroundServiceObserver callback);
@UnsupportedAppUsage
void forceStopPackage(in String packageName, int userId);
boolean killPids(in int[] pids, in String reason, boolean secure);
@@ -349,7 +352,7 @@
@UnsupportedAppUsage
boolean switchUser(int userid);
@UnsupportedAppUsage
- void setStopBackgroundUsersOnSwitch(int value);
+ void setStopUserOnSwitch(int value);
boolean removeTask(int taskId);
@UnsupportedAppUsage
void registerProcessObserver(in IProcessObserver observer);
diff --git a/core/java/android/app/IForegroundServiceObserver.aidl b/core/java/android/app/IForegroundServiceObserver.aidl
new file mode 100644
index 0000000..2b0cbed
--- /dev/null
+++ b/core/java/android/app/IForegroundServiceObserver.aidl
@@ -0,0 +1,31 @@
+/*
+ * 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.app;
+
+/**
+ * Notify the client of all changes to services' foreground state.
+ * @param serviceToken unique identifier for a service instance
+ * @param packageName identifies the app hosting the service
+ * @param userId identifies the started user in which the app is running
+ * @param isForeground whether the service is in the "foreground" mode now, i.e.
+ * whether it is an FGS
+ *
+ * @hide
+ */
+oneway interface IForegroundServiceObserver {
+ void onForegroundStateChanged(in IBinder serviceToken, in String packageName, int userId, boolean isForeground);
+}
diff --git a/core/java/android/app/OWNERS b/core/java/android/app/OWNERS
index 1909f3c..1da0a74 100644
--- a/core/java/android/app/OWNERS
+++ b/core/java/android/app/OWNERS
@@ -15,6 +15,7 @@
per-file IActivityManager.aidl = file:/services/core/java/com/android/server/am/OWNERS
per-file IApplicationThread.aidl = file:/services/core/java/com/android/server/am/OWNERS
per-file IAppTraceRetriever.aidl = file:/services/core/java/com/android/server/am/OWNERS
+per-file IForegroundServiceObserver.aidl = file:/services/core/java/com/android/server/am/OWNERS
per-file IInstrumentationWatcher.aidl = file:/services/core/java/com/android/server/am/OWNERS
per-file IntentService.aidl = file:/services/core/java/com/android/server/am/OWNERS
per-file IServiceConnection.aidl = file:/services/core/java/com/android/server/am/OWNERS
diff --git a/core/java/android/appwidget/AppWidgetHostView.java b/core/java/android/appwidget/AppWidgetHostView.java
index 7c2b1b7..8be2b48 100644
--- a/core/java/android/appwidget/AppWidgetHostView.java
+++ b/core/java/android/appwidget/AppWidgetHostView.java
@@ -97,7 +97,8 @@
AppWidgetProviderInfo mInfo;
View mView;
int mViewMode = VIEW_MODE_NOINIT;
- int mLayoutId = -1;
+ // If true, we should not try to re-apply the RemoteViews on the next inflation.
+ boolean mColorMappingChanged = false;
private InteractionHandler mInteractionHandler;
private boolean mOnLightBackground;
private SizeF mCurrentSize = null;
@@ -540,7 +541,6 @@
return;
}
content = getDefaultView();
- mLayoutId = -1;
mViewMode = VIEW_MODE_DEFAULT;
} else {
// Select the remote view we are actually going to apply.
@@ -557,8 +557,7 @@
// inflate any requested LayoutParams.
mRemoteContext = getRemoteContextEnsuringCorrectCachedApkPath();
- int layoutId = rvToApply.getLayoutId();
- if (rvToApply.canRecycleView(mView)) {
+ if (!mColorMappingChanged && rvToApply.canRecycleView(mView)) {
try {
rvToApply.reapply(mContext, mView, mInteractionHandler, mCurrentSize,
mColorResources);
@@ -583,7 +582,6 @@
}
}
- mLayoutId = layoutId;
mViewMode = VIEW_MODE_CONTENT;
}
@@ -591,6 +589,7 @@
}
private void applyContent(View content, boolean recycled, Exception exception) {
+ mColorMappingChanged = false;
if (content == null) {
if (mViewMode == VIEW_MODE_ERROR) {
// We've already done this -- nothing to do.
@@ -626,7 +625,7 @@
// If our stale view has been prepared to match active, and the new
// layout matches, try recycling it
- if (remoteViews.canRecycleView(mView)) {
+ if (!mColorMappingChanged && remoteViews.canRecycleView(mView)) {
try {
mLastExecutionSignal = remoteViews.reapplyAsync(mContext,
mView,
@@ -666,7 +665,6 @@
@Override
public void onViewApplied(View v) {
- AppWidgetHostView.this.mLayoutId = mLayoutId;
mViewMode = VIEW_MODE_CONTENT;
applyContent(v, mIsReapply, null);
@@ -907,7 +905,7 @@
}
mColorMapping = colorMapping.clone();
mColorResources = RemoteViews.ColorResources.create(mContext, mColorMapping);
- mLayoutId = -1;
+ mColorMappingChanged = true;
mViewMode = VIEW_MODE_NOINIT;
reapplyLastRemoteViews();
}
@@ -937,7 +935,7 @@
if (mColorResources != null) {
mColorResources = null;
mColorMapping = null;
- mLayoutId = -1;
+ mColorMappingChanged = true;
mViewMode = VIEW_MODE_NOINIT;
reapplyLastRemoteViews();
}
diff --git a/core/java/android/content/om/OWNERS b/core/java/android/content/om/OWNERS
index 91a0abf..3669817 100644
--- a/core/java/android/content/om/OWNERS
+++ b/core/java/android/content/om/OWNERS
@@ -3,4 +3,4 @@
toddke@android.com
toddke@google.com
patb@google.com
-rtmitchell@google.com
+zyy@google.com
diff --git a/core/java/android/content/res/OWNERS b/core/java/android/content/res/OWNERS
index bc2355c..d12d920 100644
--- a/core/java/android/content/res/OWNERS
+++ b/core/java/android/content/res/OWNERS
@@ -3,4 +3,4 @@
toddke@android.com
toddke@google.com
patb@google.com
-rtmitchell@google.com
+zyy@google.com
diff --git a/core/java/android/hardware/display/DisplayManager.java b/core/java/android/hardware/display/DisplayManager.java
index c7f3983..4481885 100644
--- a/core/java/android/hardware/display/DisplayManager.java
+++ b/core/java/android/hardware/display/DisplayManager.java
@@ -1119,7 +1119,7 @@
* @hide
*/
@TestApi
- @RequiresPermission(Manifest.permission.WRITE_SECURE_SETTINGS)
+ @RequiresPermission(Manifest.permission.MODIFY_USER_PREFERRED_DISPLAY_MODE)
public void setUserPreferredDisplayMode(@NonNull Display.Mode mode) {
// Create a new object containing default values for the unused fields like mode ID and
// alternative refresh rates.
@@ -1134,7 +1134,7 @@
* @hide
*/
@TestApi
- @RequiresPermission(Manifest.permission.WRITE_SECURE_SETTINGS)
+ @RequiresPermission(Manifest.permission.MODIFY_USER_PREFERRED_DISPLAY_MODE)
public void clearUserPreferredDisplayMode() {
mGlobal.setUserPreferredDisplayMode(null);
}
diff --git a/core/java/android/hardware/display/IDisplayManager.aidl b/core/java/android/hardware/display/IDisplayManager.aidl
index b3be9da..0d5f1af 100644
--- a/core/java/android/hardware/display/IDisplayManager.aidl
+++ b/core/java/android/hardware/display/IDisplayManager.aidl
@@ -164,7 +164,7 @@
int getPreferredWideGamutColorSpaceId();
// Sets the user preferred display mode.
- // Requires WRITE_SECURE_SETTINGS permission.
+ // Requires MODIFY_USER_PREFERRED_DISPLAY_MODE permission.
void setUserPreferredDisplayMode(in Mode mode);
Mode getUserPreferredDisplayMode();
diff --git a/core/java/android/os/Parcel.java b/core/java/android/os/Parcel.java
index d1e6716..5a2f27d 100644
--- a/core/java/android/os/Parcel.java
+++ b/core/java/android/os/Parcel.java
@@ -62,6 +62,8 @@
import java.util.Map;
import java.util.Objects;
import java.util.Set;
+import java.util.function.Function;
+import java.util.function.IntFunction;
import java.util.function.Supplier;
/**
@@ -178,8 +180,12 @@
* {@link #writeStrongInterface(IInterface)}, {@link #readStrongBinder()},
* {@link #writeBinderArray(IBinder[])}, {@link #readBinderArray(IBinder[])},
* {@link #createBinderArray()},
+ * {@link #writeInterfaceArray(T[])}, {@link #readInterfaceArray(T[], Function)},
+ * {@link #createInterfaceArray(IntFunction, Function)},
* {@link #writeBinderList(List)}, {@link #readBinderList(List)},
- * {@link #createBinderArrayList()}.</p>
+ * {@link #createBinderArrayList()},
+ * {@link #writeInterfaceList(List)}, {@link #readInterfaceList(List, Function)},
+ * {@link #createInterfaceArrayList(Function)}.</p>
*
* <p>FileDescriptor objects, representing raw Linux file descriptor identifiers,
* can be written and {@link ParcelFileDescriptor} objects returned to operate
@@ -1730,6 +1736,30 @@
}
/**
+ * Flatten a homogeneous array containing an IInterface type into the parcel,
+ * at the current dataPosition() and growing dataCapacity() if needed. The
+ * type of the objects in the array must be one that implements IInterface.
+ *
+ * @param val The array of objects to be written.
+ *
+ * @see #createInterfaceArray
+ * @see #readInterfaceArray
+ * @see IInterface
+ */
+ public final <T extends IInterface> void writeInterfaceArray(
+ @SuppressLint("ArrayReturn") @Nullable T[] val) {
+ if (val != null) {
+ int N = val.length;
+ writeInt(N);
+ for (int i=0; i<N; i++) {
+ writeStrongInterface(val[i]);
+ }
+ } else {
+ writeInt(-1);
+ }
+ }
+
+ /**
* @hide
*/
public final void writeCharSequenceArray(@Nullable CharSequence[] val) {
@@ -1785,6 +1815,50 @@
}
/**
+ * Read and return a new array of T (IInterface) from the parcel.
+ *
+ * @return the IInterface array of type T
+ * @param newArray a function to create an array of T with a given length
+ * @param asInterface a function to convert IBinder object into T (IInterface)
+ */
+ @SuppressLint({"ArrayReturn", "NullableCollection", "SamShouldBeLast"})
+ @Nullable
+ public final <T extends IInterface> T[] createInterfaceArray(
+ @NonNull IntFunction<T[]> newArray, @NonNull Function<IBinder, T> asInterface) {
+ int N = readInt();
+ if (N >= 0) {
+ T[] val = newArray.apply(N);
+ for (int i=0; i<N; i++) {
+ val[i] = asInterface.apply(readStrongBinder());
+ }
+ return val;
+ } else {
+ return null;
+ }
+ }
+
+ /**
+ * Read an array of T (IInterface) from a parcel.
+ *
+ * @param asInterface a function to convert IBinder object into T (IInterface)
+ *
+ * @throws BadParcelableException Throws BadParcelableException if the length of `val`
+ * mismatches the number of items in the parcel.
+ */
+ public final <T extends IInterface> void readInterfaceArray(
+ @SuppressLint("ArrayReturn") @NonNull T[] val,
+ @NonNull Function<IBinder, T> asInterface) {
+ int N = readInt();
+ if (N == val.length) {
+ for (int i=0; i<N; i++) {
+ val[i] = asInterface.apply(readStrongBinder());
+ }
+ } else {
+ throw new BadParcelableException("bad array lengths");
+ }
+ }
+
+ /**
* Flatten a List containing a particular object type into the parcel, at
* the current dataPosition() and growing dataCapacity() if needed. The
* type of the objects in the list must be one that implements Parcelable.
@@ -1898,6 +1972,28 @@
}
/**
+ * Flatten a {@code List} containing T (IInterface) objects into this parcel
+ * at the current position. They can later be retrieved with
+ * {@link #createInterfaceArrayList} or {@link #readInterfaceList}.
+ *
+ * @see #createInterfaceArrayList
+ * @see #readInterfaceList
+ */
+ public final <T extends IInterface> void writeInterfaceList(@Nullable List<T> val) {
+ if (val == null) {
+ writeInt(-1);
+ return;
+ }
+ int N = val.size();
+ int i=0;
+ writeInt(N);
+ while (i < N) {
+ writeStrongInterface(val.get(i));
+ i++;
+ }
+ }
+
+ /**
* Flatten a {@code List} containing arbitrary {@code Parcelable} objects into this parcel
* at the current position. They can later be retrieved using
* {@link #readParcelableList(List, ClassLoader)} if required.
@@ -3380,6 +3476,32 @@
}
/**
+ * Read and return a new ArrayList containing T (IInterface) objects from
+ * the parcel that was written with {@link #writeInterfaceList} at the
+ * current dataPosition(). Returns null if the
+ * previously written list object was null.
+ *
+ * @return A newly created ArrayList containing T (IInterface)
+ *
+ * @see #writeInterfaceList
+ */
+ @SuppressLint({"ConcreteCollection", "NullableCollection"})
+ @Nullable
+ public final <T extends IInterface> ArrayList<T> createInterfaceArrayList(
+ @NonNull Function<IBinder, T> asInterface) {
+ int N = readInt();
+ if (N < 0) {
+ return null;
+ }
+ ArrayList<T> l = new ArrayList<T>(N);
+ while (N > 0) {
+ l.add(asInterface.apply(readStrongBinder()));
+ N--;
+ }
+ return l;
+ }
+
+ /**
* Read into the given List items String objects that were written with
* {@link #writeStringList} at the current dataPosition().
*
@@ -3422,6 +3544,28 @@
}
/**
+ * Read into the given List items IInterface objects that were written with
+ * {@link #writeInterfaceList} at the current dataPosition().
+ *
+ * @see #writeInterfaceList
+ */
+ public final <T extends IInterface> void readInterfaceList(@NonNull List<T> list,
+ @NonNull Function<IBinder, T> asInterface) {
+ int M = list.size();
+ int N = readInt();
+ int i = 0;
+ for (; i < M && i < N; i++) {
+ list.set(i, asInterface.apply(readStrongBinder()));
+ }
+ for (; i<N; i++) {
+ list.add(asInterface.apply(readStrongBinder()));
+ }
+ for (; i<M; i++) {
+ list.remove(N);
+ }
+ }
+
+ /**
* Read the list of {@code Parcelable} objects at the current data position into the
* given {@code list}. The contents of the {@code list} are replaced. If the serialized
* list was {@code null}, {@code list} is cleared.
diff --git a/core/java/android/os/PowerComponents.java b/core/java/android/os/PowerComponents.java
index 4b12aa6..e863111 100644
--- a/core/java/android/os/PowerComponents.java
+++ b/core/java/android/os/PowerComponents.java
@@ -223,22 +223,32 @@
for (int componentId = 0; componentId < BatteryConsumer.POWER_COMPONENT_COUNT;
componentId++) {
- final BatteryConsumer.Key key = mData.getKey(componentId, PROCESS_STATE_ANY);
- final long powerDeciCoulombs = convertMahToDeciCoulombs(getConsumedPower(key));
- final long durationMs = getUsageDurationMillis(key);
+ final BatteryConsumer.Key[] keys = mData.getKeys(componentId);
+ for (BatteryConsumer.Key key : keys) {
+ final long powerDeciCoulombs = convertMahToDeciCoulombs(getConsumedPower(key));
+ final long durationMs = getUsageDurationMillis(key);
- if (powerDeciCoulombs == 0 && durationMs == 0) {
- // No interesting data. Make sure not to even write the COMPONENT int.
- continue;
+ if (powerDeciCoulombs == 0 && durationMs == 0) {
+ // No interesting data. Make sure not to even write the COMPONENT int.
+ continue;
+ }
+
+ interestingData = true;
+ if (proto == null) {
+ // We're just asked whether there is data, not to actually write it.
+ // And there is.
+ return true;
+ }
+
+ if (key.processState == PROCESS_STATE_ANY) {
+ writePowerComponentUsage(proto,
+ BatteryUsageStatsAtomsProto.BatteryConsumerData.POWER_COMPONENTS,
+ componentId, powerDeciCoulombs, durationMs);
+ } else {
+ writePowerUsageSlice(proto, componentId, powerDeciCoulombs, durationMs,
+ key.processState);
+ }
}
-
- interestingData = true;
- if (proto == null) {
- // We're just asked whether there is data, not to actually write it. And there is.
- return true;
- }
-
- writePowerComponent(proto, componentId, powerDeciCoulombs, durationMs);
}
for (int idx = 0; idx < mData.layout.customPowerComponentCount; idx++) {
final int componentId = BatteryConsumer.FIRST_CUSTOM_POWER_COMPONENT_ID + idx;
@@ -257,15 +267,49 @@
return true;
}
- writePowerComponent(proto, componentId, powerDeciCoulombs, durationMs);
+ writePowerComponentUsage(proto,
+ BatteryUsageStatsAtomsProto.BatteryConsumerData.POWER_COMPONENTS,
+ componentId, powerDeciCoulombs, durationMs);
}
return interestingData;
}
- private void writePowerComponent(ProtoOutputStream proto, int componentId,
+ private void writePowerUsageSlice(ProtoOutputStream proto, int componentId,
+ long powerDeciCoulombs, long durationMs, int processState) {
+ final long slicesToken =
+ proto.start(BatteryUsageStatsAtomsProto.BatteryConsumerData.SLICES);
+ writePowerComponentUsage(proto,
+ BatteryUsageStatsAtomsProto.BatteryConsumerData.PowerComponentUsageSlice
+ .POWER_COMPONENT,
+ componentId, powerDeciCoulombs, durationMs);
+
+ final int procState;
+ switch (processState) {
+ case BatteryConsumer.PROCESS_STATE_FOREGROUND:
+ procState = BatteryUsageStatsAtomsProto.BatteryConsumerData.PowerComponentUsageSlice
+ .FOREGROUND;
+ break;
+ case BatteryConsumer.PROCESS_STATE_BACKGROUND:
+ procState = BatteryUsageStatsAtomsProto.BatteryConsumerData.PowerComponentUsageSlice
+ .BACKGROUND;
+ break;
+ case BatteryConsumer.PROCESS_STATE_FOREGROUND_SERVICE:
+ procState = BatteryUsageStatsAtomsProto.BatteryConsumerData.PowerComponentUsageSlice
+ .FOREGROUND_SERVICE;
+ break;
+ default:
+ throw new IllegalArgumentException("Unknown process state: " + processState);
+ }
+
+ proto.write(BatteryUsageStatsAtomsProto.BatteryConsumerData.PowerComponentUsageSlice
+ .PROCESS_STATE, procState);
+
+ proto.end(slicesToken);
+ }
+
+ private void writePowerComponentUsage(ProtoOutputStream proto, long tag, int componentId,
long powerDeciCoulombs, long durationMs) {
- final long token =
- proto.start(BatteryUsageStatsAtomsProto.BatteryConsumerData.POWER_COMPONENTS);
+ final long token = proto.start(tag);
proto.write(
BatteryUsageStatsAtomsProto.BatteryConsumerData.PowerComponentUsage
.COMPONENT,
diff --git a/core/java/android/provider/DeviceConfig.java b/core/java/android/provider/DeviceConfig.java
index 54b87ab7..dd31e02 100644
--- a/core/java/android/provider/DeviceConfig.java
+++ b/core/java/android/provider/DeviceConfig.java
@@ -499,6 +499,13 @@
public static final String NAMESPACE_SYSTEM_TIME = "system_time";
/**
+ * Namespace for TARE configurations.
+ *
+ * @hide
+ */
+ public static final String NAMESPACE_TARE = "tare";
+
+ /**
* Telephony related properties.
*
* @hide
@@ -738,7 +745,7 @@
* @param name The name of the property to look up.
* @param defaultValue The value to return if the property does not exist or has no non-null
* value.
- * @return the correspondfing value, or defaultValue if none exists.
+ * @return the corresponding value, or defaultValue if none exists.
* @hide
*/
@SystemApi
diff --git a/core/java/android/provider/Settings.java b/core/java/android/provider/Settings.java
index 0348107..295ed77 100644
--- a/core/java/android/provider/Settings.java
+++ b/core/java/android/provider/Settings.java
@@ -16609,30 +16609,6 @@
public static final String WEAR_PLATFORM_MR_NUMBER = "wear_platform_mr_number";
/**
- * The bluetooth settings storing duplicate address of companion device.
- * @hide
- */
- public static final String COMPANION_BT_ADDRESS_DUAL = "companion_bt_address_dual";
-
- /**
- * The offset of the visible screen from the display bottom (overscan bottom).
- * @hide
- */
- public static final String BOTTOM_OFFSET = "bottom_offset";
-
- /**
- * The shape of the display.
- * @hide
- */
- public static final String DISPLAY_SHAPE = "display_shape";
-
- // Possible display shapes
- /** @hide */
- public static final int DISPLAY_SHAPE_SQUARE = 0;
- /** @hide */
- public static final int DISPLAY_SHAPE_ROUND = 1;
-
- /**
* The different levels of screen brightness the user can select.
* @hide
*/
@@ -16706,12 +16682,6 @@
public static final String AMBIENT_PLUGGED_TIMEOUT_MIN = "ambient_plugged_timeout_min";
/**
- * The companion device's bluetooth address.
- * @hide
- */
- public static final String COMPANION_ADDRESS = "companion_address";
-
- /**
* What OS does paired device has.
* @hide
*/
@@ -16758,12 +16728,6 @@
public static final int HFP_CLIENT_DISABLED = 2;
/**
- * The current HFP client profile setting.
- * @hide
- */
- public static final String HFP_CLIENT_PROFILE_ENABLED = "hfp_client_profile_enabled";
-
- /**
* The companion phone's android version.
* @hide
*/
diff --git a/core/java/android/view/ViewRootImpl.java b/core/java/android/view/ViewRootImpl.java
index be15965..c88d4f8 100644
--- a/core/java/android/view/ViewRootImpl.java
+++ b/core/java/android/view/ViewRootImpl.java
@@ -211,6 +211,7 @@
import java.io.StringWriter;
import java.lang.ref.WeakReference;
import java.util.ArrayList;
+import java.util.function.Consumer;
import java.util.HashSet;
import java.util.LinkedList;
import java.util.List;
@@ -742,6 +743,8 @@
*/
private long mRtLastAttemptedDrawFrameNum = 0;
+ private Consumer<SurfaceControl.Transaction> mBLASTDrawConsumer;
+
private HashSet<ScrollCaptureCallback> mRootScrollCaptureCallbacks;
private long mScrollCaptureRequestTimeout = SCROLL_CAPTURE_REQUEST_TIMEOUT_MILLIS;
@@ -3259,6 +3262,9 @@
}
boolean cancelDraw = mAttachInfo.mTreeObserver.dispatchOnPreDraw() || !isViewVisible;
+ if (mBLASTDrawConsumer != null) {
+ useBlastSync = true;
+ }
if (!cancelDraw) {
if (mPendingTransitions != null && mPendingTransitions.size() > 0) {
@@ -3977,6 +3983,9 @@
Log.d(mTag, "Creating frameCompleteCallback");
}
+ final Consumer<SurfaceControl.Transaction> blastSyncConsumer = mBLASTDrawConsumer;
+ mBLASTDrawConsumer = null;
+
mAttachInfo.mThreadedRenderer.setFrameCompleteCallback(() -> {
long frameNr = mBlastBufferQueue.getLastAcquiredFrameNum();
if (DEBUG_BLAST) {
@@ -4002,6 +4011,9 @@
mHandler.postAtFrontOfQueue(() -> {
if (useBlastSync) {
mSurfaceChangedTransaction.merge(tmpTransaction);
+ if (blastSyncConsumer != null) {
+ blastSyncConsumer.accept(mSurfaceChangedTransaction);
+ }
}
if (reportNextDraw) {
@@ -10457,4 +10469,35 @@
listener.onBufferTransformHintChanged(hint);
}
}
+
+ /**
+ * Redirect the next draw of this ViewRoot (from the UI thread perspective)
+ * to the passed in consumer. This can be used to create P2P synchronization
+ * between ViewRoot's however it comes with many caveats.
+ *
+ * 1. You MUST consume the transaction, by either applying it immediately or
+ * merging it in to another transaction. The threading model doesn't
+ * allow you to hold in the passed transaction.
+ * 2. If you merge it in to another transaction, this ViewRootImpl will be
+ * paused until you finally apply that transaction and it receives
+ * the callback from SF. If you lose track of the transaction you will
+ * ANR the app.
+ * 3. Only one person can consume the transaction at a time, if you already
+ * have a pending consumer for this frame, the function will return false
+ * 4. Someone else may have requested to consume the next frame, in which case
+ * this function will return false and you will not receive a callback.
+ * 5. This function does not trigger drawing so even if it returns true you
+ * may not receive a callback unless there is some other UI thread work
+ * to trigger drawing. If it returns true, and a draw occurs, the callback
+ * will be called (Though again watch out for the null transaction case!)
+ * 6. This function must be called on the UI thread. The consumer will likewise
+ * be called on the UI thread.
+ */
+ public boolean consumeNextDraw(Consumer<SurfaceControl.Transaction> consume) {
+ if (mBLASTDrawConsumer != null) {
+ return false;
+ }
+ mBLASTDrawConsumer = consume;
+ return true;
+ }
}
diff --git a/core/java/android/view/WindowManager.java b/core/java/android/view/WindowManager.java
index d76f789..8287de2 100644
--- a/core/java/android/view/WindowManager.java
+++ b/core/java/android/view/WindowManager.java
@@ -2442,6 +2442,20 @@
public static final int PRIVATE_FLAG_IS_ROUNDED_CORNERS_OVERLAY = 0x00100000;
/**
+ * Flag to indicate that this window will be excluded while computing the magnifiable region
+ * on the un-scaled screen coordinate, which could avoid the cutout on the magnification
+ * border. It should be used for unmagnifiable overlays.
+ *
+ * </p><p>
+ * Note unlike {@link #PRIVATE_FLAG_NOT_MAGNIFIABLE}, this flag doesn't affect the ability
+ * of magnification. If you want to the window to be unmagnifiable and doesn't lead to the
+ * cutout, you need to combine both of them.
+ * </p><p>
+ * @hide
+ */
+ public static final int PRIVATE_FLAG_EXCLUDE_FROM_SCREEN_MAGNIFICATION = 0x00200000;
+
+ /**
* Flag to prevent the window from being magnified by the accessibility magnifier.
*
* TODO(b/190623172): This is a temporary solution and need to find out another way instead.
@@ -2552,6 +2566,7 @@
PRIVATE_FLAG_SUSTAINED_PERFORMANCE_MODE,
SYSTEM_FLAG_HIDE_NON_SYSTEM_OVERLAY_WINDOWS,
PRIVATE_FLAG_IS_ROUNDED_CORNERS_OVERLAY,
+ PRIVATE_FLAG_EXCLUDE_FROM_SCREEN_MAGNIFICATION,
PRIVATE_FLAG_NOT_MAGNIFIABLE,
PRIVATE_FLAG_STATUS_FORCE_SHOW_NAVIGATION,
PRIVATE_FLAG_COLOR_SPACE_AGNOSTIC,
@@ -2633,6 +2648,10 @@
equals = PRIVATE_FLAG_IS_ROUNDED_CORNERS_OVERLAY,
name = "IS_ROUNDED_CORNERS_OVERLAY"),
@ViewDebug.FlagToString(
+ mask = PRIVATE_FLAG_EXCLUDE_FROM_SCREEN_MAGNIFICATION,
+ equals = PRIVATE_FLAG_EXCLUDE_FROM_SCREEN_MAGNIFICATION,
+ name = "EXCLUDE_FROM_SCREEN_MAGNIFICATION"),
+ @ViewDebug.FlagToString(
mask = PRIVATE_FLAG_NOT_MAGNIFIABLE,
equals = PRIVATE_FLAG_NOT_MAGNIFIABLE,
name = "NOT_MAGNIFIABLE"),
diff --git a/core/java/android/view/inputmethod/TextSnapshot.java b/core/java/android/view/inputmethod/TextSnapshot.java
index 33ce282..977e6f4 100644
--- a/core/java/android/view/inputmethod/TextSnapshot.java
+++ b/core/java/android/view/inputmethod/TextSnapshot.java
@@ -129,8 +129,8 @@
* <p>Values may be any combination of the following values:</p>
* <ul>
* <li>{@link android.text.TextUtils#CAP_MODE_CHARACTERS TextUtils.CAP_MODE_CHARACTERS}</li>
- * <li>{@link android.text.TextUtils#CAP_MODE_CHARACTERS TextUtils.CAP_MODE_WORDS}</li>
- * <li>{@link android.text.TextUtils#CAP_MODE_CHARACTERS TextUtils.CAP_MODE_SENTENCES}</li>
+ * <li>{@link android.text.TextUtils#CAP_MODE_WORDS TextUtils.CAP_MODE_WORDS}</li>
+ * <li>{@link android.text.TextUtils#CAP_MODE_SENTENCES TextUtils.CAP_MODE_SENTENCES}</li>
* </ul>
*
* <p>You should generally just take a non-zero value to mean "start out in caps mode" though.
diff --git a/core/java/android/window/TaskFragmentInfo.java b/core/java/android/window/TaskFragmentInfo.java
index 165dcdf..a118f9a 100644
--- a/core/java/android/window/TaskFragmentInfo.java
+++ b/core/java/android/window/TaskFragmentInfo.java
@@ -213,6 +213,7 @@
+ " isEmpty=" + mIsEmpty
+ " runningActivityCount=" + mRunningActivityCount
+ " isVisible=" + mIsVisible
+ + " activities=" + mActivities
+ " positionInParent=" + mPositionInParent
+ " isTaskClearedForReuse=" + mIsTaskClearedForReuse
+ "}";
diff --git a/core/java/com/android/internal/app/chooser/DisplayResolveInfo.java b/core/java/com/android/internal/app/chooser/DisplayResolveInfo.java
index b00148a..4f2e7db 100644
--- a/core/java/com/android/internal/app/chooser/DisplayResolveInfo.java
+++ b/core/java/com/android/internal/app/chooser/DisplayResolveInfo.java
@@ -30,9 +30,11 @@
import android.os.Parcel;
import android.os.Parcelable;
import android.os.UserHandle;
+import android.provider.DeviceConfig;
import com.android.internal.app.ResolverActivity;
import com.android.internal.app.ResolverListAdapter.ResolveInfoPresentationGetter;
+import com.android.internal.config.sysui.SystemUiDeviceConfigFlags;
import java.util.ArrayList;
import java.util.Arrays;
@@ -43,10 +45,10 @@
* resolve it to an activity.
*/
public class DisplayResolveInfo implements TargetInfo, Parcelable {
- // Temporary flag for new chooser delegate behavior. There are occassional token
- // permission errors from bouncing through the delegate. Watch out before reenabling:
- // b/157272342 is one example but this issue has been reported many times
- private static final boolean ENABLE_CHOOSER_DELEGATE = false;
+ private final boolean mEnableChooserDelegate =
+ DeviceConfig.getBoolean(DeviceConfig.NAMESPACE_SYSTEMUI,
+ SystemUiDeviceConfigFlags.USE_DELEGATE_CHOOSER,
+ false);
private final ResolveInfo mResolveInfo;
private CharSequence mDisplayLabel;
@@ -178,7 +180,7 @@
@Override
public boolean startAsCaller(ResolverActivity activity, Bundle options, int userId) {
- if (ENABLE_CHOOSER_DELEGATE) {
+ if (mEnableChooserDelegate) {
return activity.startAsCallerImpl(mResolvedIntent, options, false, userId);
} else {
activity.startActivityAsCaller(mResolvedIntent, options, null, false, userId);
diff --git a/core/java/com/android/internal/config/sysui/SystemUiDeviceConfigFlags.java b/core/java/com/android/internal/config/sysui/SystemUiDeviceConfigFlags.java
index 1b3fd7b..d1019c5 100644
--- a/core/java/com/android/internal/config/sysui/SystemUiDeviceConfigFlags.java
+++ b/core/java/com/android/internal/config/sysui/SystemUiDeviceConfigFlags.java
@@ -501,6 +501,11 @@
public static final String IS_NEARBY_SHARE_FIRST_TARGET_IN_RANKED_APP =
"is_nearby_share_first_target_in_ranked_app";
+ /**
+ * (boolean) Whether to enable the new unbundled "delegate chooser" implementation.
+ */
+ public static final String USE_DELEGATE_CHOOSER = "use_delegate_chooser";
+
private SystemUiDeviceConfigFlags() {
}
}
diff --git a/core/proto/android/os/batteryusagestats.proto b/core/proto/android/os/batteryusagestats.proto
index bcce784..c0a9f03 100644
--- a/core/proto/android/os/batteryusagestats.proto
+++ b/core/proto/android/os/batteryusagestats.proto
@@ -62,6 +62,26 @@
optional int64 duration_millis = 3;
}
repeated PowerComponentUsage power_components = 2;
+
+ // Represents a slice of power attribution, e.g. "cpu while in the background"
+ // or "wifi when running a background service". Queries that care about
+ // PowerComponentUsage slices need to be aware of all supported dimensions.
+ // There are no roll-ups included in the slices - it is up to the clients
+ // of this data to aggregate values as needed.
+ message PowerComponentUsageSlice {
+ optional PowerComponentUsage power_component = 1;
+
+ enum ProcessState {
+ UNSPECIFIED = 0;
+ FOREGROUND = 1;
+ BACKGROUND = 2;
+ FOREGROUND_SERVICE = 3;
+ }
+
+ optional ProcessState process_state = 2;
+ }
+
+ repeated PowerComponentUsageSlice slices = 3;
}
// Total power usage for the device during this session.
diff --git a/core/res/res/interpolator/emphasized.xml b/core/res/res/interpolator/emphasized.xml
new file mode 100644
index 0000000..aae939a
--- /dev/null
+++ b/core/res/res/interpolator/emphasized.xml
@@ -0,0 +1,20 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ ~ 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.
+ -->
+
+<pathInterpolator xmlns:android="http://schemas.android.com/apk/res/android"
+ android:pathData=
+ "M 0,0 C 0.05, 0, 0.133333, 0.06, 0.166666, 0.4 C 0.208333, 0.82, 0.25, 1, 1, 1"/>
diff --git a/core/res/res/interpolator/emphasized_accelerate.xml b/core/res/res/interpolator/emphasized_accelerate.xml
new file mode 100644
index 0000000..f39faf8
--- /dev/null
+++ b/core/res/res/interpolator/emphasized_accelerate.xml
@@ -0,0 +1,22 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ ~ 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.
+ -->
+
+<pathInterpolator xmlns:android="http://schemas.android.com/apk/res/android"
+ android:controlX1="0.3"
+ android:controlY1="0"
+ android:controlX2="0.8"
+ android:controlY2="0.15"/>
diff --git a/core/res/res/interpolator/emphasized_decelerate.xml b/core/res/res/interpolator/emphasized_decelerate.xml
new file mode 100644
index 0000000..3e4fab6
--- /dev/null
+++ b/core/res/res/interpolator/emphasized_decelerate.xml
@@ -0,0 +1,22 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ ~ 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.
+ -->
+
+<pathInterpolator xmlns:android="http://schemas.android.com/apk/res/android"
+ android:controlX1="0.05"
+ android:controlY1="0.7"
+ android:controlX2="0.1"
+ android:controlY2="1"/>
diff --git a/core/res/res/interpolator/standard.xml b/core/res/res/interpolator/standard.xml
new file mode 100644
index 0000000..bf61229
--- /dev/null
+++ b/core/res/res/interpolator/standard.xml
@@ -0,0 +1,22 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ ~ 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.
+ -->
+
+<pathInterpolator xmlns:android="http://schemas.android.com/apk/res/android"
+ android:controlX1="0.2"
+ android:controlY1="0"
+ android:controlX2="0"
+ android:controlY2="1"/>
diff --git a/core/res/res/interpolator/standard_accelerate.xml b/core/res/res/interpolator/standard_accelerate.xml
new file mode 100644
index 0000000..68e2fa1
--- /dev/null
+++ b/core/res/res/interpolator/standard_accelerate.xml
@@ -0,0 +1,22 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ ~ 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.
+ -->
+
+<pathInterpolator xmlns:android="http://schemas.android.com/apk/res/android"
+ android:controlX1="0.3"
+ android:controlY1="0"
+ android:controlX2="1"
+ android:controlY2="1"/>
diff --git a/core/res/res/interpolator/standard_decelerate.xml b/core/res/res/interpolator/standard_decelerate.xml
new file mode 100644
index 0000000..0169871
--- /dev/null
+++ b/core/res/res/interpolator/standard_decelerate.xml
@@ -0,0 +1,22 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ ~ 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.
+ -->
+
+<pathInterpolator xmlns:android="http://schemas.android.com/apk/res/android"
+ android:controlX1="0"
+ android:controlY1="0"
+ android:controlX2="0"
+ android:controlY2="1"/>
diff --git a/core/res/res/values/config.xml b/core/res/res/values/config.xml
index fceb951..7f68bfd 100644
--- a/core/res/res/values/config.xml
+++ b/core/res/res/values/config.xml
@@ -2723,7 +2723,7 @@
<!-- Name of the activity that will handle requests to the system to choose an activity for
the purposes of resolving an intent. -->
<string name="config_chooserActivity" translatable="false"
- >com.android.systemui/com.android.systemui.chooser.ChooserActivity</string>
+ >com.android.intentresolver/.ChooserActivity</string>
<!-- Component name of a custom ResolverActivity (Intent resolver) to be used instead of
the default framework version. If left empty, then the framework version will be used.
Example: com.google.android.myapp/.resolver.MyResolverActivity -->
diff --git a/core/tests/batterystatstests/BatteryUsageStatsProtoTests/src/com/android/internal/os/BatteryUsageStatsPulledTest.java b/core/tests/batterystatstests/BatteryUsageStatsProtoTests/src/com/android/internal/os/BatteryUsageStatsPulledTest.java
index 7ff76df..e230a54 100644
--- a/core/tests/batterystatstests/BatteryUsageStatsProtoTests/src/com/android/internal/os/BatteryUsageStatsPulledTest.java
+++ b/core/tests/batterystatstests/BatteryUsageStatsProtoTests/src/com/android/internal/os/BatteryUsageStatsPulledTest.java
@@ -28,6 +28,7 @@
import android.os.BatteryConsumer;
import android.os.BatteryUsageStats;
+import android.os.UidBatteryConsumer;
import android.os.nano.BatteryUsageStatsAtomsProto;
import android.os.nano.BatteryUsageStatsAtomsProto.BatteryConsumerData.PowerComponentUsage;
@@ -134,6 +135,43 @@
componentProto.durationMillis);
}
}
+
+ for (int componentId = 0; componentId < BatteryConsumer.POWER_COMPONENT_COUNT;
+ componentId++) {
+ final BatteryConsumer.Key[] keys = consumer.getKeys(componentId);
+ if (keys == null || keys.length <= 1) {
+ continue;
+ }
+
+ for (BatteryConsumer.Key key : keys) {
+ if (key.processState == 0) {
+ continue;
+ }
+
+ BatteryUsageStatsAtomsProto.BatteryConsumerData.PowerComponentUsageSlice
+ sliceProto = null;
+ for (BatteryUsageStatsAtomsProto.BatteryConsumerData.PowerComponentUsageSlice
+ slice : consumerProto.slices) {
+ if (slice.powerComponent.component == componentId
+ && slice.processState == key.processState) {
+ sliceProto = slice;
+ break;
+ }
+ }
+
+ final long expectedPowerDc = convertMahToDc(consumer.getConsumedPower(key));
+ final long expectedUsageDurationMillis = consumer.getUsageDurationMillis(key);
+ if (expectedPowerDc == 0 && expectedUsageDurationMillis == 0) {
+ assertThat(sliceProto).isNull();
+ } else {
+ assertThat(sliceProto).isNotNull();
+ assertThat(sliceProto.powerComponent.powerDeciCoulombs)
+ .isEqualTo(expectedPowerDc);
+ assertThat(sliceProto.powerComponent.durationMillis)
+ .isEqualTo(expectedUsageDurationMillis);
+ }
+ }
+ }
}
private void assertSameUidBatteryConsumer(
@@ -172,14 +210,17 @@
final BatteryStatsImpl.Uid batteryStatsUid3 = batteryStats.getUidStatsLocked(UID_3);
final BatteryUsageStats.Builder builder =
- new BatteryUsageStats.Builder(new String[]{"CustomConsumer1", "CustomConsumer2"})
+ new BatteryUsageStats.Builder(new String[]{"CustomConsumer1", "CustomConsumer2"},
+ /* includePowerModels */ true,
+ /* includeProcessStats */true)
.setDischargePercentage(20)
.setDischargedPowerRange(1000, 2000)
.setStatsStartTimestamp(1000);
- builder.getOrCreateUidBatteryConsumerBuilder(batteryStatsUid0)
+ final UidBatteryConsumer.Builder uidBuilder = builder.getOrCreateUidBatteryConsumerBuilder(
+ batteryStatsUid0)
.setPackageWithHighestDrain("myPackage0")
- .setTimeInStateMs(android.os.UidBatteryConsumer.STATE_FOREGROUND, 1000)
- .setTimeInStateMs(android.os.UidBatteryConsumer.STATE_BACKGROUND, 2000)
+ .setTimeInStateMs(UidBatteryConsumer.STATE_FOREGROUND, 1000)
+ .setTimeInStateMs(UidBatteryConsumer.STATE_BACKGROUND, 2000)
.setConsumedPower(
BatteryConsumer.POWER_COMPONENT_SCREEN, 300)
.setConsumedPower(
@@ -193,6 +234,20 @@
.setUsageDurationForCustomComponentMillis(
BatteryConsumer.FIRST_CUSTOM_POWER_COMPONENT_ID + 1, 800);
+ final BatteryConsumer.Key keyFg = uidBuilder.getKey(BatteryConsumer.POWER_COMPONENT_CPU,
+ BatteryConsumer.PROCESS_STATE_FOREGROUND);
+ final BatteryConsumer.Key keyBg = uidBuilder.getKey(BatteryConsumer.POWER_COMPONENT_CPU,
+ BatteryConsumer.PROCESS_STATE_BACKGROUND);
+ final BatteryConsumer.Key keyFgs = uidBuilder.getKey(BatteryConsumer.POWER_COMPONENT_CPU,
+ BatteryConsumer.PROCESS_STATE_FOREGROUND_SERVICE);
+
+ uidBuilder.setConsumedPower(keyFg, 9100, BatteryConsumer.POWER_MODEL_POWER_PROFILE)
+ .setUsageDurationMillis(keyFg, 8100)
+ .setConsumedPower(keyBg, 9200, BatteryConsumer.POWER_MODEL_MEASURED_ENERGY)
+ .setUsageDurationMillis(keyBg, 8200)
+ .setConsumedPower(keyFgs, 9300, BatteryConsumer.POWER_MODEL_MEASURED_ENERGY)
+ .setUsageDurationMillis(keyFgs, 8300);
+
builder.getOrCreateUidBatteryConsumerBuilder(batteryStatsUid1)
.setPackageWithHighestDrain("myPackage1")
.setTimeInStateMs(android.os.UidBatteryConsumer.STATE_FOREGROUND, 1234);
diff --git a/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/SplitContainer.java b/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/SplitContainer.java
index 06e7d14..1e9fda6 100644
--- a/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/SplitContainer.java
+++ b/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/SplitContainer.java
@@ -83,4 +83,13 @@
&& ((SplitPairRule) splitRule).shouldFinishSecondaryWithPrimary();
return shouldFinishSecondaryWithPrimary || isPlaceholderContainer;
}
+
+ @Override
+ public String toString() {
+ return "SplitContainer{"
+ + " primaryContainer=" + mPrimaryContainer
+ + " secondaryContainer=" + mSecondaryContainer
+ + " splitRule=" + mSplitRule
+ + "}";
+ }
}
diff --git a/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/SplitController.java b/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/SplitController.java
index 20515e7..9014102 100644
--- a/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/SplitController.java
+++ b/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/SplitController.java
@@ -497,7 +497,7 @@
return;
}
List<SplitInfo> currentSplitStates = getActiveSplitStates();
- if (mLastReportedSplitStates.equals(currentSplitStates)) {
+ if (currentSplitStates == null || mLastReportedSplitStates.equals(currentSplitStates)) {
return;
}
mLastReportedSplitStates.clear();
@@ -506,15 +506,19 @@
}
/**
- * Returns a list of descriptors for currently active split states.
+ * @return a list of descriptors for currently active split states. If the value returned is
+ * null, that indicates that the active split states are in an intermediate state and should
+ * not be reported.
*/
+ @Nullable
private List<SplitInfo> getActiveSplitStates() {
List<SplitInfo> splitStates = new ArrayList<>();
for (SplitContainer container : mSplitContainers) {
if (container.getPrimaryContainer().isEmpty()
|| container.getSecondaryContainer().isEmpty()) {
- // Skipping containers that do not have any activities to report.
- continue;
+ // We are in an intermediate state because either the split container is about to be
+ // removed or the primary or secondary container are about to receive an activity.
+ return null;
}
ActivityStack primaryContainer = container.getPrimaryContainer().toActivityStack();
ActivityStack secondaryContainer = container.getSecondaryContainer().toActivityStack();
diff --git a/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/TaskFragmentContainer.java b/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/TaskFragmentContainer.java
index 80d9c2c..6805fde 100644
--- a/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/TaskFragmentContainer.java
+++ b/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/TaskFragmentContainer.java
@@ -27,6 +27,7 @@
import android.window.WindowContainerTransaction;
import java.util.ArrayList;
+import java.util.Iterator;
import java.util.List;
/**
@@ -267,4 +268,42 @@
mLastRequestedBounds.set(bounds);
}
}
+
+ @Override
+ public String toString() {
+ return toString(true /* includeContainersToFinishOnExit */);
+ }
+
+ /**
+ * @return string for this TaskFragmentContainer and includes containers to finish on exit
+ * based on {@code includeContainersToFinishOnExit}. If containers to finish on exit are always
+ * included in the string, then calling {@link #toString()} on a container that mutually
+ * finishes with another container would cause a stack overflow.
+ */
+ private String toString(boolean includeContainersToFinishOnExit) {
+ return "TaskFragmentContainer{"
+ + " token=" + mToken
+ + " info=" + mInfo
+ + " topNonFinishingActivity=" + getTopNonFinishingActivity()
+ + " pendingAppearedActivities=" + mPendingAppearedActivities
+ + (includeContainersToFinishOnExit ? " containersToFinishOnExit="
+ + containersToFinishOnExitToString() : "")
+ + " activitiesToFinishOnExit=" + mActivitiesToFinishOnExit
+ + " isFinished=" + mIsFinished
+ + " lastRequestedBounds=" + mLastRequestedBounds
+ + "}";
+ }
+
+ private String containersToFinishOnExitToString() {
+ StringBuilder sb = new StringBuilder("[");
+ Iterator<TaskFragmentContainer> containerIterator = mContainersToFinishOnExit.iterator();
+ while (containerIterator.hasNext()) {
+ sb.append(containerIterator.next().toString(
+ false /* includeContainersToFinishOnExit */));
+ if (containerIterator.hasNext()) {
+ sb.append(", ");
+ }
+ }
+ return sb.append("]").toString();
+ }
}
diff --git a/libs/WindowManager/Shell/res/layout/pip_menu.xml b/libs/WindowManager/Shell/res/layout/pip_menu.xml
index 7dc2f31..9fe0247 100644
--- a/libs/WindowManager/Shell/res/layout/pip_menu.xml
+++ b/libs/WindowManager/Shell/res/layout/pip_menu.xml
@@ -65,28 +65,25 @@
<LinearLayout
android:id="@+id/top_end_container"
android:layout_gravity="top|end"
- android:layout_width="match_parent"
+ android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:orientation="horizontal">
-
<ImageButton
android:id="@+id/settings"
android:layout_width="@dimen/pip_action_size"
android:layout_height="@dimen/pip_action_size"
android:contentDescription="@string/pip_phone_settings"
- android:layout_gravity="top|start"
android:gravity="center"
android:src="@drawable/pip_ic_settings"
android:background="?android:selectableItemBackgroundBorderless" />
<ImageButton
- android:id="@+id/enter_split"
+ android:id="@+id/dismiss"
android:layout_width="@dimen/pip_action_size"
android:layout_height="@dimen/pip_action_size"
- android:layout_gravity="top|start"
+ android:contentDescription="@string/pip_phone_close"
android:gravity="center"
- android:contentDescription="@string/pip_phone_enter_split"
- android:src="@drawable/pip_expand"
+ android:src="@drawable/pip_ic_close_white"
android:background="?android:selectableItemBackgroundBorderless" />
</LinearLayout>
@@ -100,14 +97,4 @@
android:padding="@dimen/pip_resize_handle_padding"
android:src="@drawable/pip_resize_handle"
android:background="?android:selectableItemBackgroundBorderless" />
-
- <ImageButton
- android:id="@+id/dismiss"
- android:layout_width="@dimen/pip_action_size"
- android:layout_height="@dimen/pip_action_size"
- android:contentDescription="@string/pip_phone_close"
- android:layout_gravity="top|end"
- android:gravity="center"
- android:src="@drawable/pip_ic_close_white"
- android:background="?android:selectableItemBackgroundBorderless" />
</FrameLayout>
diff --git a/libs/WindowManager/Shell/res/values/strings.xml b/libs/WindowManager/Shell/res/values/strings.xml
index c88fc16..764854a 100644
--- a/libs/WindowManager/Shell/res/values/strings.xml
+++ b/libs/WindowManager/Shell/res/values/strings.xml
@@ -24,9 +24,6 @@
<!-- Label for PIP settings button [CHAR LIMIT=NONE]-->
<string name="pip_phone_settings">Settings</string>
- <!-- Label for the PIP enter split button [CHAR LIMIT=NONE] -->
- <string name="pip_phone_enter_split">Enter split screen</string>
-
<!-- Title of menu shown over picture-in-picture. Used for accessibility. -->
<string name="pip_menu_title">Menu</string>
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/dagger/TvPipModule.java b/libs/WindowManager/Shell/src/com/android/wm/shell/dagger/TvPipModule.java
index 711a0ac..b80dcd0 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/dagger/TvPipModule.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/dagger/TvPipModule.java
@@ -43,7 +43,6 @@
import com.android.wm.shell.pip.tv.TvPipMenuController;
import com.android.wm.shell.pip.tv.TvPipNotificationController;
import com.android.wm.shell.pip.tv.TvPipTransition;
-import com.android.wm.shell.splitscreen.SplitScreenController;
import com.android.wm.shell.transition.Transitions;
import java.util.Optional;
@@ -161,14 +160,13 @@
PipTransitionController pipTransitionController,
PipSurfaceTransactionHelper pipSurfaceTransactionHelper,
Optional<LegacySplitScreenController> splitScreenOptional,
- Optional<SplitScreenController> newSplitScreenOptional,
DisplayController displayController,
PipUiEventLogger pipUiEventLogger, ShellTaskOrganizer shellTaskOrganizer,
@ShellMainThread ShellExecutor mainExecutor) {
return new PipTaskOrganizer(context,
syncTransactionQueue, pipTransitionState, pipBoundsState, pipBoundsAlgorithm,
tvPipMenuController, pipAnimationController, pipSurfaceTransactionHelper,
- pipTransitionController, splitScreenOptional, newSplitScreenOptional,
- displayController, pipUiEventLogger, shellTaskOrganizer, mainExecutor);
+ pipTransitionController, splitScreenOptional, displayController, pipUiEventLogger,
+ shellTaskOrganizer, mainExecutor);
}
}
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 ec70147..944dfed 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
@@ -55,7 +55,6 @@
import com.android.wm.shell.pip.phone.PipController;
import com.android.wm.shell.pip.phone.PipMotionHelper;
import com.android.wm.shell.pip.phone.PipTouchHandler;
-import com.android.wm.shell.splitscreen.SplitScreenController;
import com.android.wm.shell.startingsurface.StartingWindowTypeAlgorithm;
import com.android.wm.shell.startingsurface.phone.PhoneStartingWindowTypeAlgorithm;
import com.android.wm.shell.transition.Transitions;
@@ -216,15 +215,14 @@
PipSurfaceTransactionHelper pipSurfaceTransactionHelper,
PipTransitionController pipTransitionController,
Optional<LegacySplitScreenController> splitScreenOptional,
- Optional<SplitScreenController> newSplitScreenOptional,
DisplayController displayController,
PipUiEventLogger pipUiEventLogger, ShellTaskOrganizer shellTaskOrganizer,
@ShellMainThread ShellExecutor mainExecutor) {
return new PipTaskOrganizer(context,
syncTransactionQueue, pipTransitionState, pipBoundsState, pipBoundsAlgorithm,
menuPhoneController, pipAnimationController, pipSurfaceTransactionHelper,
- pipTransitionController, splitScreenOptional, newSplitScreenOptional,
- displayController, pipUiEventLogger, shellTaskOrganizer, mainExecutor);
+ pipTransitionController, splitScreenOptional, displayController, pipUiEventLogger,
+ shellTaskOrganizer, mainExecutor);
}
@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 6cc5f09..b6e5804 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
@@ -77,7 +77,6 @@
import com.android.wm.shell.common.annotations.ShellMainThread;
import com.android.wm.shell.legacysplitscreen.LegacySplitScreenController;
import com.android.wm.shell.pip.phone.PipMotionHelper;
-import com.android.wm.shell.splitscreen.SplitScreenController;
import com.android.wm.shell.transition.Transitions;
import java.io.PrintWriter;
@@ -127,8 +126,7 @@
private final int mExitAnimationDuration;
private final int mCrossFadeAnimationDuration;
private final PipSurfaceTransactionHelper mSurfaceTransactionHelper;
- private final Optional<LegacySplitScreenController> mLegacySplitScreenOptional;
- private final Optional<SplitScreenController> mSplitScreenOptional;
+ private final Optional<LegacySplitScreenController> mSplitScreenOptional;
protected final ShellTaskOrganizer mTaskOrganizer;
protected final ShellExecutor mMainExecutor;
@@ -254,8 +252,7 @@
@NonNull PipAnimationController pipAnimationController,
@NonNull PipSurfaceTransactionHelper surfaceTransactionHelper,
@NonNull PipTransitionController pipTransitionController,
- Optional<LegacySplitScreenController> legacySplitScreenOptional,
- Optional<SplitScreenController> splitScreenOptional,
+ Optional<LegacySplitScreenController> splitScreenOptional,
@NonNull DisplayController displayController,
@NonNull PipUiEventLogger pipUiEventLogger,
@NonNull ShellTaskOrganizer shellTaskOrganizer,
@@ -277,7 +274,6 @@
mPipAnimationController = pipAnimationController;
mPipUiEventLoggerLogger = pipUiEventLogger;
mSurfaceControlTransactionFactory = SurfaceControl.Transaction::new;
- mLegacySplitScreenOptional = legacySplitScreenOptional;
mSplitScreenOptional = splitScreenOptional;
mTaskOrganizer = shellTaskOrganizer;
mMainExecutor = mainExecutor;
@@ -377,11 +373,8 @@
* activity render it's final configuration while the Task is still in PiP.
* - setWindowingMode to undefined at the end of transition
* @param animationDurationMs duration in millisecond for the exiting PiP transition
- * @param requestEnterSplit whether the enterSplit button is pressed on PiP or not.
- * Indicate the user wishes to directly put PiP into split screen
- * mode.
*/
- public void exitPip(int animationDurationMs, boolean requestEnterSplit) {
+ public void exitPip(int animationDurationMs) {
if (!mPipTransitionState.isInPip()
|| mPipTransitionState.getTransitionState() == PipTransitionState.EXITING_PIP
|| mToken == null) {
@@ -394,7 +387,7 @@
PipUiEventLogger.PipUiEventEnum.PICTURE_IN_PICTURE_EXPAND_TO_FULLSCREEN);
final WindowContainerTransaction wct = new WindowContainerTransaction();
final Rect destinationBounds = mPipBoundsState.getDisplayBounds();
- final int direction = syncWithSplitScreenBounds(destinationBounds, requestEnterSplit)
+ final int direction = syncWithSplitScreenBounds(destinationBounds)
? TRANSITION_DIRECTION_LEAVE_PIP_TO_SPLIT_SCREEN
: TRANSITION_DIRECTION_LEAVE_PIP;
final SurfaceControl.Transaction tx = mSurfaceControlTransactionFactory.getTransaction();
@@ -403,7 +396,7 @@
// We set to fullscreen here for now, but later it will be set to UNDEFINED for
// the proper windowing mode to take place. See #applyWindowingModeChangeOnExit.
wct.setActivityWindowingMode(mToken,
- direction == TRANSITION_DIRECTION_LEAVE_PIP_TO_SPLIT_SCREEN && !requestEnterSplit
+ direction == TRANSITION_DIRECTION_LEAVE_PIP_TO_SPLIT_SCREEN
? WINDOWING_MODE_SPLIT_SCREEN_SECONDARY
: WINDOWING_MODE_FULLSCREEN);
wct.setBounds(mToken, destinationBounds);
@@ -442,7 +435,7 @@
wct.setWindowingMode(mToken, getOutPipWindowingMode());
// Simply reset the activity mode set prior to the animation running.
wct.setActivityWindowingMode(mToken, WINDOWING_MODE_UNDEFINED);
- mLegacySplitScreenOptional.ifPresent(splitScreen -> {
+ mSplitScreenOptional.ifPresent(splitScreen -> {
if (direction == TRANSITION_DIRECTION_LEAVE_PIP_TO_SPLIT_SCREEN) {
wct.reparent(mToken, splitScreen.getSecondaryRoot(), true /* onTop */);
}
@@ -1172,7 +1165,6 @@
@PipAnimationController.TransitionDirection int direction,
@PipAnimationController.AnimationType int type) {
final Rect preResizeBounds = new Rect(mPipBoundsState.getBounds());
- final boolean isPipTopLeft = isPipTopLeft();
mPipBoundsState.setBounds(destinationBounds);
if (direction == TRANSITION_DIRECTION_REMOVE_STACK) {
removePipImmediately();
@@ -1218,10 +1210,10 @@
null /* callback */, false /* withStartDelay */);
});
} else {
- applyFinishBoundsResize(wct, direction, isPipTopLeft);
+ applyFinishBoundsResize(wct, direction);
}
} else {
- applyFinishBoundsResize(wct, direction, isPipTopLeft);
+ applyFinishBoundsResize(wct, direction);
}
finishResizeForMenu(destinationBounds);
@@ -1249,11 +1241,7 @@
} else if (isOutPipDirection(direction)) {
// If we are animating to fullscreen or split screen, then we need to reset the
// override bounds on the task to ensure that the task "matches" the parent's bounds.
- if (direction == TRANSITION_DIRECTION_LEAVE_PIP_TO_SPLIT_SCREEN) {
- taskBounds = destinationBounds;
- } else {
- taskBounds = null;
- }
+ taskBounds = null;
applyWindowingModeChangeOnExit(wct, direction);
} else {
// Just a resize in PIP
@@ -1273,20 +1261,8 @@
* applying it.
*/
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);
- } else {
- mTaskOrganizer.applyTransaction(wct);
- }
- }
-
- private boolean isPipTopLeft() {
- final Rect topLeft = new Rect();
- final Rect bottomRight = new Rect();
- mSplitScreenOptional.get().getStageBounds(topLeft, bottomRight);
-
- return topLeft.contains(mPipBoundsState.getBounds());
+ @PipAnimationController.TransitionDirection int direction) {
+ mTaskOrganizer.applyTransaction(wct);
}
/**
@@ -1371,27 +1347,18 @@
}
/**
- * Sync with {@link LegacySplitScreenController} or {@link SplitScreenController} on destination
- * bounds if PiP is going to split screen.
+ * Sync with {@link LegacySplitScreenController} on destination bounds if PiP is going to split
+ * screen.
*
* @param destinationBoundsOut contain the updated destination bounds if applicable
* @return {@code true} if destinationBounds is altered for split screen
*/
- private boolean syncWithSplitScreenBounds(Rect destinationBoundsOut, boolean enterSplit) {
- if (enterSplit && mSplitScreenOptional.isPresent()) {
- final Rect topLeft = new Rect();
- final Rect bottomRight = new Rect();
- mSplitScreenOptional.get().getStageBounds(topLeft, bottomRight);
- final boolean isPipTopLeft = isPipTopLeft();
- destinationBoundsOut.set(isPipTopLeft ? topLeft : bottomRight);
- return true;
- }
-
- if (!mLegacySplitScreenOptional.isPresent()) {
+ private boolean syncWithSplitScreenBounds(Rect destinationBoundsOut) {
+ if (!mSplitScreenOptional.isPresent()) {
return false;
}
- LegacySplitScreenController legacySplitScreen = mLegacySplitScreenOptional.get();
+ LegacySplitScreenController legacySplitScreen = mSplitScreenOptional.get();
if (!legacySplitScreen.isDividerVisible()) {
// fail early if system is not in split screen mode
return false;
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PhonePipMenuController.java b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PhonePipMenuController.java
index 5687f4d..ae8c1b6 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PhonePipMenuController.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PhonePipMenuController.java
@@ -95,11 +95,6 @@
* Called when the PIP requested to show the menu.
*/
void onPipShowMenu();
-
- /**
- * Called when the PIP requested to enter Split.
- */
- void onEnterSplit();
}
private final Matrix mMoveTransform = new Matrix();
@@ -463,10 +458,6 @@
mListeners.forEach(Listener::onPipDismiss);
}
- void onEnterSplit() {
- mListeners.forEach(Listener::onEnterSplit);
- }
-
/**
* @return the best set of actions to show in the PiP menu.
*/
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipAccessibilityInteractionConnection.java b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipAccessibilityInteractionConnection.java
index 69ae45d..47a8c67 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipAccessibilityInteractionConnection.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipAccessibilityInteractionConnection.java
@@ -151,7 +151,7 @@
result = true;
break;
case AccessibilityNodeInfo.ACTION_EXPAND:
- mMotionHelper.expandLeavePip(false /* skipAnimation */);
+ mMotionHelper.expandLeavePip();
result = true;
break;
default:
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipController.java b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipController.java
index aeeb73f..a32eb16 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipController.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipController.java
@@ -482,8 +482,7 @@
false /* fromShelfAdjustment */,
wct /* windowContainerTransaction */);
if (wct != null) {
- mPipTaskOrganizer.applyFinishBoundsResize(wct, TRANSITION_DIRECTION_SAME,
- false /* wasPipTopLeft */);
+ mPipTaskOrganizer.applyFinishBoundsResize(wct, TRANSITION_DIRECTION_SAME);
}
};
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipMenuIconsAlgorithm.java b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipMenuIconsAlgorithm.java
index 0644657..3eeba6e 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipMenuIconsAlgorithm.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipMenuIconsAlgorithm.java
@@ -18,6 +18,8 @@
import android.content.Context;
import android.graphics.Rect;
+import android.util.Log;
+import android.view.Gravity;
import android.view.View;
import android.view.ViewGroup;
import android.widget.FrameLayout;
@@ -32,7 +34,6 @@
protected ViewGroup mViewRoot;
protected ViewGroup mTopEndContainer;
protected View mDragHandle;
- protected View mEnterSplitButton;
protected View mSettingsButton;
protected View mDismissButton;
@@ -43,13 +44,14 @@
* Bind the necessary views.
*/
public void bindViews(ViewGroup viewRoot, ViewGroup topEndContainer, View dragHandle,
- View enterSplitButton, View settingsButton, View dismissButton) {
+ View settingsButton, View dismissButton) {
mViewRoot = viewRoot;
mTopEndContainer = topEndContainer;
mDragHandle = dragHandle;
- mEnterSplitButton = enterSplitButton;
mSettingsButton = settingsButton;
mDismissButton = dismissButton;
+
+ bindInitialViewState();
}
/**
@@ -70,4 +72,22 @@
v.setLayoutParams(params);
}
}
+
+ /** Calculate the initial state of the menu icons. Called when the menu is first created. */
+ private void bindInitialViewState() {
+ if (mViewRoot == null || mTopEndContainer == null || mDragHandle == null
+ || mSettingsButton == null || mDismissButton == null) {
+ Log.e(TAG, "One of the required views is null.");
+ return;
+ }
+ // The menu view layout starts out with the settings button aligned at the top|end of the
+ // view group next to the dismiss button. On phones, the settings button should be aligned
+ // to the top|start of the view, so move it to parent view group to then align it to the
+ // top|start of the menu.
+ mTopEndContainer.removeView(mSettingsButton);
+ mViewRoot.addView(mSettingsButton);
+
+ setLayoutGravity(mDragHandle, Gravity.START | Gravity.TOP);
+ setLayoutGravity(mSettingsButton, Gravity.START | Gravity.TOP);
+ }
}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipMenuView.java b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipMenuView.java
index 7bbebe5..8ef2b6b 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipMenuView.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipMenuView.java
@@ -99,7 +99,7 @@
private static final float MENU_BACKGROUND_ALPHA = 0.3f;
private static final float DISABLED_ACTION_ALPHA = 0.54f;
- private static final boolean ENABLE_ENTER_SPLIT = false;
+ private static final boolean ENABLE_RESIZE_HANDLE = false;
private int mMenuState;
private boolean mAllowMenuTimeout = true;
@@ -139,7 +139,7 @@
protected View mViewRoot;
protected View mSettingsButton;
protected View mDismissButton;
- protected View mEnterSplitButton;
+ protected View mResizeHandle;
protected View mTopEndContainer;
protected PipMenuIconsAlgorithm mPipMenuIconsAlgorithm;
@@ -177,23 +177,14 @@
}
});
- mEnterSplitButton = findViewById(R.id.enter_split);
- mEnterSplitButton.setAlpha(0);
- mEnterSplitButton.setOnClickListener(v -> {
- if (mMenuContainer.getAlpha() != 0) {
- enterSplit();
- }
- });
-
- findViewById(R.id.resize_handle).setAlpha(0);
-
+ mResizeHandle = findViewById(R.id.resize_handle);
+ mResizeHandle.setAlpha(0);
mActionsGroup = findViewById(R.id.actions_group);
mBetweenActionPaddingLand = getResources().getDimensionPixelSize(
R.dimen.pip_between_action_padding_land);
mPipMenuIconsAlgorithm = new PipMenuIconsAlgorithm(mContext);
mPipMenuIconsAlgorithm.bindViews((ViewGroup) mViewRoot, (ViewGroup) mTopEndContainer,
- findViewById(R.id.resize_handle), mEnterSplitButton, mSettingsButton,
- mDismissButton);
+ mResizeHandle, mSettingsButton, mDismissButton);
mDismissFadeOutDurationMs = context.getResources()
.getInteger(R.integer.config_pipExitAnimationDuration);
@@ -277,13 +268,14 @@
mSettingsButton.getAlpha(), 1f);
ObjectAnimator dismissAnim = ObjectAnimator.ofFloat(mDismissButton, View.ALPHA,
mDismissButton.getAlpha(), 1f);
- ObjectAnimator enterSplitAnim = ObjectAnimator.ofFloat(mEnterSplitButton, View.ALPHA,
- mEnterSplitButton.getAlpha(), ENABLE_ENTER_SPLIT ? 1f : 0f);
+ ObjectAnimator resizeAnim = ObjectAnimator.ofFloat(mResizeHandle, View.ALPHA,
+ mResizeHandle.getAlpha(),
+ ENABLE_RESIZE_HANDLE && showResizeHandle ? 1f : 0f);
if (menuState == MENU_STATE_FULL) {
mMenuContainerAnimator.playTogether(menuAnim, settingsAnim, dismissAnim,
- enterSplitAnim);
+ resizeAnim);
} else {
- mMenuContainerAnimator.playTogether(enterSplitAnim);
+ mMenuContainerAnimator.playTogether(resizeAnim);
}
mMenuContainerAnimator.setInterpolator(Interpolators.ALPHA_IN);
mMenuContainerAnimator.setDuration(ANIMATION_HIDE_DURATION_MS);
@@ -336,7 +328,7 @@
mMenuContainer.setAlpha(0f);
mSettingsButton.setAlpha(0f);
mDismissButton.setAlpha(0f);
- mEnterSplitButton.setAlpha(0f);
+ mResizeHandle.setAlpha(0f);
}
void pokeMenu() {
@@ -376,10 +368,9 @@
mSettingsButton.getAlpha(), 0f);
ObjectAnimator dismissAnim = ObjectAnimator.ofFloat(mDismissButton, View.ALPHA,
mDismissButton.getAlpha(), 0f);
- ObjectAnimator enterSplitAnim = ObjectAnimator.ofFloat(mEnterSplitButton, View.ALPHA,
- mEnterSplitButton.getAlpha(), 0f);
- mMenuContainerAnimator.playTogether(menuAnim, settingsAnim, dismissAnim,
- enterSplitAnim);
+ ObjectAnimator resizeAnim = ObjectAnimator.ofFloat(mResizeHandle, View.ALPHA,
+ mResizeHandle.getAlpha(), 0f);
+ mMenuContainerAnimator.playTogether(menuAnim, settingsAnim, dismissAnim, resizeAnim);
mMenuContainerAnimator.setInterpolator(Interpolators.ALPHA_OUT);
mMenuContainerAnimator.setDuration(getFadeOutDuration(animationType));
mMenuContainerAnimator.addListener(new AnimatorListenerAdapter() {
@@ -531,14 +522,6 @@
}
}
- private void enterSplit() {
- // Do not notify menu visibility when hiding the menu, the controller will do this when it
- // handles the message
- hideMenu(mController::onEnterSplit, false /* notifyMenuVisibility */, true /* resize */,
- ANIM_TYPE_HIDE);
- }
-
-
private void showSettings() {
final Pair<ComponentName, Integer> topPipActivityInfo =
PipUtils.getTopPipActivity(mContext);
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipMotionHelper.java b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipMotionHelper.java
index c634b7f..dbd09fd 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipMotionHelper.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipMotionHelper.java
@@ -338,29 +338,22 @@
* Resizes the pinned stack back to unknown windowing mode, which could be freeform or
* * fullscreen depending on the display area's windowing mode.
*/
- void expandLeavePip(boolean skipAnimation) {
- expandLeavePip(skipAnimation, false /* enterSplit */);
- }
-
- /**
- * Resizes the pinned task to split-screen mode.
- */
- void expandIntoSplit() {
- expandLeavePip(false, true /* enterSplit */);
+ void expandLeavePip() {
+ expandLeavePip(false /* skipAnimation */);
}
/**
* Resizes the pinned stack back to unknown windowing mode, which could be freeform or
* fullscreen depending on the display area's windowing mode.
*/
- private void expandLeavePip(boolean skipAnimation, boolean enterSplit) {
+ void expandLeavePip(boolean skipAnimation) {
if (DEBUG) {
Log.d(TAG, "exitPip: skipAnimation=" + skipAnimation
+ " callers=\n" + Debug.getCallers(5, " "));
}
cancelPhysicsAnimation();
mMenuController.hideMenu(ANIM_TYPE_NONE, false /* resize */);
- mPipTaskOrganizer.exitPip(skipAnimation ? 0 : LEAVE_PIP_DURATION, enterSplit);
+ mPipTaskOrganizer.exitPip(skipAnimation ? 0 : LEAVE_PIP_DURATION);
}
/**
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipTouchHandler.java b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipTouchHandler.java
index 570fd5e..9f2f6a5 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipTouchHandler.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipTouchHandler.java
@@ -139,12 +139,7 @@
@Override
public void onPipExpand() {
- mMotionHelper.expandLeavePip(false /* skipAnimation */);
- }
-
- @Override
- public void onEnterSplit() {
- mMotionHelper.expandIntoSplit();
+ mMotionHelper.expandLeavePip();
}
@Override
@@ -904,7 +899,7 @@
// Expand to fullscreen if this is a double tap
// the PiP should be frozen until the transition ends
setTouchEnabled(false);
- mMotionHelper.expandLeavePip(false /* skipAnimation */);
+ mMotionHelper.expandLeavePip();
}
} else if (mMenuState != MENU_STATE_FULL) {
if (mPipBoundsState.isStashed()) {
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/tv/TvPipController.java b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/tv/TvPipController.java
index 00083d9..a2e9b64 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/tv/TvPipController.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/tv/TvPipController.java
@@ -219,7 +219,7 @@
public void movePipToFullscreen() {
if (DEBUG) Log.d(TAG, "movePipToFullscreen(), state=" + stateToName(mState));
- mPipTaskOrganizer.exitPip(mResizeAnimationDuration, false /* requestEnterSplit */);
+ mPipTaskOrganizer.exitPip(mResizeAnimationDuration);
onPipDisappeared();
}
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 7457be2..04058ed 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
@@ -202,25 +202,11 @@
return moveToSideStage(task, sideStagePosition);
}
- public boolean moveToSideStage(int taskId, @SplitPosition int sideStagePosition,
- WindowContainerTransaction wct) {
- final ActivityManager.RunningTaskInfo task = mTaskOrganizer.getRunningTaskInfo(taskId);
- if (task == null) {
- throw new IllegalArgumentException("Unknown taskId" + taskId);
- }
- return moveToSideStage(task, sideStagePosition, wct);
- }
-
public boolean moveToSideStage(ActivityManager.RunningTaskInfo task,
@SplitPosition int sideStagePosition) {
return mStageCoordinator.moveToSideStage(task, sideStagePosition);
}
- public boolean moveToSideStage(ActivityManager.RunningTaskInfo task,
- @SplitPosition int sideStagePosition, WindowContainerTransaction wct) {
- return mStageCoordinator.moveToSideStage(task, sideStagePosition, wct);
- }
-
public boolean removeFromSideStage(int taskId) {
return mStageCoordinator.removeFromSideStage(taskId);
}
@@ -238,11 +224,6 @@
leftOrTop ? SPLIT_POSITION_TOP_OR_LEFT : SPLIT_POSITION_BOTTOM_OR_RIGHT);
}
- public void enterSplitScreen(int taskId, boolean leftOrTop, WindowContainerTransaction wct) {
- moveToSideStage(taskId,
- leftOrTop ? SPLIT_POSITION_TOP_OR_LEFT : SPLIT_POSITION_BOTTOM_OR_RIGHT, wct);
- }
-
public void exitSplitScreen(int toTopTaskId, @ExitReason int exitReason) {
mStageCoordinator.exitSplitScreen(toTopTaskId, exitReason);
}
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 3c35e6a..8471e1e 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
@@ -280,11 +280,6 @@
boolean moveToSideStage(ActivityManager.RunningTaskInfo task,
@SplitPosition int sideStagePosition) {
final WindowContainerTransaction wct = new WindowContainerTransaction();
- return moveToSideStage(task, sideStagePosition, wct);
- }
-
- boolean moveToSideStage(ActivityManager.RunningTaskInfo task,
- @SplitPosition int sideStagePosition, WindowContainerTransaction wct) {
final WindowContainerTransaction evictWct = new WindowContainerTransaction();
setSideStagePosition(sideStagePosition, wct);
mSideStage.evictAllChildren(evictWct);
diff --git a/libs/WindowManager/Shell/tests/OWNERS b/libs/WindowManager/Shell/tests/OWNERS
index d80699d..f49e80a 100644
--- a/libs/WindowManager/Shell/tests/OWNERS
+++ b/libs/WindowManager/Shell/tests/OWNERS
@@ -1,3 +1,4 @@
# Bug component: 909476
# includes OWNERS from parent directories
natanieljr@google.com
+pablogamito@google.com
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/apppairs/AppPairsTestCannotPairNonResizeableApps.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/apppairs/AppPairsTestCannotPairNonResizeableApps.kt
index 038be9c..ecc2d31 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/apppairs/AppPairsTestCannotPairNonResizeableApps.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/apppairs/AppPairsTestCannotPairNonResizeableApps.kt
@@ -52,9 +52,9 @@
testSpec: FlickerTestParameter
) : AppPairsTransition(testSpec) {
- override val transition: FlickerBuilder.(Map<String, Any?>) -> Unit
+ override val transition: FlickerBuilder.() -> Unit
get() = {
- super.transition(this, it)
+ super.transition(this)
transitions {
nonResizeableApp?.launchViaIntent(wmHelper)
// TODO pair apps through normal UX flow
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/apppairs/AppPairsTestPairPrimaryAndSecondaryApps.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/apppairs/AppPairsTestPairPrimaryAndSecondaryApps.kt
index bbc6b2d..04c82e5 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/apppairs/AppPairsTestPairPrimaryAndSecondaryApps.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/apppairs/AppPairsTestPairPrimaryAndSecondaryApps.kt
@@ -46,9 +46,9 @@
class AppPairsTestPairPrimaryAndSecondaryApps(
testSpec: FlickerTestParameter
) : AppPairsTransition(testSpec) {
- override val transition: FlickerBuilder.(Map<String, Any?>) -> Unit
+ override val transition: FlickerBuilder.() -> Unit
get() = {
- super.transition(this, it)
+ super.transition(this)
transitions {
// TODO pair apps through normal UX flow
executeShellCommand(
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/apppairs/AppPairsTestSupportPairNonResizeableApps.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/apppairs/AppPairsTestSupportPairNonResizeableApps.kt
index bb784a8..b7d3ba6 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/apppairs/AppPairsTestSupportPairNonResizeableApps.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/apppairs/AppPairsTestSupportPairNonResizeableApps.kt
@@ -52,9 +52,9 @@
testSpec: FlickerTestParameter
) : AppPairsTransition(testSpec) {
- override val transition: FlickerBuilder.(Map<String, Any?>) -> Unit
+ override val transition: FlickerBuilder.() -> Unit
get() = {
- super.transition(this, it)
+ super.transition(this)
transitions {
nonResizeableApp?.launchViaIntent(wmHelper)
// TODO pair apps through normal UX flow
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/apppairs/AppPairsTestUnpairPrimaryAndSecondaryApps.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/apppairs/AppPairsTestUnpairPrimaryAndSecondaryApps.kt
index a1a4db1..f6ce3d4 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/apppairs/AppPairsTestUnpairPrimaryAndSecondaryApps.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/apppairs/AppPairsTestUnpairPrimaryAndSecondaryApps.kt
@@ -47,9 +47,9 @@
class AppPairsTestUnpairPrimaryAndSecondaryApps(
testSpec: FlickerTestParameter
) : AppPairsTransition(testSpec) {
- override val transition: FlickerBuilder.(Map<String, Any?>) -> Unit
+ override val transition: FlickerBuilder.() -> Unit
get() = {
- super.transition(this, it)
+ super.transition(this)
setup {
eachRun {
executeShellCommand(
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/apppairs/AppPairsTransition.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/apppairs/AppPairsTransition.kt
index 9e20bbb..863c3af 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/apppairs/AppPairsTransition.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/apppairs/AppPairsTransition.kt
@@ -25,14 +25,11 @@
import com.android.server.wm.flicker.FlickerBuilderProvider
import com.android.server.wm.flicker.FlickerTestParameter
import com.android.server.wm.flicker.dsl.FlickerBuilder
-import com.android.server.wm.flicker.helpers.isRotated
import com.android.server.wm.flicker.helpers.setRotation
import com.android.server.wm.flicker.helpers.wakeUpAndGoToHomeScreen
import com.android.server.wm.flicker.navBarLayerIsVisible
import com.android.server.wm.flicker.navBarLayerRotatesAndScales
import com.android.server.wm.flicker.navBarWindowIsVisible
-import com.android.server.wm.flicker.repetitions
-import com.android.server.wm.flicker.startRotation
import com.android.server.wm.flicker.statusBarLayerIsVisible
import com.android.server.wm.flicker.statusBarLayerRotatesScales
import com.android.server.wm.flicker.statusBarWindowIsVisible
@@ -50,7 +47,6 @@
abstract class AppPairsTransition(protected val testSpec: FlickerTestParameter) {
protected val instrumentation: Instrumentation = InstrumentationRegistry.getInstrumentation()
protected val context: Context = instrumentation.context
- protected val isRotated = testSpec.config.startRotation.isRotated()
protected val activityHelper = ActivityHelper.getInstance()
protected val appPairsHelper = AppPairsHelper(instrumentation,
Components.SplitScreenActivity.LABEL,
@@ -82,20 +78,18 @@
@FlickerBuilderProvider
fun buildFlicker(): FlickerBuilder {
return FlickerBuilder(instrumentation).apply {
- withTestName { testSpec.name }
- repeat { testSpec.config.repetitions }
- transition(this, testSpec.config)
+ transition(this)
}
}
- internal open val transition: FlickerBuilder.(Map<String, Any?>) -> Unit
- get() = { configuration ->
+ internal open val transition: FlickerBuilder.() -> Unit
+ get() = {
setup {
test {
device.wakeUpAndGoToHomeScreen()
}
eachRun {
- this.setRotation(configuration.startRotation)
+ this.setRotation(testSpec.startRotation)
primaryApp.launchViaIntent(wmHelper)
secondaryApp.launchViaIntent(wmHelper)
nonResizeableApp?.launchViaIntent(wmHelper)
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/apppairs/RotateTwoLaunchedAppsInAppPairsMode.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/apppairs/RotateTwoLaunchedAppsInAppPairsMode.kt
index 56a2531..13824b8 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/apppairs/RotateTwoLaunchedAppsInAppPairsMode.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/apppairs/RotateTwoLaunchedAppsInAppPairsMode.kt
@@ -25,7 +25,6 @@
import com.android.server.wm.flicker.FlickerTestParameterFactory
import com.android.server.wm.flicker.annotation.Group1
import com.android.server.wm.flicker.dsl.FlickerBuilder
-import com.android.server.wm.flicker.endRotation
import com.android.server.wm.flicker.helpers.setRotation
import com.android.wm.shell.flicker.appPairsDividerIsVisibleAtEnd
import com.android.wm.shell.flicker.appPairsPrimaryBoundsIsVisibleAtEnd
@@ -50,14 +49,14 @@
class RotateTwoLaunchedAppsInAppPairsMode(
testSpec: FlickerTestParameter
) : RotateTwoLaunchedAppsTransition(testSpec) {
- override val transition: FlickerBuilder.(Map<String, Any?>) -> Unit
+ override val transition: FlickerBuilder.() -> Unit
get() = {
- super.transition(this, it)
+ super.transition(this)
transitions {
executeShellCommand(composePairsCommand(
primaryTaskId, secondaryTaskId, true /* pair */))
waitAppsShown(primaryApp, secondaryApp)
- setRotation(testSpec.config.endRotation)
+ setRotation(testSpec.endRotation)
}
}
@@ -85,13 +84,13 @@
@Presubmit
@Test
fun appPairsPrimaryBoundsIsVisibleAtEnd() =
- testSpec.appPairsPrimaryBoundsIsVisibleAtEnd(testSpec.config.endRotation,
+ testSpec.appPairsPrimaryBoundsIsVisibleAtEnd(testSpec.endRotation,
primaryApp.component)
@FlakyTest
@Test
fun appPairsSecondaryBoundsIsVisibleAtEnd() =
- testSpec.appPairsSecondaryBoundsIsVisibleAtEnd(testSpec.config.endRotation,
+ testSpec.appPairsSecondaryBoundsIsVisibleAtEnd(testSpec.endRotation,
secondaryApp.component)
companion object {
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/apppairs/RotateTwoLaunchedAppsRotateAndEnterAppPairsMode.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/apppairs/RotateTwoLaunchedAppsRotateAndEnterAppPairsMode.kt
index 0699a4f..c003084 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/apppairs/RotateTwoLaunchedAppsRotateAndEnterAppPairsMode.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/apppairs/RotateTwoLaunchedAppsRotateAndEnterAppPairsMode.kt
@@ -25,7 +25,6 @@
import com.android.server.wm.flicker.FlickerTestParameterFactory
import com.android.server.wm.flicker.annotation.Group1
import com.android.server.wm.flicker.dsl.FlickerBuilder
-import com.android.server.wm.flicker.endRotation
import com.android.server.wm.flicker.helpers.setRotation
import com.android.wm.shell.flicker.appPairsDividerIsVisibleAtEnd
import com.android.wm.shell.flicker.appPairsPrimaryBoundsIsVisibleAtEnd
@@ -50,11 +49,11 @@
class RotateTwoLaunchedAppsRotateAndEnterAppPairsMode(
testSpec: FlickerTestParameter
) : RotateTwoLaunchedAppsTransition(testSpec) {
- override val transition: FlickerBuilder.(Map<String, Any?>) -> Unit
+ override val transition: FlickerBuilder.() -> Unit
get() = {
- super.transition(this, it)
+ super.transition(this)
transitions {
- this.setRotation(testSpec.config.endRotation)
+ this.setRotation(testSpec.endRotation)
executeShellCommand(
composePairsCommand(primaryTaskId, secondaryTaskId, pair = true))
waitAppsShown(primaryApp, secondaryApp)
@@ -93,13 +92,13 @@
@FlakyTest(bugId = 172776659)
@Test
fun appPairsPrimaryBoundsIsVisibleAtEnd() =
- testSpec.appPairsPrimaryBoundsIsVisibleAtEnd(testSpec.config.endRotation,
+ testSpec.appPairsPrimaryBoundsIsVisibleAtEnd(testSpec.endRotation,
primaryApp.component)
@FlakyTest(bugId = 172776659)
@Test
fun appPairsSecondaryBoundsIsVisibleAtEnd() =
- testSpec.appPairsSecondaryBoundsIsVisibleAtEnd(testSpec.config.endRotation,
+ testSpec.appPairsSecondaryBoundsIsVisibleAtEnd(testSpec.endRotation,
secondaryApp.component)
companion object {
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/apppairs/RotateTwoLaunchedAppsTransition.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/apppairs/RotateTwoLaunchedAppsTransition.kt
index b95193a1..670fbd8 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/apppairs/RotateTwoLaunchedAppsTransition.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/apppairs/RotateTwoLaunchedAppsTransition.kt
@@ -34,7 +34,7 @@
override val nonResizeableApp: SplitScreenHelper?
get() = null
- override val transition: FlickerBuilder.(Map<String, Any?>) -> Unit
+ override val transition: FlickerBuilder.() -> Unit
get() = {
setup {
test {
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/bubble/BaseBubbleScreen.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/bubble/BaseBubbleScreen.kt
index b432bb6..99f7e23 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/bubble/BaseBubbleScreen.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/bubble/BaseBubbleScreen.kt
@@ -32,7 +32,6 @@
import com.android.server.wm.flicker.FlickerTestParameterFactory
import com.android.server.wm.flicker.dsl.FlickerBuilder
import com.android.server.wm.flicker.helpers.SYSTEMUI_PACKAGE
-import com.android.server.wm.flicker.repetitions
import com.android.wm.shell.flicker.helpers.LaunchBubbleHelper
import org.junit.runners.Parameterized
@@ -51,14 +50,13 @@
protected val uid = context.packageManager.getApplicationInfo(
testApp.component.packageName, 0).uid
- protected abstract val transition: FlickerBuilder.(Map<String, Any?>) -> Unit
+ protected abstract val transition: FlickerBuilder.() -> Unit
@JvmOverloads
protected open fun buildTransition(
- extraSpec: FlickerBuilder.(Map<String, Any?>) -> Unit = {}
- ): FlickerBuilder.(Map<String, Any?>) -> Unit {
- return { configuration ->
-
+ extraSpec: FlickerBuilder.() -> Unit = {}
+ ): FlickerBuilder.() -> Unit {
+ return {
setup {
test {
notifyManager.setBubblesAllowed(testApp.component.packageName,
@@ -75,7 +73,7 @@
testApp.exit()
}
- extraSpec(this, configuration)
+ extraSpec(this)
}
}
@@ -87,8 +85,7 @@
@FlickerBuilderProvider
fun buildFlicker(): FlickerBuilder {
return FlickerBuilder(instrumentation).apply {
- repeat { testSpec.config.repetitions }
- transition(this, testSpec.config)
+ transition(this)
}
}
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/bubble/DismissBubbleScreen.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/bubble/DismissBubbleScreen.kt
index 80acd3d..1605d80 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/bubble/DismissBubbleScreen.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/bubble/DismissBubbleScreen.kt
@@ -49,7 +49,7 @@
private val wm = context.getSystemService(Context.WINDOW_SERVICE) as WindowManager
private val displaySize = DisplayMetrics()
- override val transition: FlickerBuilder.(Map<String, Any?>) -> Unit
+ override val transition: FlickerBuilder.() -> Unit
get() = buildTransition {
setup {
eachRun {
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/bubble/ExpandBubbleScreen.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/bubble/ExpandBubbleScreen.kt
index 66520d2..d415aae 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/bubble/ExpandBubbleScreen.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/bubble/ExpandBubbleScreen.kt
@@ -44,7 +44,7 @@
@Group4
class ExpandBubbleScreen(testSpec: FlickerTestParameter) : BaseBubbleScreen(testSpec) {
- override val transition: FlickerBuilder.(Map<String, Any?>) -> Unit
+ override val transition: FlickerBuilder.() -> Unit
get() = buildTransition {
setup {
test {
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/bubble/LaunchBubbleFromLockScreen.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/bubble/LaunchBubbleFromLockScreen.kt
index bbdf997..61e27f2 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/bubble/LaunchBubbleFromLockScreen.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/bubble/LaunchBubbleFromLockScreen.kt
@@ -42,7 +42,7 @@
@Group4
class LaunchBubbleFromLockScreen(testSpec: FlickerTestParameter) : BaseBubbleScreen(testSpec) {
- override val transition: FlickerBuilder.(Map<String, Any?>) -> Unit
+ override val transition: FlickerBuilder.() -> Unit
get() = buildTransition {
setup {
eachRun {
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/bubble/LaunchBubbleScreen.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/bubble/LaunchBubbleScreen.kt
index 97c27c7..a8f17a75 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/bubble/LaunchBubbleScreen.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/bubble/LaunchBubbleScreen.kt
@@ -41,7 +41,7 @@
@Group4
class LaunchBubbleScreen(testSpec: FlickerTestParameter) : BaseBubbleScreen(testSpec) {
- override val transition: FlickerBuilder.(Map<String, Any?>) -> Unit
+ override val transition: FlickerBuilder.() -> Unit
get() = buildTransition {
transitions {
val addBubbleBtn = waitAndGetAddBubbleBtn()
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/bubble/MultiBubblesScreen.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/bubble/MultiBubblesScreen.kt
index d92e047..8e5a33c 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/bubble/MultiBubblesScreen.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/bubble/MultiBubblesScreen.kt
@@ -43,7 +43,7 @@
@Group4
class MultiBubblesScreen(testSpec: FlickerTestParameter) : BaseBubbleScreen(testSpec) {
- override val transition: FlickerBuilder.(Map<String, Any?>) -> Unit
+ override val transition: FlickerBuilder.() -> Unit
get() = buildTransition {
setup {
test {
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/EnterSplitScreenDockActivity.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/EnterSplitScreenDockActivity.kt
index bd44d08..c86a122 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/EnterSplitScreenDockActivity.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/EnterSplitScreenDockActivity.kt
@@ -28,7 +28,6 @@
import com.android.server.wm.flicker.dsl.FlickerBuilder
import com.android.server.wm.flicker.helpers.launchSplitScreen
import com.android.server.wm.flicker.navBarWindowIsVisible
-import com.android.server.wm.flicker.startRotation
import com.android.server.wm.flicker.statusBarWindowIsVisible
import com.android.server.wm.traces.common.FlickerComponentName
import com.android.wm.shell.flicker.dockedStackDividerBecomesVisible
@@ -53,9 +52,9 @@
class EnterSplitScreenDockActivity(
testSpec: FlickerTestParameter
) : LegacySplitScreenTransition(testSpec) {
- override val transition: FlickerBuilder.(Map<String, Any?>) -> Unit
- get() = { configuration ->
- super.transition(this, configuration)
+ override val transition: FlickerBuilder.() -> Unit
+ get() = {
+ super.transition(this)
transitions {
device.launchSplitScreen(wmHelper)
}
@@ -69,7 +68,7 @@
@Presubmit
@Test
fun dockedStackPrimaryBoundsIsVisibleAtEnd() =
- testSpec.dockedStackPrimaryBoundsIsVisibleAtEnd(testSpec.config.startRotation,
+ testSpec.dockedStackPrimaryBoundsIsVisibleAtEnd(testSpec.startRotation,
splitScreenApp.component)
@Presubmit
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/EnterSplitScreenFromDetachedRecentTask.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/EnterSplitScreenFromDetachedRecentTask.kt
index 625d48b..2f9244b 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/EnterSplitScreenFromDetachedRecentTask.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/EnterSplitScreenFromDetachedRecentTask.kt
@@ -48,9 +48,9 @@
testSpec: FlickerTestParameter
) : LegacySplitScreenTransition(testSpec) {
- override val transition: FlickerBuilder.(Map<String, Any?>) -> Unit
- get() = { configuration ->
- cleanSetup(this, configuration)
+ override val transition: FlickerBuilder.() -> Unit
+ get() = {
+ cleanSetup(this)
setup {
eachRun {
splitScreenApp.launchViaIntent(wmHelper)
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/EnterSplitScreenLaunchToSide.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/EnterSplitScreenLaunchToSide.kt
index 2ed2806..1740c3e 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/EnterSplitScreenLaunchToSide.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/EnterSplitScreenLaunchToSide.kt
@@ -27,7 +27,6 @@
import com.android.server.wm.flicker.helpers.launchSplitScreen
import com.android.server.wm.flicker.helpers.reopenAppFromOverview
import com.android.server.wm.flicker.navBarWindowIsVisible
-import com.android.server.wm.flicker.startRotation
import com.android.server.wm.flicker.statusBarWindowIsVisible
import com.android.server.wm.traces.common.FlickerComponentName
import com.android.wm.shell.flicker.dockedStackDividerBecomesVisible
@@ -52,9 +51,9 @@
class EnterSplitScreenLaunchToSide(
testSpec: FlickerTestParameter
) : LegacySplitScreenTransition(testSpec) {
- override val transition: FlickerBuilder.(Map<String, Any?>) -> Unit
- get() = { configuration ->
- super.transition(this, configuration)
+ override val transition: FlickerBuilder.() -> Unit
+ get() = {
+ super.transition(this)
transitions {
device.launchSplitScreen(wmHelper)
device.reopenAppFromOverview(wmHelper)
@@ -69,13 +68,13 @@
@Presubmit
@Test
fun dockedStackPrimaryBoundsIsVisibleAtEnd() =
- testSpec.dockedStackPrimaryBoundsIsVisibleAtEnd(testSpec.config.startRotation,
+ testSpec.dockedStackPrimaryBoundsIsVisibleAtEnd(testSpec.startRotation,
splitScreenApp.component)
@Presubmit
@Test
fun dockedStackSecondaryBoundsIsVisibleAtEnd() =
- testSpec.dockedStackSecondaryBoundsIsVisibleAtEnd(testSpec.config.startRotation,
+ testSpec.dockedStackSecondaryBoundsIsVisibleAtEnd(testSpec.startRotation,
secondaryApp.component)
@Presubmit
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/EnterSplitScreenNotSupportNonResizable.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/EnterSplitScreenNotSupportNonResizable.kt
index ee6cf34..4c063b9 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/EnterSplitScreenNotSupportNonResizable.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/EnterSplitScreenNotSupportNonResizable.kt
@@ -55,9 +55,9 @@
testSpec: FlickerTestParameter
) : LegacySplitScreenTransition(testSpec) {
- override val transition: FlickerBuilder.(Map<String, Any?>) -> Unit
- get() = { configuration ->
- cleanSetup(this, configuration)
+ override val transition: FlickerBuilder.() -> Unit
+ get() = {
+ cleanSetup(this)
setup {
eachRun {
nonResizeableApp.launchViaIntent(wmHelper)
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/EnterSplitScreenSupportNonResizable.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/EnterSplitScreenSupportNonResizable.kt
index 163b6ffda..f75dee6 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/EnterSplitScreenSupportNonResizable.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/EnterSplitScreenSupportNonResizable.kt
@@ -54,9 +54,9 @@
testSpec: FlickerTestParameter
) : LegacySplitScreenTransition(testSpec) {
- override val transition: FlickerBuilder.(Map<String, Any?>) -> Unit
- get() = { configuration ->
- cleanSetup(this, configuration)
+ override val transition: FlickerBuilder.() -> Unit
+ get() = {
+ cleanSetup(this)
setup {
eachRun {
nonResizeableApp.launchViaIntent(wmHelper)
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/ExitLegacySplitScreenFromBottom.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/ExitLegacySplitScreenFromBottom.kt
index 2b629b0..3885155 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/ExitLegacySplitScreenFromBottom.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/ExitLegacySplitScreenFromBottom.kt
@@ -50,9 +50,9 @@
class ExitLegacySplitScreenFromBottom(
testSpec: FlickerTestParameter
) : LegacySplitScreenTransition(testSpec) {
- override val transition: FlickerBuilder.(Map<String, Any?>) -> Unit
- get() = { configuration ->
- super.transition(this, configuration)
+ override val transition: FlickerBuilder.() -> Unit
+ get() = {
+ super.transition(this)
setup {
eachRun {
splitScreenApp.launchViaIntent(wmHelper)
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/ExitPrimarySplitScreenShowSecondaryFullscreen.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/ExitPrimarySplitScreenShowSecondaryFullscreen.kt
index 95fe3be..d913a6d 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/ExitPrimarySplitScreenShowSecondaryFullscreen.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/ExitPrimarySplitScreenShowSecondaryFullscreen.kt
@@ -51,9 +51,9 @@
class ExitPrimarySplitScreenShowSecondaryFullscreen(
testSpec: FlickerTestParameter
) : LegacySplitScreenTransition(testSpec) {
- override val transition: FlickerBuilder.(Map<String, Any?>) -> Unit
- get() = { configuration ->
- super.transition(this, configuration)
+ override val transition: FlickerBuilder.() -> Unit
+ get() = {
+ super.transition(this)
teardown {
eachRun {
secondaryApp.exit(wmHelper)
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/LegacySplitScreenFromIntentNotSupportNonResizable.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/LegacySplitScreenFromIntentNotSupportNonResizable.kt
index f7d628d..f3ff7b1 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/LegacySplitScreenFromIntentNotSupportNonResizable.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/LegacySplitScreenFromIntentNotSupportNonResizable.kt
@@ -53,9 +53,9 @@
testSpec: FlickerTestParameter
) : LegacySplitScreenTransition(testSpec) {
- override val transition: FlickerBuilder.(Map<String, Any?>) -> Unit
- get() = { configuration ->
- cleanSetup(this, configuration)
+ override val transition: FlickerBuilder.() -> Unit
+ get() = {
+ cleanSetup(this)
setup {
eachRun {
splitScreenApp.launchViaIntent(wmHelper)
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/LegacySplitScreenFromIntentSupportNonResizable.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/LegacySplitScreenFromIntentSupportNonResizable.kt
index a5c6571..42e707a 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/LegacySplitScreenFromIntentSupportNonResizable.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/LegacySplitScreenFromIntentSupportNonResizable.kt
@@ -53,9 +53,9 @@
testSpec: FlickerTestParameter
) : LegacySplitScreenTransition(testSpec) {
- override val transition: FlickerBuilder.(Map<String, Any?>) -> Unit
- get() = { configuration ->
- cleanSetup(this, configuration)
+ override val transition: FlickerBuilder.() -> Unit
+ get() = {
+ cleanSetup(this)
setup {
eachRun {
splitScreenApp.launchViaIntent(wmHelper)
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/LegacySplitScreenFromRecentNotSupportNonResizable.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/LegacySplitScreenFromRecentNotSupportNonResizable.kt
index 6f486b0..079a6ef 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/LegacySplitScreenFromRecentNotSupportNonResizable.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/LegacySplitScreenFromRecentNotSupportNonResizable.kt
@@ -55,9 +55,9 @@
testSpec: FlickerTestParameter
) : LegacySplitScreenTransition(testSpec) {
- override val transition: FlickerBuilder.(Map<String, Any?>) -> Unit
- get() = { configuration ->
- cleanSetup(this, configuration)
+ override val transition: FlickerBuilder.() -> Unit
+ get() = {
+ cleanSetup(this)
setup {
eachRun {
nonResizeableApp.launchViaIntent(wmHelper)
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/LegacySplitScreenFromRecentSupportNonResizable.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/LegacySplitScreenFromRecentSupportNonResizable.kt
index f03c927..6ac8683 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/LegacySplitScreenFromRecentSupportNonResizable.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/LegacySplitScreenFromRecentSupportNonResizable.kt
@@ -54,9 +54,9 @@
testSpec: FlickerTestParameter
) : LegacySplitScreenTransition(testSpec) {
- override val transition: FlickerBuilder.(Map<String, Any?>) -> Unit
- get() = { configuration ->
- cleanSetup(this, configuration)
+ override val transition: FlickerBuilder.() -> Unit
+ get() = {
+ cleanSetup(this)
setup {
eachRun {
nonResizeableApp.launchViaIntent(wmHelper)
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/LegacySplitScreenRotateTransition.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/LegacySplitScreenRotateTransition.kt
index 1e89a25..b01f41c 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/LegacySplitScreenRotateTransition.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/LegacySplitScreenRotateTransition.kt
@@ -26,7 +26,7 @@
abstract class LegacySplitScreenRotateTransition(
testSpec: FlickerTestParameter
) : LegacySplitScreenTransition(testSpec) {
- override val transition: FlickerBuilder.(Map<String, Any?>) -> Unit
+ override val transition: FlickerBuilder.() -> Unit
get() = {
setup {
eachRun {
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/LegacySplitScreenToLauncher.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/LegacySplitScreenToLauncher.kt
index 2ccd03b..5fe13e0 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/LegacySplitScreenToLauncher.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/LegacySplitScreenToLauncher.kt
@@ -25,7 +25,6 @@
import com.android.server.wm.flicker.FlickerTestParameterFactory
import com.android.server.wm.flicker.annotation.Group2
import com.android.server.wm.flicker.dsl.FlickerBuilder
-import com.android.server.wm.flicker.endRotation
import com.android.server.wm.flicker.entireScreenCovered
import com.android.server.wm.flicker.helpers.exitSplitScreen
import com.android.server.wm.flicker.helpers.launchSplitScreen
@@ -61,8 +60,8 @@
) : LegacySplitScreenTransition(testSpec) {
private val testApp = SimpleAppHelper(instrumentation)
- override val transition: FlickerBuilder.(Map<String, Any?>) -> Unit
- get() = { configuration ->
+ override val transition: FlickerBuilder.() -> Unit
+ get() = {
setup {
test {
device.wakeUpAndGoToHomeScreen()
@@ -70,7 +69,7 @@
}
eachRun {
testApp.launchViaIntent(wmHelper)
- this.setRotation(configuration.endRotation)
+ this.setRotation(testSpec.endRotation)
device.launchSplitScreen(wmHelper)
device.waitForIdle()
}
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/LegacySplitScreenTransition.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/LegacySplitScreenTransition.kt
index 661c8b6..a4a1f61 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/LegacySplitScreenTransition.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/LegacySplitScreenTransition.kt
@@ -25,12 +25,9 @@
import com.android.server.wm.flicker.FlickerBuilderProvider
import com.android.server.wm.flicker.FlickerTestParameter
import com.android.server.wm.flicker.dsl.FlickerBuilder
-import com.android.server.wm.flicker.helpers.isRotated
import com.android.server.wm.flicker.helpers.openQuickStepAndClearRecentAppsFromOverview
import com.android.server.wm.flicker.helpers.setRotation
import com.android.server.wm.flicker.helpers.wakeUpAndGoToHomeScreen
-import com.android.server.wm.flicker.repetitions
-import com.android.server.wm.flicker.startRotation
import com.android.server.wm.traces.common.FlickerComponentName
import com.android.wm.shell.flicker.helpers.BaseAppHelper.Companion.isShellTransitionsEnabled
import com.android.wm.shell.flicker.helpers.MultiWindowHelper.Companion.getDevEnableNonResizableMultiWindow
@@ -45,7 +42,6 @@
abstract class LegacySplitScreenTransition(protected val testSpec: FlickerTestParameter) {
protected val instrumentation: Instrumentation = InstrumentationRegistry.getInstrumentation()
protected val context: Context = instrumentation.context
- protected val isRotated = testSpec.config.startRotation.isRotated()
protected val splitScreenApp = SplitScreenHelper.getPrimary(instrumentation)
protected val secondaryApp = SplitScreenHelper.getSecondary(instrumentation)
protected val nonResizeableApp = SplitScreenHelper.getNonResizeable(instrumentation)
@@ -82,15 +78,15 @@
FlickerComponentName.SPLASH_SCREEN,
FlickerComponentName.SNAPSHOT)
- protected open val transition: FlickerBuilder.(Map<String, Any?>) -> Unit
- get() = { configuration ->
+ protected open val transition: FlickerBuilder.() -> Unit
+ get() = {
setup {
eachRun {
device.wakeUpAndGoToHomeScreen()
device.openQuickStepAndClearRecentAppsFromOverview(wmHelper)
secondaryApp.launchViaIntent(wmHelper)
splitScreenApp.launchViaIntent(wmHelper)
- this.setRotation(configuration.startRotation)
+ this.setRotation(testSpec.startRotation)
}
}
teardown {
@@ -105,19 +101,17 @@
@FlickerBuilderProvider
fun buildFlicker(): FlickerBuilder {
return FlickerBuilder(instrumentation).apply {
- withTestName { testSpec.name }
- repeat { testSpec.config.repetitions }
- transition(this, testSpec.config)
+ transition(this)
}
}
- internal open val cleanSetup: FlickerBuilder.(Map<String, Any?>) -> Unit
- get() = { configuration ->
+ internal open val cleanSetup: FlickerBuilder.() -> Unit
+ get() = {
setup {
eachRun {
device.wakeUpAndGoToHomeScreen()
device.openQuickStepAndClearRecentAppsFromOverview(wmHelper)
- this.setRotation(configuration.startRotation)
+ this.setRotation(testSpec.startRotation)
}
}
teardown {
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/OpenAppToLegacySplitScreen.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/OpenAppToLegacySplitScreen.kt
index 34eff80..087b21c 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/OpenAppToLegacySplitScreen.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/OpenAppToLegacySplitScreen.kt
@@ -49,9 +49,9 @@
class OpenAppToLegacySplitScreen(
testSpec: FlickerTestParameter
) : LegacySplitScreenTransition(testSpec) {
- override val transition: FlickerBuilder.(Map<String, Any?>) -> Unit
- get() = { configuration ->
- super.transition(this, configuration)
+ override val transition: FlickerBuilder.() -> Unit
+ get() = {
+ super.transition(this)
transitions {
device.launchSplitScreen(wmHelper)
wmHelper.waitForAppTransitionIdle()
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/ResizeLegacySplitScreen.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/ResizeLegacySplitScreen.kt
index 58e1def..a238bc2 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/ResizeLegacySplitScreen.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/ResizeLegacySplitScreen.kt
@@ -37,7 +37,6 @@
import com.android.server.wm.flicker.navBarLayerIsVisible
import com.android.server.wm.flicker.navBarLayerRotatesAndScales
import com.android.server.wm.flicker.navBarWindowIsVisible
-import com.android.server.wm.flicker.startRotation
import com.android.server.wm.flicker.statusBarLayerIsVisible
import com.android.server.wm.flicker.statusBarLayerRotatesScales
import com.android.server.wm.flicker.statusBarWindowIsVisible
@@ -69,12 +68,12 @@
private val testAppTop = SimpleAppHelper(instrumentation)
private val testAppBottom = ImeAppHelper(instrumentation)
- override val transition: FlickerBuilder.(Map<String, Any?>) -> Unit
- get() = { configuration ->
+ override val transition: FlickerBuilder.() -> Unit
+ get() = {
setup {
eachRun {
device.wakeUpAndGoToHomeScreen()
- this.setRotation(configuration.startRotation)
+ this.setRotation(testSpec.startRotation)
this.launcherStrategy.clearRecentAppsFromOverview()
testAppBottom.launchViaIntent(wmHelper)
device.pressHome()
@@ -220,8 +219,8 @@
.map {
val description = (startRatio.toString().replace("/", "-") + "_to_" +
stopRatio.toString().replace("/", "-"))
- val newName = "${FlickerTestParameter.defaultName(it.config)}_$description"
- FlickerTestParameter(it.config, name = newName)
+ val newName = "${FlickerTestParameter.defaultName(it)}_$description"
+ FlickerTestParameter(it.config, nameOverride = newName)
}
}
}
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/RotateOneLaunchedAppAndEnterSplitScreen.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/RotateOneLaunchedAppAndEnterSplitScreen.kt
index 8a50bc0..50cd548 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/RotateOneLaunchedAppAndEnterSplitScreen.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/RotateOneLaunchedAppAndEnterSplitScreen.kt
@@ -29,7 +29,6 @@
import com.android.server.wm.flicker.helpers.setRotation
import com.android.server.wm.flicker.navBarLayerRotatesAndScales
import com.android.server.wm.flicker.navBarWindowIsVisible
-import com.android.server.wm.flicker.startRotation
import com.android.server.wm.flicker.statusBarLayerRotatesScales
import com.android.server.wm.flicker.statusBarWindowIsVisible
import com.android.wm.shell.flicker.dockedStackDividerIsVisibleAtEnd
@@ -53,12 +52,12 @@
class RotateOneLaunchedAppAndEnterSplitScreen(
testSpec: FlickerTestParameter
) : LegacySplitScreenRotateTransition(testSpec) {
- override val transition: FlickerBuilder.(Map<String, Any?>) -> Unit
- get() = { configuration ->
- super.transition(this, configuration)
+ override val transition: FlickerBuilder.() -> Unit
+ get() = {
+ super.transition(this)
transitions {
device.launchSplitScreen(wmHelper)
- this.setRotation(testSpec.config.startRotation)
+ this.setRotation(testSpec.startRotation)
}
}
@@ -69,7 +68,7 @@
@Presubmit
@Test
fun dockedStackPrimaryBoundsIsVisibleAtEnd() =
- testSpec.dockedStackPrimaryBoundsIsVisibleAtEnd(testSpec.config.startRotation,
+ testSpec.dockedStackPrimaryBoundsIsVisibleAtEnd(testSpec.startRotation,
splitScreenApp.component)
@Presubmit
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/RotateOneLaunchedAppInSplitScreenMode.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/RotateOneLaunchedAppInSplitScreenMode.kt
index 84676a9..8d52225 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/RotateOneLaunchedAppInSplitScreenMode.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/RotateOneLaunchedAppInSplitScreenMode.kt
@@ -29,7 +29,6 @@
import com.android.server.wm.flicker.helpers.setRotation
import com.android.server.wm.flicker.navBarLayerRotatesAndScales
import com.android.server.wm.flicker.navBarWindowIsVisible
-import com.android.server.wm.flicker.startRotation
import com.android.server.wm.flicker.statusBarLayerRotatesScales
import com.android.server.wm.flicker.statusBarWindowIsVisible
import com.android.wm.shell.flicker.dockedStackDividerIsVisibleAtEnd
@@ -53,11 +52,11 @@
class RotateOneLaunchedAppInSplitScreenMode(
testSpec: FlickerTestParameter
) : LegacySplitScreenRotateTransition(testSpec) {
- override val transition: FlickerBuilder.(Map<String, Any?>) -> Unit
- get() = { configuration ->
- super.transition(this, configuration)
+ override val transition: FlickerBuilder.() -> Unit
+ get() = {
+ super.transition(this)
transitions {
- this.setRotation(testSpec.config.startRotation)
+ this.setRotation(testSpec.startRotation)
device.launchSplitScreen(wmHelper)
}
}
@@ -69,7 +68,7 @@
@Presubmit
@Test
fun dockedStackPrimaryBoundsIsVisibleAtEnd() = testSpec.dockedStackPrimaryBoundsIsVisibleAtEnd(
- testSpec.config.startRotation, splitScreenApp.component)
+ testSpec.startRotation, splitScreenApp.component)
@Presubmit
@Test
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/RotateTwoLaunchedAppAndEnterSplitScreen.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/RotateTwoLaunchedAppAndEnterSplitScreen.kt
index 2abdca9..070f636 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/RotateTwoLaunchedAppAndEnterSplitScreen.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/RotateTwoLaunchedAppAndEnterSplitScreen.kt
@@ -29,7 +29,6 @@
import com.android.server.wm.flicker.helpers.setRotation
import com.android.server.wm.flicker.navBarLayerRotatesAndScales
import com.android.server.wm.flicker.navBarWindowIsVisible
-import com.android.server.wm.flicker.startRotation
import com.android.server.wm.flicker.statusBarLayerRotatesScales
import com.android.server.wm.flicker.statusBarWindowIsVisible
import com.android.wm.shell.flicker.dockedStackDividerIsVisibleAtEnd
@@ -54,11 +53,11 @@
class RotateTwoLaunchedAppAndEnterSplitScreen(
testSpec: FlickerTestParameter
) : LegacySplitScreenRotateTransition(testSpec) {
- override val transition: FlickerBuilder.(Map<String, Any?>) -> Unit
- get() = { configuration ->
- super.transition(this, configuration)
+ override val transition: FlickerBuilder.() -> Unit
+ get() = {
+ super.transition(this)
transitions {
- this.setRotation(testSpec.config.startRotation)
+ this.setRotation(testSpec.startRotation)
device.launchSplitScreen(wmHelper)
device.reopenAppFromOverview(wmHelper)
}
@@ -71,13 +70,13 @@
@Presubmit
@Test
fun dockedStackPrimaryBoundsIsVisibleAtEnd() =
- testSpec.dockedStackPrimaryBoundsIsVisibleAtEnd(testSpec.config.startRotation,
+ testSpec.dockedStackPrimaryBoundsIsVisibleAtEnd(testSpec.startRotation,
splitScreenApp.component)
@Presubmit
@Test
fun dockedStackSecondaryBoundsIsVisibleAtEnd() =
- testSpec.dockedStackSecondaryBoundsIsVisibleAtEnd(testSpec.config.startRotation,
+ testSpec.dockedStackSecondaryBoundsIsVisibleAtEnd(testSpec.startRotation,
secondaryApp.component)
@Presubmit
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/RotateTwoLaunchedAppInSplitScreenMode.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/RotateTwoLaunchedAppInSplitScreenMode.kt
index fe9b9f5..fabbd26 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/RotateTwoLaunchedAppInSplitScreenMode.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/RotateTwoLaunchedAppInSplitScreenMode.kt
@@ -30,7 +30,6 @@
import com.android.server.wm.flicker.helpers.setRotation
import com.android.server.wm.flicker.navBarLayerRotatesAndScales
import com.android.server.wm.flicker.navBarWindowIsVisible
-import com.android.server.wm.flicker.startRotation
import com.android.server.wm.flicker.statusBarLayerRotatesScales
import com.android.server.wm.flicker.statusBarWindowIsVisible
import com.android.wm.shell.flicker.dockedStackDividerIsVisibleAtEnd
@@ -55,18 +54,18 @@
class RotateTwoLaunchedAppInSplitScreenMode(
testSpec: FlickerTestParameter
) : LegacySplitScreenRotateTransition(testSpec) {
- override val transition: FlickerBuilder.(Map<String, Any?>) -> Unit
- get() = { configuration ->
- super.transition(this, configuration)
+ override val transition: FlickerBuilder.() -> Unit
+ get() = {
+ super.transition(this)
setup {
eachRun {
device.launchSplitScreen(wmHelper)
device.reopenAppFromOverview(wmHelper)
- this.setRotation(testSpec.config.startRotation)
+ this.setRotation(testSpec.startRotation)
}
}
transitions {
- this.setRotation(testSpec.config.startRotation)
+ this.setRotation(testSpec.startRotation)
}
}
@@ -77,13 +76,13 @@
@Presubmit
@Test
fun dockedStackPrimaryBoundsIsVisibleAtEnd() =
- testSpec.dockedStackPrimaryBoundsIsVisibleAtEnd(testSpec.config.startRotation,
+ testSpec.dockedStackPrimaryBoundsIsVisibleAtEnd(testSpec.startRotation,
splitScreenApp.component)
@Presubmit
@Test
fun dockedStackSecondaryBoundsIsVisibleAtEnd() =
- testSpec.dockedStackSecondaryBoundsIsVisibleAtEnd(testSpec.config.startRotation,
+ testSpec.dockedStackSecondaryBoundsIsVisibleAtEnd(testSpec.startRotation,
secondaryApp.component)
@Presubmit
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/EnterPipTest.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/EnterPipTest.kt
index 52a744f..33626d0 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/EnterPipTest.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/EnterPipTest.kt
@@ -58,7 +58,7 @@
/**
* Defines the transition used to run the test
*/
- override val transition: FlickerBuilder.(Map<String, Any?>) -> Unit
+ override val transition: FlickerBuilder.() -> Unit
get() = buildTransition(eachRun = true, stringExtras = emptyMap()) {
transitions {
pipApp.clickEnterPipButton(wmHelper)
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/EnterPipToOtherOrientationTest.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/EnterPipToOtherOrientationTest.kt
index c8c3f4d..791505b 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/EnterPipToOtherOrientationTest.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/EnterPipToOtherOrientationTest.kt
@@ -74,9 +74,9 @@
/**
* Defines the transition used to run the test
*/
- override val transition: FlickerBuilder.(Map<String, Any?>) -> Unit
- get() = { configuration ->
- setupAndTeardown(this, configuration)
+ override val transition: FlickerBuilder.() -> Unit
+ get() = {
+ setupAndTeardown(this)
setup {
eachRun {
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/ExitPipToAppTransition.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/ExitPipToAppTransition.kt
index 64b7eb5..8267442 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/ExitPipToAppTransition.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/ExitPipToAppTransition.kt
@@ -102,7 +102,7 @@
}
/**
- * Checks that the visible region of [pipApp] covers the full display area at the end of
+ * Checks that the visible region oft [pipApp] covers the full display area at the end of
* the transition
*/
@Presubmit
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/ExitPipTransition.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/ExitPipTransition.kt
index 5207fed..6c9fed9 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/ExitPipTransition.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/ExitPipTransition.kt
@@ -23,18 +23,17 @@
import com.android.server.wm.flicker.LAUNCHER_COMPONENT
import com.android.server.wm.flicker.dsl.FlickerBuilder
import com.android.server.wm.flicker.helpers.setRotation
-import com.android.server.wm.flicker.startRotation
import org.junit.Test
/**
* Base class for exiting pip (closing pip window) without returning to the app
*/
abstract class ExitPipTransition(testSpec: FlickerTestParameter) : PipTransition(testSpec) {
- override val transition: FlickerBuilder.(Map<String, Any?>) -> Unit
- get() = buildTransition(eachRun = true) { configuration ->
+ override val transition: FlickerBuilder.() -> Unit
+ get() = buildTransition(eachRun = true) {
setup {
eachRun {
- this.setRotation(configuration.startRotation)
+ this.setRotation(testSpec.startRotation)
}
}
teardown {
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/ExitPipViaExpandButtonClickTest.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/ExitPipViaExpandButtonClickTest.kt
index b53342d..5f29dbc 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/ExitPipViaExpandButtonClickTest.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/ExitPipViaExpandButtonClickTest.kt
@@ -59,7 +59,7 @@
/**
* Defines the transition used to run the test
*/
- override val transition: FlickerBuilder.(Map<String, Any?>) -> Unit
+ override val transition: FlickerBuilder.() -> Unit
get() = buildTransition(eachRun = true) {
setup {
eachRun {
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/ExitPipViaIntentTest.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/ExitPipViaIntentTest.kt
index 1fec3cf..00ccf26 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/ExitPipViaIntentTest.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/ExitPipViaIntentTest.kt
@@ -56,7 +56,7 @@
/**
* Defines the transition used to run the test
*/
- override val transition: FlickerBuilder.(Map<String, Any?>) -> Unit
+ override val transition: FlickerBuilder.() -> Unit
get() = buildTransition(eachRun = true) {
setup {
eachRun {
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/ExitPipWithDismissButtonTest.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/ExitPipWithDismissButtonTest.kt
index 73626c2..b0b11e9 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/ExitPipWithDismissButtonTest.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/ExitPipWithDismissButtonTest.kt
@@ -52,9 +52,9 @@
@FixMethodOrder(MethodSorters.NAME_ASCENDING)
@Group3
class ExitPipWithDismissButtonTest(testSpec: FlickerTestParameter) : ExitPipTransition(testSpec) {
- override val transition: FlickerBuilder.(Map<String, Any?>) -> Unit
+ override val transition: FlickerBuilder.() -> Unit
get() = {
- super.transition(this, it)
+ super.transition(this)
transitions {
pipApp.closePipWindow(wmHelper)
}
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/ExitPipWithSwipeDownTest.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/ExitPipWithSwipeDownTest.kt
index 9e43dee..f4eb701 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/ExitPipWithSwipeDownTest.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/ExitPipWithSwipeDownTest.kt
@@ -55,9 +55,9 @@
@FixMethodOrder(MethodSorters.NAME_ASCENDING)
@Group3
class ExitPipWithSwipeDownTest(testSpec: FlickerTestParameter) : ExitPipTransition(testSpec) {
- override val transition: FlickerBuilder.(Map<String, Any?>) -> Unit
- get() = { args ->
- super.transition(this, args)
+ override val transition: FlickerBuilder.() -> Unit
+ get() = {
+ super.transition(this)
transitions {
val pipRegion = wmHelper.getWindowRegion(pipApp.component).bounds
val pipCenterX = pipRegion.centerX()
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/ExpandPipOnDoubleClickTest.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/ExpandPipOnDoubleClickTest.kt
index d0fee9a..f196764 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/ExpandPipOnDoubleClickTest.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/ExpandPipOnDoubleClickTest.kt
@@ -55,7 +55,7 @@
@FixMethodOrder(MethodSorters.NAME_ASCENDING)
@Group3
class ExpandPipOnDoubleClickTest(testSpec: FlickerTestParameter) : PipTransition(testSpec) {
- override val transition: FlickerBuilder.(Map<String, Any?>) -> Unit
+ override val transition: FlickerBuilder.() -> Unit
get() = buildTransition(eachRun = true) {
transitions {
pipApp.doubleClickPipWindow(wmHelper)
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/MovePipDownShelfHeightChangeTest.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/MovePipDownShelfHeightChangeTest.kt
index 0ab857d..d9685f3 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/MovePipDownShelfHeightChangeTest.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/MovePipDownShelfHeightChangeTest.kt
@@ -59,7 +59,7 @@
/**
* Defines the transition used to run the test
*/
- override val transition: FlickerBuilder.(Map<String, Any?>) -> Unit
+ override val transition: FlickerBuilder.() -> Unit
get() = buildTransition(eachRun = false) {
teardown {
eachRun {
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/MovePipUpShelfHeightChangeTest.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/MovePipUpShelfHeightChangeTest.kt
index e507edf..c6b42ea 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/MovePipUpShelfHeightChangeTest.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/MovePipUpShelfHeightChangeTest.kt
@@ -59,7 +59,7 @@
/**
* Defines the transition used to run the test
*/
- override val transition: FlickerBuilder.(Map<String, Any?>) -> Unit
+ override val transition: FlickerBuilder.() -> Unit
get() = buildTransition(eachRun = false) {
teardown {
eachRun {
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/PipKeyboardTest.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/PipKeyboardTest.kt
index aba8ace..45cbdc8 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/PipKeyboardTest.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/PipKeyboardTest.kt
@@ -26,7 +26,6 @@
import com.android.server.wm.flicker.dsl.FlickerBuilder
import com.android.server.wm.flicker.helpers.WindowUtils
import com.android.server.wm.flicker.helpers.setRotation
-import com.android.server.wm.flicker.startRotation
import com.android.server.wm.traces.common.FlickerComponentName
import com.android.wm.shell.flicker.helpers.ImeAppHelper
import org.junit.FixMethodOrder
@@ -47,12 +46,12 @@
class PipKeyboardTest(testSpec: FlickerTestParameter) : PipTransition(testSpec) {
private val imeApp = ImeAppHelper(instrumentation)
- override val transition: FlickerBuilder.(Map<String, Any?>) -> Unit
- get() = buildTransition(eachRun = false) { configuration ->
+ override val transition: FlickerBuilder.() -> Unit
+ get() = buildTransition(eachRun = false) {
setup {
test {
imeApp.launchViaIntent(wmHelper)
- setRotation(configuration.startRotation)
+ setRotation(testSpec.startRotation)
}
}
teardown {
@@ -78,7 +77,7 @@
@Test
fun pipInVisibleBounds() {
testSpec.assertWm {
- val displayBounds = WindowUtils.getDisplayBounds(testSpec.config.startRotation)
+ val displayBounds = WindowUtils.getDisplayBounds(testSpec.startRotation)
coversAtMost(displayBounds, pipApp.component)
}
}
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/PipLegacySplitScreenTest.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/PipLegacySplitScreenTest.kt
index 9bea5c0..3e3ea16 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/PipLegacySplitScreenTest.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/PipLegacySplitScreenTest.kt
@@ -27,7 +27,6 @@
import com.android.server.wm.flicker.dsl.FlickerBuilder
import com.android.server.wm.flicker.helpers.launchSplitScreen
import com.android.server.wm.flicker.helpers.wakeUpAndGoToHomeScreen
-import com.android.server.wm.flicker.repetitions
import com.android.server.wm.flicker.rules.RemoveAllTasksButHomeRule.Companion.removeAllTasksButHome
import com.android.wm.shell.flicker.helpers.BaseAppHelper.Companion.isShellTransitionsEnabled
import com.android.wm.shell.flicker.helpers.FixedAppHelper
@@ -64,10 +63,8 @@
assumeFalse(isShellTransitionsEnabled())
}
- override val transition: FlickerBuilder.(Map<String, Any?>) -> Unit
+ override val transition: FlickerBuilder.() -> Unit
get() = {
- withTestName { testSpec.name }
- repeat { testSpec.config.repetitions }
setup {
test {
removeAllTasksButHome()
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/PipRotationTest.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/PipRotationTest.kt
index 669f37a..af984b3 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/PipRotationTest.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/PipRotationTest.kt
@@ -25,12 +25,10 @@
import com.android.server.wm.flicker.FlickerTestParameterFactory
import com.android.server.wm.flicker.annotation.Group4
import com.android.server.wm.flicker.dsl.FlickerBuilder
-import com.android.server.wm.flicker.endRotation
import com.android.server.wm.flicker.entireScreenCovered
import com.android.server.wm.flicker.helpers.WindowUtils
import com.android.server.wm.flicker.helpers.setRotation
import com.android.server.wm.flicker.navBarLayerRotatesAndScales
-import com.android.server.wm.flicker.startRotation
import com.android.server.wm.flicker.statusBarLayerRotatesScales
import com.android.wm.shell.flicker.helpers.FixedAppHelper
import org.junit.FixMethodOrder
@@ -47,7 +45,7 @@
* Actions:
* Launch a [pipApp] in pip mode
* Launch another app [fixedApp] (appears below pip)
- * Rotate the screen from [testSpec.config.startRotation] to [testSpec.config.endRotation]
+ * Rotate the screen from [testSpec.startRotation] to [testSpec.endRotation]
* (usually, 0->90 and 90->0)
*
* Notes:
@@ -65,21 +63,21 @@
@Group4
class PipRotationTest(testSpec: FlickerTestParameter) : PipTransition(testSpec) {
private val fixedApp = FixedAppHelper(instrumentation)
- private val screenBoundsStart = WindowUtils.getDisplayBounds(testSpec.config.startRotation)
- private val screenBoundsEnd = WindowUtils.getDisplayBounds(testSpec.config.endRotation)
+ private val screenBoundsStart = WindowUtils.getDisplayBounds(testSpec.startRotation)
+ private val screenBoundsEnd = WindowUtils.getDisplayBounds(testSpec.endRotation)
- override val transition: FlickerBuilder.(Map<String, Any?>) -> Unit
- get() = buildTransition(eachRun = false) { configuration ->
+ override val transition: FlickerBuilder.() -> Unit
+ get() = buildTransition(eachRun = false) {
setup {
test {
fixedApp.launchViaIntent(wmHelper)
}
eachRun {
- setRotation(configuration.startRotation)
+ setRotation(testSpec.startRotation)
}
}
transitions {
- setRotation(configuration.endRotation)
+ setRotation(testSpec.endRotation)
}
}
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/PipTransition.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/PipTransition.kt
index e8a61e8..93a4e1b 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/PipTransition.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/PipTransition.kt
@@ -26,15 +26,12 @@
import com.android.server.wm.flicker.dsl.FlickerBuilder
import com.android.server.wm.flicker.entireScreenCovered
import com.android.server.wm.flicker.helpers.WindowUtils
-import com.android.server.wm.flicker.helpers.isRotated
import com.android.server.wm.flicker.helpers.setRotation
import com.android.server.wm.flicker.helpers.wakeUpAndGoToHomeScreen
import com.android.server.wm.flicker.navBarLayerIsVisible
import com.android.server.wm.flicker.navBarLayerRotatesAndScales
import com.android.server.wm.flicker.navBarWindowIsVisible
-import com.android.server.wm.flicker.repetitions
import com.android.server.wm.flicker.rules.RemoveAllTasksButHomeRule.Companion.removeAllTasksButHome
-import com.android.server.wm.flicker.startRotation
import com.android.server.wm.flicker.statusBarLayerIsVisible
import com.android.server.wm.flicker.statusBarLayerRotatesScales
import com.android.server.wm.flicker.statusBarWindowIsVisible
@@ -44,11 +41,10 @@
abstract class PipTransition(protected val testSpec: FlickerTestParameter) {
protected val instrumentation: Instrumentation = InstrumentationRegistry.getInstrumentation()
- protected val isRotated = testSpec.config.startRotation.isRotated()
protected val pipApp = PipAppHelper(instrumentation)
- protected val displayBounds = WindowUtils.getDisplayBounds(testSpec.config.startRotation)
+ protected val displayBounds = WindowUtils.getDisplayBounds(testSpec.startRotation)
protected val broadcastActionTrigger = BroadcastActionTrigger(instrumentation)
- protected abstract val transition: FlickerBuilder.(Map<String, Any?>) -> Unit
+ protected abstract val transition: FlickerBuilder.() -> Unit
// Helper class to process test actions by broadcast.
protected class BroadcastActionTrigger(private val instrumentation: Instrumentation) {
private fun createIntentWithAction(broadcastAction: String): Intent {
@@ -81,16 +77,14 @@
@FlickerBuilderProvider
fun buildFlicker(): FlickerBuilder {
return FlickerBuilder(instrumentation).apply {
- withTestName { testSpec.name }
- repeat { testSpec.config.repetitions }
- transition(this, testSpec.config)
+ transition(this)
}
}
/**
* Gets a configuration that handles basic setup and teardown of pip tests
*/
- protected val setupAndTeardown: FlickerBuilder.(Map<String, Any?>) -> Unit
+ protected val setupAndTeardown: FlickerBuilder.() -> Unit
get() = {
setup {
test {
@@ -121,10 +115,10 @@
protected open fun buildTransition(
eachRun: Boolean,
stringExtras: Map<String, String> = mapOf(Components.PipActivity.EXTRA_ENTER_PIP to "true"),
- extraSpec: FlickerBuilder.(Map<String, Any?>) -> Unit = {}
- ): FlickerBuilder.(Map<String, Any?>) -> Unit {
- return { configuration ->
- setupAndTeardown(this, configuration)
+ extraSpec: FlickerBuilder.() -> Unit = {}
+ ): FlickerBuilder.() -> Unit {
+ return {
+ setupAndTeardown(this)
setup {
test {
@@ -155,7 +149,7 @@
}
}
- extraSpec(this, configuration)
+ extraSpec(this)
}
}
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/SetRequestedOrientationWhilePinnedTest.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/SetRequestedOrientationWhilePinnedTest.kt
index d6dbc36..f8e2d38 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/SetRequestedOrientationWhilePinnedTest.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/SetRequestedOrientationWhilePinnedTest.kt
@@ -50,9 +50,9 @@
private val startingBounds = WindowUtils.getDisplayBounds(Surface.ROTATION_0)
private val endingBounds = WindowUtils.getDisplayBounds(Surface.ROTATION_90)
- override val transition: FlickerBuilder.(Map<String, Any?>) -> Unit
- get() = { configuration ->
- setupAndTeardown(this, configuration)
+ override val transition: FlickerBuilder.() -> Unit
+ get() = {
+ setupAndTeardown(this)
setup {
eachRun {
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/pip/PipTaskOrganizerTest.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/pip/PipTaskOrganizerTest.java
index 0172cf32..0270093 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/pip/PipTaskOrganizerTest.java
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/pip/PipTaskOrganizerTest.java
@@ -50,7 +50,6 @@
import com.android.wm.shell.common.SyncTransactionQueue;
import com.android.wm.shell.legacysplitscreen.LegacySplitScreenController;
import com.android.wm.shell.pip.phone.PhonePipMenuController;
-import com.android.wm.shell.splitscreen.SplitScreenController;
import org.junit.Before;
import org.junit.Test;
@@ -76,8 +75,7 @@
@Mock private PipTransitionController mMockPipTransitionController;
@Mock private PipSurfaceTransactionHelper mMockPipSurfaceTransactionHelper;
@Mock private PipUiEventLogger mMockPipUiEventLogger;
- @Mock private Optional<LegacySplitScreenController> mMockOptionalLegacySplitScreen;
- @Mock private Optional<SplitScreenController> mMockOptionalSplitScreen;
+ @Mock private Optional<LegacySplitScreenController> mMockOptionalSplitScreen;
@Mock private ShellTaskOrganizer mMockShellTaskOrganizer;
private TestShellExecutor mMainExecutor;
private PipBoundsState mPipBoundsState;
@@ -101,9 +99,8 @@
mMockSyncTransactionQueue, mPipTransitionState, mPipBoundsState,
mPipBoundsAlgorithm, mMockPhonePipMenuController,
mMockPipAnimationController, mMockPipSurfaceTransactionHelper,
- mMockPipTransitionController, mMockOptionalLegacySplitScreen,
- mMockOptionalSplitScreen, mMockDisplayController, mMockPipUiEventLogger,
- mMockShellTaskOrganizer, mMainExecutor));
+ mMockPipTransitionController, mMockOptionalSplitScreen, mMockDisplayController,
+ mMockPipUiEventLogger, mMockShellTaskOrganizer, mMainExecutor));
mMainExecutor.flushAll();
preparePipTaskOrg();
}
diff --git a/libs/androidfw/OWNERS b/libs/androidfw/OWNERS
index 610fd80..17f5164 100644
--- a/libs/androidfw/OWNERS
+++ b/libs/androidfw/OWNERS
@@ -1,6 +1,6 @@
set noparent
toddke@google.com
-rtmitchell@google.com
+zyy@google.com
patb@google.com
per-file CursorWindow.cpp=omakoto@google.com
diff --git a/libs/androidfw/ResourceTypes.cpp b/libs/androidfw/ResourceTypes.cpp
index cae2d0b..5e8a623 100644
--- a/libs/androidfw/ResourceTypes.cpp
+++ b/libs/androidfw/ResourceTypes.cpp
@@ -2677,30 +2677,27 @@
// DENSITY_ANY is now dealt with. We should look to
// pick a density bucket and potentially scale it.
// Any density is potentially useful
- // because the system will scale it. Scaling down
- // is generally better than scaling up.
+ // because the system will scale it. Always prefer
+ // scaling down.
int h = thisDensity;
int l = otherDensity;
bool bImBigger = true;
if (l > h) {
- int t = h;
- h = l;
- l = t;
+ std::swap(l, h);
bImBigger = false;
}
- if (requestedDensity >= h) {
- // requested value higher than both l and h, give h
+ if (h == requestedDensity) {
+ // This handles the case where l == h == requestedDensity.
+ // In that case, this and o are equally good so both
+ // true and false are valid. This preserves previous
+ // behavior.
return bImBigger;
- }
- if (l >= requestedDensity) {
+ } else if (l >= requestedDensity) {
// requested value lower than both l and h, give l
return !bImBigger;
- }
- // saying that scaling down is 2x better than up
- if (((2 * l) - requestedDensity) * h > requestedDensity * requestedDensity) {
- return !bImBigger;
} else {
+ // otherwise give h
return bImBigger;
}
}
diff --git a/libs/androidfw/tests/Config_test.cpp b/libs/androidfw/tests/Config_test.cpp
index b54915f..698c36f 100644
--- a/libs/androidfw/tests/Config_test.cpp
+++ b/libs/androidfw/tests/Config_test.cpp
@@ -75,6 +75,9 @@
configs.add(buildDensityConfig(int(ResTable_config::DENSITY_HIGH) + 20));
ASSERT_EQ(expectedBest, selectBest(deviceConfig, configs));
+ configs.add(buildDensityConfig(int(ResTable_config::DENSITY_XHIGH) - 1));
+ ASSERT_EQ(expectedBest, selectBest(deviceConfig, configs));
+
expectedBest = buildDensityConfig(ResTable_config::DENSITY_XHIGH);
configs.add(expectedBest);
ASSERT_EQ(expectedBest, selectBest(deviceConfig, configs));
diff --git a/media/java/android/media/MediaCodec.java b/media/java/android/media/MediaCodec.java
index 85baa3d..4daedfc 100644
--- a/media/java/android/media/MediaCodec.java
+++ b/media/java/android/media/MediaCodec.java
@@ -60,10 +60,10 @@
with {@link MediaExtractor}, {@link MediaSync}, {@link MediaMuxer}, {@link MediaCrypto},
{@link MediaDrm}, {@link Image}, {@link Surface}, and {@link AudioTrack}.)
<p>
- <center><object style="width: 540px; height: 205px;" type="image/svg+xml"
- data="../../../images/media/mediacodec_buffers.svg"><img
- src="../../../images/media/mediacodec_buffers.png" style="width: 540px; height: 205px"
- alt="MediaCodec buffer flow diagram"></object></center>
+ <center>
+ <img src="../../../images/media/mediacodec_buffers.svg" style="width: 540px; height: 205px"
+ alt="MediaCodec buffer flow diagram">
+ </center>
<p>
In broad terms, a codec processes input data to generate output data. It processes data
asynchronously and uses a set of input and output buffers. At a simplistic level, you request
@@ -268,10 +268,10 @@
Uninitialized, Configured and Error, whereas the Executing state conceptually progresses through
three sub-states: Flushed, Running and End-of-Stream.
<p>
- <center><object style="width: 516px; height: 353px;" type="image/svg+xml"
- data="../../../images/media/mediacodec_states.svg"><img
- src="../../../images/media/mediacodec_states.png" style="width: 519px; height: 356px"
- alt="MediaCodec state diagram"></object></center>
+ <center>
+ <img src="../../../images/media/mediacodec_states.svg" style="width: 519px; height: 356px"
+ alt="MediaCodec state diagram">
+ </center>
<p>
When you create a codec using one of the factory methods, the codec is in the Uninitialized
state. First, you need to configure it via {@link #configure configure(…)}, which brings
@@ -513,10 +513,10 @@
Similarly, upon an initial call to {@code start} the codec will move directly to the Running
sub-state and start passing available input buffers via the callback.
<p>
- <center><object style="width: 516px; height: 353px;" type="image/svg+xml"
- data="../../../images/media/mediacodec_async_states.svg"><img
- src="../../../images/media/mediacodec_async_states.png" style="width: 516px; height: 353px"
- alt="MediaCodec state diagram for asynchronous operation"></object></center>
+ <center>
+ <img src="../../../images/media/mediacodec_async_states.svg" style="width: 516px; height: 353px"
+ alt="MediaCodec state diagram for asynchronous operation">
+ </center>
<p>
MediaCodec is typically used like this in asynchronous mode:
<pre class=prettyprint>
diff --git a/media/jni/tuner/DvrClient.cpp b/media/jni/tuner/DvrClient.cpp
index 052b465..3027838 100644
--- a/media/jni/tuner/DvrClient.cpp
+++ b/media/jni/tuner/DvrClient.cpp
@@ -278,8 +278,12 @@
Result DvrClient::close() {
if (mDvrMQEventFlag != nullptr) {
EventFlag::deleteEventFlag(&mDvrMQEventFlag);
+ mDvrMQEventFlag = nullptr;
}
- mDvrMQ = nullptr;
+ if (mDvrMQ != nullptr) {
+ delete mDvrMQ;
+ mDvrMQ = nullptr;
+ }
if (mTunerDvr != nullptr) {
Status s = mTunerDvr->close();
diff --git a/media/jni/tuner/FilterClient.cpp b/media/jni/tuner/FilterClient.cpp
index fe746fa..e8b3de8 100644
--- a/media/jni/tuner/FilterClient.cpp
+++ b/media/jni/tuner/FilterClient.cpp
@@ -177,11 +177,14 @@
}
Result FilterClient::close() {
- if (mFilterMQEventFlag) {
+ if (mFilterMQEventFlag != nullptr) {
EventFlag::deleteEventFlag(&mFilterMQEventFlag);
+ mFilterMQEventFlag = nullptr;
}
- mFilterMQEventFlag = nullptr;
- mFilterMQ = nullptr;
+ if (mFilterMQ != nullptr) {
+ delete mFilterMQ;
+ mFilterMQ = nullptr;
+ }
if (mTunerFilter != nullptr) {
Status s = mTunerFilter->close();
diff --git a/packages/SettingsLib/src/com/android/settingslib/SignalIcon.java b/packages/SettingsLib/src/com/android/settingslib/SignalIcon.java
index 794b0eb..280e407 100644
--- a/packages/SettingsLib/src/com/android/settingslib/SignalIcon.java
+++ b/packages/SettingsLib/src/com/android/settingslib/SignalIcon.java
@@ -15,13 +15,8 @@
*/
package com.android.settingslib;
-import com.android.settingslib.mobile.TelephonyIcons;
-
-import java.text.SimpleDateFormat;
-import java.util.Objects;
-
/**
- * Icons and states for SysUI and Settings.
+ * Icons for SysUI and Settings.
*/
public class SignalIcon {
@@ -71,92 +66,6 @@
}
/**
- * Holds states for SysUI.
- */
- public static class State {
- // No locale as it's only used for logging purposes
- private static SimpleDateFormat sSDF = new SimpleDateFormat("MM-dd HH:mm:ss.SSS");
- public boolean connected;
- public boolean enabled;
- public boolean activityIn;
- public boolean activityOut;
- public int level;
- public IconGroup iconGroup;
- public int inetCondition;
- public int rssi; // Only for logging.
-
- // Not used for comparison, just used for logging.
- public long time;
-
- /**
- * Generates a copy of the source state.
- */
- public void copyFrom(State state) {
- connected = state.connected;
- enabled = state.enabled;
- level = state.level;
- iconGroup = state.iconGroup;
- inetCondition = state.inetCondition;
- activityIn = state.activityIn;
- activityOut = state.activityOut;
- rssi = state.rssi;
- time = state.time;
- }
-
- @Override
- public String toString() {
- if (time != 0) {
- StringBuilder builder = new StringBuilder();
- toString(builder);
- return builder.toString();
- } else {
- return "Empty " + getClass().getSimpleName();
- }
- }
-
- protected void toString(StringBuilder builder) {
- builder.append("connected=").append(connected).append(',')
- .append("enabled=").append(enabled).append(',')
- .append("level=").append(level).append(',')
- .append("inetCondition=").append(inetCondition).append(',')
- .append("iconGroup=").append(iconGroup).append(',')
- .append("activityIn=").append(activityIn).append(',')
- .append("activityOut=").append(activityOut).append(',')
- .append("rssi=").append(rssi).append(',')
- .append("lastModified=").append(sSDF.format(time));
- }
-
- @Override
- public boolean equals(Object o) {
- if (!o.getClass().equals(getClass())) {
- return false;
- }
- State other = (State) o;
- return other.connected == connected
- && other.enabled == enabled
- && other.level == level
- && other.inetCondition == inetCondition
- && other.iconGroup == iconGroup
- && other.activityIn == activityIn
- && other.activityOut == activityOut
- && other.rssi == rssi;
- }
-
- @Override
- public int hashCode() {
- return Objects.hash(
- connected,
- enabled,
- level,
- inetCondition,
- iconGroup,
- activityIn,
- activityOut,
- rssi);
- }
- }
-
- /**
* Holds icons for a given MobileState.
*/
public static class MobileIconGroup extends IconGroup {
@@ -189,110 +98,4 @@
this.dataType = dataType;
}
}
-
- /**
- * Holds mobile states for SysUI.
- */
- public static class MobileState extends State {
- public String networkName;
- public String networkNameData;
- public boolean dataSim;
- public boolean dataConnected;
- public boolean isEmergency;
- public boolean airplaneMode;
- public boolean carrierNetworkChangeMode;
- public boolean isDefault;
- public boolean userSetup;
- public boolean roaming;
- public boolean defaultDataOff; // Tracks the on/off state of the defaultDataSubscription
-
- @Override
- public void copyFrom(State s) {
- super.copyFrom(s);
- MobileState state = (MobileState) s;
- dataSim = state.dataSim;
- networkName = state.networkName;
- networkNameData = state.networkNameData;
- dataConnected = state.dataConnected;
- isDefault = state.isDefault;
- isEmergency = state.isEmergency;
- airplaneMode = state.airplaneMode;
- carrierNetworkChangeMode = state.carrierNetworkChangeMode;
- userSetup = state.userSetup;
- roaming = state.roaming;
- defaultDataOff = state.defaultDataOff;
- }
-
- /** @return true if this state is disabled or not default data */
- public boolean isDataDisabledOrNotDefault() {
- return (iconGroup == TelephonyIcons.DATA_DISABLED
- || (iconGroup == TelephonyIcons.NOT_DEFAULT_DATA)) && userSetup;
- }
-
- /** @return if this state is considered to have inbound activity */
- public boolean hasActivityIn() {
- return dataConnected && !carrierNetworkChangeMode && activityIn;
- }
-
- /** @return if this state is considered to have outbound activity */
- public boolean hasActivityOut() {
- return dataConnected && !carrierNetworkChangeMode && activityOut;
- }
-
- /** @return true if this state should show a RAT icon in quick settings */
- public boolean showQuickSettingsRatIcon() {
- return dataConnected || isDataDisabledOrNotDefault();
- }
-
- @Override
- protected void toString(StringBuilder builder) {
- super.toString(builder);
- builder.append(',');
- builder.append("dataSim=").append(dataSim).append(',');
- builder.append("networkName=").append(networkName).append(',');
- builder.append("networkNameData=").append(networkNameData).append(',');
- builder.append("dataConnected=").append(dataConnected).append(',');
- builder.append("roaming=").append(roaming).append(',');
- builder.append("isDefault=").append(isDefault).append(',');
- builder.append("isEmergency=").append(isEmergency).append(',');
- builder.append("airplaneMode=").append(airplaneMode).append(',');
- builder.append("carrierNetworkChangeMode=").append(carrierNetworkChangeMode)
- .append(',');
- builder.append("userSetup=").append(userSetup).append(',');
- builder.append("defaultDataOff=").append(defaultDataOff).append(',');
- builder.append("showQuickSettingsRatIcon=").append(showQuickSettingsRatIcon());
- }
-
- @Override
- public boolean equals(Object o) {
- return super.equals(o)
- && Objects.equals(((MobileState) o).networkName, networkName)
- && Objects.equals(((MobileState) o).networkNameData, networkNameData)
- && ((MobileState) o).dataSim == dataSim
- && ((MobileState) o).dataConnected == dataConnected
- && ((MobileState) o).isEmergency == isEmergency
- && ((MobileState) o).airplaneMode == airplaneMode
- && ((MobileState) o).carrierNetworkChangeMode == carrierNetworkChangeMode
- && ((MobileState) o).userSetup == userSetup
- && ((MobileState) o).isDefault == isDefault
- && ((MobileState) o).roaming == roaming
- && ((MobileState) o).defaultDataOff == defaultDataOff;
- }
-
- @Override
- public int hashCode() {
- return Objects.hash(super.hashCode(),
- networkName,
- networkNameData,
- dataSim,
- dataConnected,
- isEmergency,
- airplaneMode,
- carrierNetworkChangeMode,
- userSetup,
- isDefault,
- roaming,
- defaultDataOff);
- }
- }
}
diff --git a/packages/SettingsLib/src/com/android/settingslib/core/instrumentation/MetricsFeatureProvider.java b/packages/SettingsLib/src/com/android/settingslib/core/instrumentation/MetricsFeatureProvider.java
index 6cb60d1..7390b6a 100644
--- a/packages/SettingsLib/src/com/android/settingslib/core/instrumentation/MetricsFeatureProvider.java
+++ b/packages/SettingsLib/src/com/android/settingslib/core/instrumentation/MetricsFeatureProvider.java
@@ -97,6 +97,9 @@
/**
* Logs a simple action without page id or attribution
+ *
+ * @param category the target page
+ * @param taggedData the data for {@link EventLogWriter}
*/
public void action(Context context, int category, Pair<Integer, Object>... taggedData) {
for (LogWriter writer : mLoggerWriters) {
diff --git a/packages/SettingsProvider/src/android/provider/settings/validators/GlobalSettingsValidators.java b/packages/SettingsProvider/src/android/provider/settings/validators/GlobalSettingsValidators.java
index 93f900d..2bd5bdc 100644
--- a/packages/SettingsProvider/src/android/provider/settings/validators/GlobalSettingsValidators.java
+++ b/packages/SettingsProvider/src/android/provider/settings/validators/GlobalSettingsValidators.java
@@ -257,14 +257,6 @@
VALIDATORS.put(Global.Wearable.SYSTEM_CAPABILITIES, ANY_INTEGER_VALIDATOR);
VALIDATORS.put(Global.Wearable.SYSTEM_EDITION, ANY_INTEGER_VALIDATOR);
VALIDATORS.put(Global.Wearable.WEAR_PLATFORM_MR_NUMBER, ANY_INTEGER_VALIDATOR);
- VALIDATORS.put(Global.Wearable.BOTTOM_OFFSET, ANY_INTEGER_VALIDATOR);
- VALIDATORS.put(
- Global.Wearable.DISPLAY_SHAPE,
- new DiscreteValueValidator(
- new String[] {
- String.valueOf(Global.Wearable.DISPLAY_SHAPE_ROUND),
- String.valueOf(Global.Wearable.DISPLAY_SHAPE_SQUARE)
- }));
VALIDATORS.put(Global.Wearable.MOBILE_SIGNAL_DETECTOR, BOOLEAN_VALIDATOR);
VALIDATORS.put(Global.Wearable.AMBIENT_ENABLED, BOOLEAN_VALIDATOR);
VALIDATORS.put(Global.Wearable.AMBIENT_TILT_TO_WAKE, BOOLEAN_VALIDATOR);
@@ -299,7 +291,6 @@
String.valueOf(Global.Wearable.HFP_CLIENT_ENABLED),
String.valueOf(Global.Wearable.HFP_CLIENT_DISABLED)
}));
- VALIDATORS.put(Global.Wearable.HFP_CLIENT_PROFILE_ENABLED, BOOLEAN_VALIDATOR);
VALIDATORS.put(Global.Wearable.COMPANION_OS_VERSION, ANY_INTEGER_VALIDATOR);
VALIDATORS.put(Global.Wearable.ENABLE_ALL_LANGUAGES, BOOLEAN_VALIDATOR);
VALIDATORS.put(Global.Wearable.OEM_SETUP_VERSION, ANY_INTEGER_VALIDATOR);
diff --git a/packages/SettingsProvider/src/com/android/providers/settings/SettingsProvider.java b/packages/SettingsProvider/src/com/android/providers/settings/SettingsProvider.java
index ea46ef1..cd4047b 100644
--- a/packages/SettingsProvider/src/com/android/providers/settings/SettingsProvider.java
+++ b/packages/SettingsProvider/src/com/android/providers/settings/SettingsProvider.java
@@ -5294,11 +5294,6 @@
Global.Wearable.WEAR_PLATFORM_MR_NUMBER,
SystemProperties.getInt("ro.cw_build.platform_mr", 0));
initGlobalSettingsDefaultValForWearLocked(
- Settings.Global.Wearable.BOTTOM_OFFSET, 0);
- initGlobalSettingsDefaultValForWearLocked(
- Settings.Global.Wearable.DISPLAY_SHAPE,
- Settings.Global.Wearable.DISPLAY_SHAPE_SQUARE);
- initGlobalSettingsDefaultValForWearLocked(
Settings.Global.Wearable.SCREEN_BRIGHTNESS_LEVEL,
getContext()
.getResources()
@@ -5345,8 +5340,6 @@
Settings.Global.Wearable.AMBIENT_PLUGGED_TIMEOUT_MIN,
SystemProperties.getInt("ro.ambient.plugged_timeout_min", -1));
initGlobalSettingsDefaultValForWearLocked(
- Settings.Global.Wearable.COMPANION_ADDRESS, "");
- initGlobalSettingsDefaultValForWearLocked(
Settings.Global.Wearable.PAIRED_DEVICE_OS_TYPE,
Settings.Global.Wearable.PAIRED_DEVICE_OS_TYPE_UNKNOWN);
initGlobalSettingsDefaultValForWearLocked(
@@ -5359,12 +5352,6 @@
disabledProfileSetting.isNull()
? 0
: Long.parseLong(disabledProfileSetting.getValue());
- final boolean isHfpClientProfileEnabled =
- (disabledProfileSettingValue & (1 << BluetoothProfile.HEADSET_CLIENT))
- == 0;
- initGlobalSettingsDefaultValForWearLocked(
- Settings.Global.Wearable.HFP_CLIENT_PROFILE_ENABLED,
- isHfpClientProfileEnabled);
initGlobalSettingsDefaultValForWearLocked(
Settings.Global.Wearable.COMPANION_OS_VERSION,
Settings.Global.Wearable.COMPANION_OS_VERSION_UNDEFINED);
diff --git a/packages/SettingsProvider/test/src/android/provider/SettingsBackupTest.java b/packages/SettingsProvider/test/src/android/provider/SettingsBackupTest.java
index 82012d9..c05e01d 100644
--- a/packages/SettingsProvider/test/src/android/provider/SettingsBackupTest.java
+++ b/packages/SettingsProvider/test/src/android/provider/SettingsBackupTest.java
@@ -632,9 +632,6 @@
Settings.Global.Wearable.SYSTEM_CAPABILITIES,
Settings.Global.Wearable.SYSTEM_EDITION,
Settings.Global.Wearable.WEAR_PLATFORM_MR_NUMBER,
- Settings.Global.Wearable.COMPANION_BT_ADDRESS_DUAL,
- Settings.Global.Wearable.DISPLAY_SHAPE,
- Settings.Global.Wearable.BOTTOM_OFFSET,
Settings.Global.Wearable.SCREEN_BRIGHTNESS_LEVEL,
Settings.Global.Wearable.MOBILE_SIGNAL_DETECTOR,
Settings.Global.Wearable.AMBIENT_ENABLED,
@@ -647,12 +644,10 @@
Settings.Global.Wearable.AMBIENT_GESTURE_SENSOR_ID,
Settings.Global.Wearable.AMBIENT_LOW_BIT_ENABLED,
Settings.Global.Wearable.AMBIENT_PLUGGED_TIMEOUT_MIN,
- Settings.Global.Wearable.COMPANION_ADDRESS,
Settings.Global.Wearable.PAIRED_DEVICE_OS_TYPE,
Settings.Global.Wearable.COMPANION_BLE_ROLE,
Settings.Global.Wearable.COMPANION_NAME,
Settings.Global.Wearable.USER_HFP_CLIENT_SETTING,
- Settings.Global.Wearable.HFP_CLIENT_PROFILE_ENABLED,
Settings.Global.Wearable.COMPANION_OS_VERSION,
Settings.Global.Wearable.ENABLE_ALL_LANGUAGES,
Settings.Global.Wearable.SETUP_LOCALE,
diff --git a/packages/Shell/AndroidManifest.xml b/packages/Shell/AndroidManifest.xml
index b620654..f59f099 100644
--- a/packages/Shell/AndroidManifest.xml
+++ b/packages/Shell/AndroidManifest.xml
@@ -470,8 +470,7 @@
<uses-permission android:name="android.permission.MANAGE_TIME_AND_ZONE_DETECTION" />
<uses-permission android:name="android.permission.SUGGEST_EXTERNAL_TIME" />
- <!-- Permissions needed for testing locale manager service -->
- <!-- todo(b/201957547): Add CTS test name when available-->
+ <!-- Permissions needed for CTS test - CtsLocaleManagerTestCases -->
<uses-permission android:name="android.permission.READ_APP_SPECIFIC_LOCALES" />
<!-- Permission required for CTS test - android.server.biometrics -->
diff --git a/packages/SystemUI/Android.bp b/packages/SystemUI/Android.bp
index 6b91d64..2f117fc 100644
--- a/packages/SystemUI/Android.bp
+++ b/packages/SystemUI/Android.bp
@@ -50,17 +50,6 @@
srcs: ["src/com/android/systemui/EventLogTags.logtags"],
}
-java_library {
- name: "SystemUI-flags",
- srcs: [
- "src/com/android/systemui/flags/Flags.java",
- ],
- libs: [
- "SystemUI-flag-types",
- ],
- static_kotlin_stdlib: false,
-}
-
filegroup {
name: "ReleaseJavaFiles",
srcs: [
@@ -127,7 +116,6 @@
"iconloader_base",
"SystemUI-tags",
"SystemUI-proto",
- "SystemUI-flags",
"monet",
"dagger2",
"jsr330",
diff --git a/packages/SystemUI/AndroidManifest.xml b/packages/SystemUI/AndroidManifest.xml
index 26a4962..e1e9420 100644
--- a/packages/SystemUI/AndroidManifest.xml
+++ b/packages/SystemUI/AndroidManifest.xml
@@ -366,10 +366,6 @@
android:exported="false"
android:finishOnTaskLaunch="true" />
- <activity android:name=".screenrecord.ScreenRecordDialog"
- android:theme="@style/ScreenRecord"
- android:showForAllUsers="true"
- android:excludeFromRecents="true" />
<service android:name=".screenrecord.RecordingService" />
<receiver android:name=".SysuiRestartReceiver"
diff --git a/packages/SystemUI/OWNERS b/packages/SystemUI/OWNERS
index 1cf14f2..ce23a8b 100644
--- a/packages/SystemUI/OWNERS
+++ b/packages/SystemUI/OWNERS
@@ -22,6 +22,7 @@
hyunyoungs@google.com
jaggies@google.com
jamesoleary@google.com
+jbolinger@google.com
jdemeulenaere@google.com
jeffdq@google.com
jjaggi@google.com
diff --git a/packages/SystemUI/animation/src/com/android/systemui/animation/DialogLaunchAnimator.kt b/packages/SystemUI/animation/src/com/android/systemui/animation/DialogLaunchAnimator.kt
index 865f96b..faa7554 100644
--- a/packages/SystemUI/animation/src/com/android/systemui/animation/DialogLaunchAnimator.kt
+++ b/packages/SystemUI/animation/src/com/android/systemui/animation/DialogLaunchAnimator.kt
@@ -16,11 +16,16 @@
package com.android.systemui.animation
+import android.animation.Animator
+import android.animation.AnimatorListenerAdapter
+import android.animation.ValueAnimator
import android.app.Dialog
import android.content.Context
import android.graphics.Color
+import android.graphics.Rect
import android.os.Looper
import android.util.Log
+import android.util.MathUtils
import android.view.GhostView
import android.view.Gravity
import android.view.View
@@ -32,6 +37,7 @@
import android.view.WindowManager.LayoutParams.FLAG_LAYOUT_IN_SCREEN
import android.view.WindowManagerPolicyConstants
import android.widget.FrameLayout
+import kotlin.math.roundToInt
private const val TAG = "DialogLaunchAnimator"
@@ -52,7 +58,8 @@
private val currentAnimations = hashSetOf<DialogLaunchAnimation>()
/**
- * Show [dialog] by expanding it from [view].
+ * Show [dialog] by expanding it from [view]. If [animateBackgroundBoundsChange] is true, then
+ * the background of the dialog will be animated when the dialog bounds change.
*
* Caveats: When calling this function, the dialog content view will actually be stolen and
* attached to a different dialog (and thus a different window) which means that the actual
@@ -60,7 +67,12 @@
* must call dismiss(), hide() and show() on the [Dialog] returned by this function to actually
* dismiss, hide or show the dialog.
*/
- fun showFromView(dialog: Dialog, view: View): Dialog {
+ @JvmOverloads
+ fun showFromView(
+ dialog: Dialog,
+ view: View,
+ animateBackgroundBoundsChange: Boolean = false
+ ): Dialog {
if (Looper.myLooper() != Looper.getMainLooper()) {
throw IllegalStateException(
"showFromView must be called from the main thread and dialog must be created in " +
@@ -78,7 +90,8 @@
val launchAnimation = DialogLaunchAnimation(
context, launchAnimator, hostDialogProvider, view,
- onDialogDismissed = { currentAnimations.remove(it) }, originalDialog = dialog)
+ onDialogDismissed = { currentAnimations.remove(it) }, originalDialog = dialog,
+ animateBackgroundBoundsChange)
val hostDialog = launchAnimation.hostDialog
currentAnimations.add(launchAnimation)
@@ -208,7 +221,10 @@
private val onDialogDismissed: (DialogLaunchAnimation) -> Unit,
/** The original dialog whose content will be shown and animate in/out in [hostDialog]. */
- private val originalDialog: Dialog
+ private val originalDialog: Dialog,
+
+ /** Whether we should animate the dialog background when its bounds change. */
+ private val animateBackgroundBoundsChange: Boolean
) {
/**
* The fullscreen dialog to which we will add the content view [originalDialogView] of
@@ -221,10 +237,11 @@
private val hostDialogRoot = FrameLayout(context)
/**
- * The content view of [originalDialog], which will be stolen from that dialog and added to
- * [hostDialogRoot].
+ * The parent of the original dialog content view, that serves as a fake window that will have
+ * the same size as the original dialog window and to which we will set the original dialog
+ * window background.
*/
- private var originalDialogView: View? = null
+ private val dialogContentParent = FrameLayout(context)
/**
* The background color of [originalDialogView], taking into consideration the [originalDialog]
@@ -246,6 +263,11 @@
private var isTouchSurfaceGhostDrawn = false
private var isOriginalDialogViewLaidOut = false
+ private var backgroundLayoutListener = if (animateBackgroundBoundsChange) {
+ AnimatedBoundsLayoutListener()
+ } else {
+ null
+ }
fun start() {
// Show the host (fullscreen) dialog, to which we will add the stolen dialog view.
@@ -374,9 +396,6 @@
}
private fun showDialogFromView(dialogView: View) {
- // Save the dialog view for later as we will need it for the close animation.
- this.originalDialogView = dialogView
-
// Close the dialog when clicking outside of it.
hostDialogRoot.setOnClickListener { hostDialog.dismiss() }
dialogView.isClickable = true
@@ -394,17 +413,13 @@
throw IllegalStateException("Dialogs with no backgrounds on window are not supported")
}
- dialogView.setBackgroundResource(backgroundRes)
- originalDialogBackgroundColor =
- GhostedViewLaunchAnimatorController.findGradientDrawable(dialogView.background!!)
- ?.color
- ?.defaultColor ?: Color.BLACK
-
- // Add the dialog view to the host (fullscreen) dialog and make it invisible to make sure
- // it's not drawn yet.
- (dialogView.parent as? ViewGroup)?.removeView(dialogView)
+ // Add a parent view to the original dialog view to which we will set the original dialog
+ // window background. This View serves as a fake window with background, so that we are sure
+ // that we don't override the dialog view paddings with the window background that usually
+ // has insets.
+ dialogContentParent.setBackgroundResource(backgroundRes)
hostDialogRoot.addView(
- dialogView,
+ dialogContentParent,
// We give it the size of its original dialog window.
FrameLayout.LayoutParams(
@@ -413,10 +428,31 @@
Gravity.CENTER
)
)
- dialogView.visibility = View.INVISIBLE
+
+ // Make the dialog view parent invisible for now, to make sure it's not drawn yet.
+ dialogContentParent.visibility = View.INVISIBLE
+
+ val background = dialogContentParent.background!!
+ originalDialogBackgroundColor =
+ GhostedViewLaunchAnimatorController.findGradientDrawable(background)
+ ?.color
+ ?.defaultColor ?: Color.BLACK
+
+ // Add the dialog view to its parent (that has the original window background).
+ (dialogView.parent as? ViewGroup)?.removeView(dialogView)
+ dialogContentParent.addView(
+ dialogView,
+
+ // It should match its parent size, which is sized the same as the original dialog
+ // window.
+ FrameLayout.LayoutParams(
+ ViewGroup.LayoutParams.MATCH_PARENT,
+ ViewGroup.LayoutParams.MATCH_PARENT
+ )
+ )
// Start the animation when the dialog is laid out in the center of the host dialog.
- dialogView.addOnLayoutChangeListener(object : View.OnLayoutChangeListener {
+ dialogContentParent.addOnLayoutChangeListener(object : View.OnLayoutChangeListener {
override fun onLayoutChange(
view: View,
left: Int,
@@ -428,7 +464,7 @@
oldRight: Int,
oldBottom: Int
) {
- dialogView.removeOnLayoutChangeListener(this)
+ dialogContentParent.removeOnLayoutChangeListener(this)
isOriginalDialogViewLaidOut = true
maybeStartLaunchAnimation()
@@ -479,6 +515,13 @@
if (dismissRequested) {
hostDialog.dismiss()
}
+
+ // If necessary, we animate the dialog background when its bounds change. We do it
+ // at the end of the launch animation, because the lauch animation already correctly
+ // handles bounds changes.
+ if (backgroundLayoutListener != null) {
+ dialogContentParent.addOnLayoutChangeListener(backgroundLayoutListener)
+ }
}
)
}
@@ -548,7 +591,11 @@
}
touchSurface.visibility = View.VISIBLE
- originalDialogView!!.visibility = View.INVISIBLE
+ dialogContentParent.visibility = View.INVISIBLE
+
+ if (backgroundLayoutListener != null) {
+ dialogContentParent.removeOnLayoutChangeListener(backgroundLayoutListener)
+ }
// The animated ghost was just removed. We create a temporary ghost that will be
// removed only once we draw the touch surface, to avoid flickering that would
@@ -578,12 +625,10 @@
onLaunchAnimationStart: () -> Unit = {},
onLaunchAnimationEnd: () -> Unit = {}
) {
- val dialogView = this.originalDialogView!!
-
// Create 2 ghost controllers to animate both the dialog and the touch surface in the host
// dialog.
- val startView = if (isLaunching) touchSurface else dialogView
- val endView = if (isLaunching) dialogView else touchSurface
+ val startView = if (isLaunching) touchSurface else dialogContentParent
+ val endView = if (isLaunching) dialogContentParent else touchSurface
val startViewController = GhostedViewLaunchAnimatorController(startView)
val endViewController = GhostedViewLaunchAnimatorController(endView)
startViewController.launchContainer = hostDialogRoot
@@ -662,4 +707,81 @@
return (touchSurface.parent as? View)?.isShown ?: true
}
+
+ /** A layout listener to animate the change of bounds of the dialog background. */
+ class AnimatedBoundsLayoutListener : View.OnLayoutChangeListener {
+ companion object {
+ private const val ANIMATION_DURATION = 500L
+ }
+
+ private var lastBounds: Rect? = null
+ private var currentAnimator: ValueAnimator? = null
+
+ override fun onLayoutChange(
+ view: View,
+ left: Int,
+ top: Int,
+ right: Int,
+ bottom: Int,
+ oldLeft: Int,
+ oldTop: Int,
+ oldRight: Int,
+ oldBottom: Int
+ ) {
+ // Don't animate if bounds didn't actually change.
+ if (left == oldLeft && top == oldTop && right == oldRight && bottom == oldBottom) {
+ // Make sure that we that the last bounds set by the animator were not overridden.
+ lastBounds?.let { bounds ->
+ view.left = bounds.left
+ view.top = bounds.top
+ view.right = bounds.right
+ view.bottom = bounds.bottom
+ }
+ return
+ }
+
+ if (lastBounds == null) {
+ lastBounds = Rect(oldLeft, oldTop, oldRight, oldBottom)
+ }
+
+ val bounds = lastBounds!!
+ val startLeft = bounds.left
+ val startTop = bounds.top
+ val startRight = bounds.right
+ val startBottom = bounds.bottom
+
+ currentAnimator?.cancel()
+ currentAnimator = null
+
+ val animator = ValueAnimator.ofFloat(0f, 1f).apply {
+ duration = ANIMATION_DURATION
+ interpolator = Interpolators.STANDARD
+
+ addListener(object : AnimatorListenerAdapter() {
+ override fun onAnimationEnd(animation: Animator) {
+ currentAnimator = null
+ }
+ })
+
+ addUpdateListener { animatedValue ->
+ val progress = animatedValue.animatedFraction
+
+ // Compute new bounds.
+ bounds.left = MathUtils.lerp(startLeft, left, progress).roundToInt()
+ bounds.top = MathUtils.lerp(startTop, top, progress).roundToInt()
+ bounds.right = MathUtils.lerp(startRight, right, progress).roundToInt()
+ bounds.bottom = MathUtils.lerp(startBottom, bottom, progress).roundToInt()
+
+ // Set the new bounds.
+ view.left = bounds.left
+ view.top = bounds.top
+ view.right = bounds.right
+ view.bottom = bounds.bottom
+ }
+ }
+
+ currentAnimator = animator
+ animator.start()
+ }
+ }
}
diff --git a/packages/SystemUI/res/drawable/stat_sys_managed_profile_status.xml b/packages/SystemUI/res/drawable/stat_sys_managed_profile_status.xml
index c415ecd..88914ded 100644
--- a/packages/SystemUI/res/drawable/stat_sys_managed_profile_status.xml
+++ b/packages/SystemUI/res/drawable/stat_sys_managed_profile_status.xml
@@ -20,5 +20,5 @@
android:viewportHeight="24">
<path
android:fillColor="@android:color/white"
- android:pathData="M20,6h-4V4c0-1.1-0.9-2-2-2h-4C8.9,2,8,2.9,8,4v2H4C2.9,6,2,6.9,2,8l0,11c0,1.1,0.9,2,2,2h16c1.1,0,2-0.9,2-2V8 C22,6.9,21.1,6,20,6z M10,4h4v2h-4V4z M20,19H4V8h16V19z" />
+ android:pathData="@*android:string/config_work_badge_path_24" />
</vector>
\ No newline at end of file
diff --git a/packages/SystemUI/res/layout/qs_user_dialog_content.xml b/packages/SystemUI/res/layout/qs_user_dialog_content.xml
index 543b7d7..9495ee6 100644
--- a/packages/SystemUI/res/layout/qs_user_dialog_content.xml
+++ b/packages/SystemUI/res/layout/qs_user_dialog_content.xml
@@ -16,78 +16,74 @@
~ limitations under the License.
-->
-<FrameLayout
+<androidx.constraintlayout.widget.ConstraintLayout
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:sysui="http://schemas.android.com/apk/res-auto"
android:layout_width="match_parent"
- android:layout_height="wrap_content">
- <androidx.constraintlayout.widget.ConstraintLayout
- android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:padding="24dp"
+ android:layout_marginStart="16dp"
+ android:layout_marginEnd="16dp"
+>
+ <TextView
+ android:id="@+id/title"
android:layout_height="wrap_content"
- android:padding="24dp"
- android:layout_marginStart="16dp"
- android:layout_marginEnd="16dp"
- >
- <TextView
- android:id="@+id/title"
- android:layout_height="wrap_content"
- android:layout_width="0dp"
- android:textAlignment="center"
- android:text="@string/qs_user_switch_dialog_title"
- android:textAppearance="@style/TextAppearance.QSDialog.Title"
- android:layout_marginBottom="32dp"
- sysui:layout_constraintTop_toTopOf="parent"
- sysui:layout_constraintStart_toStartOf="parent"
- sysui:layout_constraintEnd_toEndOf="parent"
- sysui:layout_constraintBottom_toTopOf="@id/grid"
- />
-
- <com.android.systemui.qs.PseudoGridView
- android:id="@+id/grid"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:layout_marginBottom="28dp"
- sysui:verticalSpacing="4dp"
- sysui:horizontalSpacing="4dp"
- sysui:fixedChildWidth="80dp"
- sysui:layout_constraintTop_toBottomOf="@id/title"
- sysui:layout_constraintStart_toStartOf="parent"
- sysui:layout_constraintEnd_toEndOf="parent"
- sysui:layout_constraintBottom_toTopOf="@id/barrier"
+ android:layout_width="0dp"
+ android:textAlignment="center"
+ android:text="@string/qs_user_switch_dialog_title"
+ android:textAppearance="@style/TextAppearance.QSDialog.Title"
+ android:layout_marginBottom="32dp"
+ sysui:layout_constraintTop_toTopOf="parent"
+ sysui:layout_constraintStart_toStartOf="parent"
+ sysui:layout_constraintEnd_toEndOf="parent"
+ sysui:layout_constraintBottom_toTopOf="@id/grid"
/>
- <androidx.constraintlayout.widget.Barrier
- android:id="@+id/barrier"
- android:layout_height="wrap_content"
- android:layout_width="wrap_content"
- sysui:barrierDirection="top"
- sysui:constraint_referenced_ids="settings,done"
- />
+ <com.android.systemui.qs.PseudoGridView
+ android:id="@+id/grid"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_marginBottom="28dp"
+ sysui:verticalSpacing="4dp"
+ sysui:horizontalSpacing="4dp"
+ sysui:fixedChildWidth="80dp"
+ sysui:layout_constraintTop_toBottomOf="@id/title"
+ sysui:layout_constraintStart_toStartOf="parent"
+ sysui:layout_constraintEnd_toEndOf="parent"
+ sysui:layout_constraintBottom_toTopOf="@id/barrier"
+ />
- <Button
- android:id="@+id/settings"
- android:layout_width="wrap_content"
- android:layout_height="48dp"
- android:text="@string/quick_settings_more_user_settings"
- sysui:layout_constraintTop_toBottomOf="@id/barrier"
- sysui:layout_constraintBottom_toBottomOf="parent"
- sysui:layout_constraintStart_toStartOf="parent"
- sysui:layout_constraintEnd_toStartOf="@id/done"
- sysui:layout_constraintHorizontal_chainStyle="spread_inside"
- style="@style/Widget.QSDialog.Button.BorderButton"
- />
+ <androidx.constraintlayout.widget.Barrier
+ android:id="@+id/barrier"
+ android:layout_height="wrap_content"
+ android:layout_width="wrap_content"
+ sysui:barrierDirection="top"
+ sysui:constraint_referenced_ids="settings,done"
+ />
- <Button
- android:id="@+id/done"
- android:layout_width="wrap_content"
- android:layout_height="48dp"
- android:text="@string/quick_settings_done"
- sysui:layout_constraintTop_toBottomOf="@id/barrier"
- sysui:layout_constraintBottom_toBottomOf="parent"
- sysui:layout_constraintStart_toEndOf="@id/settings"
- sysui:layout_constraintEnd_toEndOf="parent"
- style="@style/Widget.QSDialog.Button"
- />
+ <Button
+ android:id="@+id/settings"
+ android:layout_width="wrap_content"
+ android:layout_height="48dp"
+ android:text="@string/quick_settings_more_user_settings"
+ sysui:layout_constraintTop_toBottomOf="@id/barrier"
+ sysui:layout_constraintBottom_toBottomOf="parent"
+ sysui:layout_constraintStart_toStartOf="parent"
+ sysui:layout_constraintEnd_toStartOf="@id/done"
+ sysui:layout_constraintHorizontal_chainStyle="spread_inside"
+ style="@style/Widget.QSDialog.Button.BorderButton"
+ />
- </androidx.constraintlayout.widget.ConstraintLayout>
-</FrameLayout>
\ No newline at end of file
+ <Button
+ android:id="@+id/done"
+ android:layout_width="wrap_content"
+ android:layout_height="48dp"
+ android:text="@string/quick_settings_done"
+ sysui:layout_constraintTop_toBottomOf="@id/barrier"
+ sysui:layout_constraintBottom_toBottomOf="parent"
+ sysui:layout_constraintStart_toEndOf="@id/settings"
+ sysui:layout_constraintEnd_toEndOf="parent"
+ style="@style/Widget.QSDialog.Button"
+ />
+
+</androidx.constraintlayout.widget.ConstraintLayout>
\ No newline at end of file
diff --git a/packages/SystemUI/res/layout/screen_record_dialog.xml b/packages/SystemUI/res/layout/screen_record_dialog.xml
index c122829..e43a149 100644
--- a/packages/SystemUI/res/layout/screen_record_dialog.xml
+++ b/packages/SystemUI/res/layout/screen_record_dialog.xml
@@ -17,8 +17,7 @@
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="wrap_content"
- android:orientation="vertical"
- android:background="@drawable/rounded_bg_full">
+ android:orientation="vertical">
<!-- Scrollview is necessary to fit everything in landscape layout -->
<ScrollView
diff --git a/packages/SystemUI/res/values/styles.xml b/packages/SystemUI/res/values/styles.xml
index fbbbf9f..974eda7 100644
--- a/packages/SystemUI/res/values/styles.xml
+++ b/packages/SystemUI/res/values/styles.xml
@@ -883,6 +883,7 @@
<item name="android:lineHeight">20sp</item>
<item name="android:fontFamily">@*android:string/config_bodyFontFamily</item>
<item name="android:stateListAnimator">@null</item>
+ <item name="android:layout_marginHorizontal">4dp</item>
</style>
<style name="Widget.QSDialog.Button.BorderButton">
diff --git a/packages/SystemUI/shared/Android.bp b/packages/SystemUI/shared/Android.bp
index 2909043..d172006 100644
--- a/packages/SystemUI/shared/Android.bp
+++ b/packages/SystemUI/shared/Android.bp
@@ -45,9 +45,6 @@
":wm_shell-aidls",
":wm_shell_util-sources",
],
- libs: [
- "SystemUI-flags",
- ],
static_libs: [
"PluginCoreLib",
"androidx.dynamicanimation_dynamicanimation",
@@ -75,7 +72,6 @@
],
static_kotlin_stdlib: false,
libs: [
- "SystemUI-flags",
"androidx.concurrent_concurrent-futures",
],
static_libs: [
diff --git a/packages/SystemUI/shared/src/com/android/systemui/flags/Flag.kt b/packages/SystemUI/shared/src/com/android/systemui/flags/Flag.kt
index d9b6a34..9574101 100644
--- a/packages/SystemUI/shared/src/com/android/systemui/flags/Flag.kt
+++ b/packages/SystemUI/shared/src/com/android/systemui/flags/Flag.kt
@@ -22,15 +22,21 @@
interface Flag<T> : Parcelable {
val id: Int
val default: T
+ val resourceOverride: Int
override fun describeContents() = 0
+
+ fun hasResourceOverride(): Boolean {
+ return resourceOverride != -1
+ }
}
// Consider using the "parcelize" kotlin library.
data class BooleanFlag @JvmOverloads constructor(
override val id: Int,
- override val default: Boolean = false
+ override val default: Boolean = false,
+ override val resourceOverride: Int = -1
) : Flag<Boolean> {
companion object {
@@ -54,7 +60,8 @@
data class StringFlag @JvmOverloads constructor(
override val id: Int,
- override val default: String = ""
+ override val default: String = "",
+ override val resourceOverride: Int = -1
) : Flag<String> {
companion object {
@JvmField
@@ -77,7 +84,8 @@
data class IntFlag @JvmOverloads constructor(
override val id: Int,
- override val default: Int = 0
+ override val default: Int = 0,
+ override val resourceOverride: Int = -1
) : Flag<Int> {
companion object {
@@ -101,7 +109,8 @@
data class LongFlag @JvmOverloads constructor(
override val id: Int,
- override val default: Long = 0
+ override val default: Long = 0,
+ override val resourceOverride: Int = -1
) : Flag<Long> {
companion object {
@@ -125,7 +134,8 @@
data class FloatFlag @JvmOverloads constructor(
override val id: Int,
- override val default: Float = 0f
+ override val default: Float = 0f,
+ override val resourceOverride: Int = -1
) : Flag<Float> {
companion object {
@@ -149,7 +159,8 @@
data class DoubleFlag @JvmOverloads constructor(
override val id: Int,
- override val default: Double = 0.0
+ override val default: Double = 0.0,
+ override val resourceOverride: Int = -1
) : Flag<Double> {
companion object {
diff --git a/packages/SystemUI/src/com/android/keyguard/CarrierTextManager.java b/packages/SystemUI/src/com/android/keyguard/CarrierTextManager.java
index cfef6cb..907943a 100644
--- a/packages/SystemUI/src/com/android/keyguard/CarrierTextManager.java
+++ b/packages/SystemUI/src/com/android/keyguard/CarrierTextManager.java
@@ -261,8 +261,10 @@
mCarrierTextCallback = callback;
if (mNetworkSupported.get()) {
// Keyguard update monitor expects callbacks from main thread
- mMainExecutor.execute(() -> mKeyguardUpdateMonitor.registerCallback(mCallback));
- mWakefulnessLifecycle.addObserver(mWakefulnessObserver);
+ mMainExecutor.execute(() -> {
+ mKeyguardUpdateMonitor.registerCallback(mCallback);
+ mWakefulnessLifecycle.addObserver(mWakefulnessObserver);
+ });
mTelephonyListenerManager.addActiveDataSubscriptionIdListener(mPhoneStateListener);
} else {
// Don't listen and clear out the text when the device isn't a phone.
@@ -272,8 +274,10 @@
}
} else {
mCarrierTextCallback = null;
- mMainExecutor.execute(() -> mKeyguardUpdateMonitor.removeCallback(mCallback));
- mWakefulnessLifecycle.removeObserver(mWakefulnessObserver);
+ mMainExecutor.execute(() -> {
+ mKeyguardUpdateMonitor.removeCallback(mCallback);
+ mWakefulnessLifecycle.removeObserver(mWakefulnessObserver);
+ });
mTelephonyListenerManager.removeActiveDataSubscriptionIdListener(mPhoneStateListener);
}
}
diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardUpdateMonitor.java b/packages/SystemUI/src/com/android/keyguard/KeyguardUpdateMonitor.java
index a41a497..85bc8f7 100644
--- a/packages/SystemUI/src/com/android/keyguard/KeyguardUpdateMonitor.java
+++ b/packages/SystemUI/src/com/android/keyguard/KeyguardUpdateMonitor.java
@@ -102,7 +102,6 @@
import com.android.systemui.dagger.qualifiers.Background;
import com.android.systemui.dagger.qualifiers.Main;
import com.android.systemui.dump.DumpManager;
-import com.android.systemui.flags.FeatureFlags;
import com.android.systemui.plugins.statusbar.StatusBarStateController;
import com.android.systemui.shared.system.TaskStackChangeListener;
import com.android.systemui.shared.system.TaskStackChangeListeners;
@@ -323,7 +322,6 @@
private boolean mLockIconPressed;
private int mActiveMobileDataSubscription = SubscriptionManager.INVALID_SUBSCRIPTION_ID;
private final Executor mBackgroundExecutor;
- private int mLockScreenMode;
/**
* Short delay before restarting fingerprint authentication after a successful try. This should
@@ -1736,11 +1734,11 @@
DumpManager dumpManager,
RingerModeTracker ringerModeTracker,
@Background Executor backgroundExecutor,
+ @Main Executor mainExecutor,
StatusBarStateController statusBarStateController,
LockPatternUtils lockPatternUtils,
AuthController authController,
TelephonyListenerManager telephonyListenerManager,
- FeatureFlags featureFlags,
InteractionJankMonitor interactionJankMonitor,
LatencyTracker latencyTracker) {
mContext = context;
@@ -1966,6 +1964,17 @@
mBiometricManager.registerEnabledOnKeyguardCallback(mBiometricEnabledCallback);
}
+ // in case authenticators aren't registered yet at this point:
+ mAuthController.addCallback(new AuthController.Callback() {
+ @Override
+ public void onAllAuthenticatorsRegistered() {
+ }
+
+ @Override
+ public void onEnrollmentsChanged() {
+ mainExecutor.execute(() -> updateBiometricListeningState());
+ }
+ });
updateBiometricListeningState();
if (mFpm != null) {
mFpm.addLockoutResetCallback(mFingerprintLockoutResetCallback);
@@ -2043,7 +2052,7 @@
/**
* @return if udfps is available on this device. will return true even if the user hasn't
- * enrolled udfps.
+ * enrolled udfps. This may be false if called before onAllAuthenticatorsRegistered.
*/
public boolean isUdfpsAvailable() {
return mAuthController.getUdfpsProps() != null
@@ -2092,7 +2101,6 @@
return;
}
- // TODO: Add support for multiple fingerprint sensors, b/173730729
updateUdfpsEnrolled(getCurrentUser());
final boolean shouldListenForFingerprint = shouldListenForFingerprint(isUdfpsEnrolled());
final boolean runningOrRestarting = mFingerprintRunningState == BIOMETRIC_STATE_RUNNING
diff --git a/packages/SystemUI/src/com/android/keyguard/LockIconViewController.java b/packages/SystemUI/src/com/android/keyguard/LockIconViewController.java
index 3c80a18..8a0b5b8 100644
--- a/packages/SystemUI/src/com/android/keyguard/LockIconViewController.java
+++ b/packages/SystemUI/src/com/android/keyguard/LockIconViewController.java
@@ -695,11 +695,20 @@
private final AuthController.Callback mAuthControllerCallback = new AuthController.Callback() {
@Override
public void onAllAuthenticatorsRegistered() {
- // must be called from the main thread since it may update the views
- mExecutor.execute(() -> {
- updateIsUdfpsEnrolled();
- updateConfiguration();
- });
+ updateUdfpsConfig();
+ }
+
+ @Override
+ public void onEnrollmentsChanged() {
+ updateUdfpsConfig();
}
};
+
+ private void updateUdfpsConfig() {
+ // must be called from the main thread since it may update the views
+ mExecutor.execute(() -> {
+ updateIsUdfpsEnrolled();
+ updateConfiguration();
+ });
+ }
}
diff --git a/packages/SystemUI/src/com/android/systemui/accessibility/floatingmenu/AccessibilityFloatingMenuView.java b/packages/SystemUI/src/com/android/systemui/accessibility/floatingmenu/AccessibilityFloatingMenuView.java
index 59d9aff..d2703f5 100644
--- a/packages/SystemUI/src/com/android/systemui/accessibility/floatingmenu/AccessibilityFloatingMenuView.java
+++ b/packages/SystemUI/src/com/android/systemui/accessibility/floatingmenu/AccessibilityFloatingMenuView.java
@@ -22,6 +22,7 @@
import static android.view.WindowInsets.Type.displayCutout;
import static android.view.WindowInsets.Type.ime;
import static android.view.WindowInsets.Type.systemBars;
+import static android.view.WindowManager.LayoutParams.PRIVATE_FLAG_EXCLUDE_FROM_SCREEN_MAGNIFICATION;
import static java.util.Objects.requireNonNull;
@@ -659,6 +660,7 @@
| WindowManager.LayoutParams.FLAG_LAYOUT_NO_LIMITS,
PixelFormat.TRANSLUCENT);
params.receiveInsetsIgnoringZOrder = true;
+ params.privateFlags |= PRIVATE_FLAG_EXCLUDE_FROM_SCREEN_MAGNIFICATION;
params.windowAnimations = android.R.style.Animation_Translucent;
params.gravity = Gravity.START | Gravity.TOP;
params.x = (mAlignment == Alignment.RIGHT) ? getMaxWindowX() : getMinWindowX();
diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/AuthController.java b/packages/SystemUI/src/com/android/systemui/biometrics/AuthController.java
index 5472d6b..7215736 100644
--- a/packages/SystemUI/src/com/android/systemui/biometrics/AuthController.java
+++ b/packages/SystemUI/src/com/android/systemui/biometrics/AuthController.java
@@ -142,6 +142,10 @@
mUdfpsEnrolledForUser.put(userId, hasEnrollments);
}
}
+
+ for (Callback cb : mCallbacks) {
+ cb.onEnrollmentsChanged();
+ }
}
};
@@ -844,5 +848,11 @@
* registered before this call, this callback will never be triggered.
*/
void onAllAuthenticatorsRegistered();
+
+ /**
+ * Called when UDFPS enrollments have changed. This is called after boot and on changes to
+ * enrollment.
+ */
+ void onEnrollmentsChanged();
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/AuthRippleController.kt b/packages/SystemUI/src/com/android/systemui/biometrics/AuthRippleController.kt
index 8b04bf5..ec17d4e 100644
--- a/packages/SystemUI/src/com/android/systemui/biometrics/AuthRippleController.kt
+++ b/packages/SystemUI/src/com/android/systemui/biometrics/AuthRippleController.kt
@@ -292,10 +292,16 @@
}
}
- private val authControllerCallback = AuthController.Callback {
- updateSensorLocation()
- updateUdfpsDependentParams()
- }
+ private val authControllerCallback =
+ object : AuthController.Callback {
+ override fun onAllAuthenticatorsRegistered() {
+ updateSensorLocation()
+ updateUdfpsDependentParams()
+ }
+
+ override fun onEnrollmentsChanged() {
+ }
+ }
private fun updateUdfpsDependentParams() {
authController.udfpsProps?.let {
diff --git a/packages/SystemUI/src/com/android/systemui/dagger/DefaultActivityBinder.java b/packages/SystemUI/src/com/android/systemui/dagger/DefaultActivityBinder.java
index de8ed70..bce8784 100644
--- a/packages/SystemUI/src/com/android/systemui/dagger/DefaultActivityBinder.java
+++ b/packages/SystemUI/src/com/android/systemui/dagger/DefaultActivityBinder.java
@@ -22,7 +22,6 @@
import com.android.systemui.keyguard.WorkLockActivity;
import com.android.systemui.people.PeopleSpaceActivity;
import com.android.systemui.people.widget.LaunchConversationActivity;
-import com.android.systemui.screenrecord.ScreenRecordDialog;
import com.android.systemui.screenshot.LongScreenshotActivity;
import com.android.systemui.sensorprivacy.SensorUseStartedActivity;
import com.android.systemui.sensorprivacy.television.TvUnblockSensorActivity;
@@ -67,12 +66,6 @@
@ClassKey(BrightnessDialog.class)
public abstract Activity bindBrightnessDialog(BrightnessDialog activity);
- /** Inject into ScreenRecordDialog */
- @Binds
- @IntoMap
- @ClassKey(ScreenRecordDialog.class)
- public abstract Activity bindScreenRecordDialog(ScreenRecordDialog activity);
-
/** Inject into UsbDebuggingActivity. */
@Binds
@IntoMap
diff --git a/packages/SystemUI/src/com/android/systemui/dagger/SystemUIModule.java b/packages/SystemUI/src/com/android/systemui/dagger/SystemUIModule.java
index 11a0f6c..3c86760 100644
--- a/packages/SystemUI/src/com/android/systemui/dagger/SystemUIModule.java
+++ b/packages/SystemUI/src/com/android/systemui/dagger/SystemUIModule.java
@@ -60,6 +60,7 @@
import com.android.systemui.statusbar.notification.collection.inflation.NotificationRowBinder;
import com.android.systemui.statusbar.notification.collection.inflation.NotificationRowBinderImpl;
import com.android.systemui.statusbar.notification.collection.legacy.NotificationGroupManagerLegacy;
+import com.android.systemui.statusbar.notification.collection.render.NotificationVisibilityProvider;
import com.android.systemui.statusbar.notification.interruption.NotificationInterruptStateProvider;
import com.android.systemui.statusbar.notification.people.PeopleHubModule;
import com.android.systemui.statusbar.notification.row.dagger.ExpandableNotificationRowComponent;
@@ -206,7 +207,9 @@
NotificationShadeWindowController notificationShadeWindowController,
StatusBarStateController statusBarStateController, ShadeController shadeController,
ConfigurationController configurationController,
- @Nullable IStatusBarService statusBarService, INotificationManager notificationManager,
+ @Nullable IStatusBarService statusBarService,
+ INotificationManager notificationManager,
+ NotificationVisibilityProvider visibilityProvider,
NotificationInterruptStateProvider interruptionStateProvider,
ZenModeController zenModeController, NotificationLockscreenUserManager notifUserManager,
NotificationGroupManagerLegacy groupManager, NotificationEntryManager entryManager,
@@ -215,6 +218,7 @@
return Optional.ofNullable(BubblesManager.create(context, bubblesOptional,
notificationShadeWindowController, statusBarStateController, shadeController,
configurationController, statusBarService, notificationManager,
+ visibilityProvider,
interruptionStateProvider, zenModeController, notifUserManager,
groupManager, entryManager, notifPipeline, sysUiState, featureFlags, dumpManager,
sysuiMainExecutor));
diff --git a/packages/SystemUI/src/com/android/systemui/doze/DozeScreenState.java b/packages/SystemUI/src/com/android/systemui/doze/DozeScreenState.java
index 8f1486b..908397b 100644
--- a/packages/SystemUI/src/com/android/systemui/doze/DozeScreenState.java
+++ b/packages/SystemUI/src/com/android/systemui/doze/DozeScreenState.java
@@ -107,6 +107,11 @@
public void onAllAuthenticatorsRegistered() {
updateUdfpsController();
}
+
+ @Override
+ public void onEnrollmentsChanged() {
+ updateUdfpsController();
+ }
});
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/flags/FeatureFlagReader.java b/packages/SystemUI/src/com/android/systemui/flags/FeatureFlagReader.java
deleted file mode 100644
index c87a7e4..0000000
--- a/packages/SystemUI/src/com/android/systemui/flags/FeatureFlagReader.java
+++ /dev/null
@@ -1,175 +0,0 @@
-/*
- * 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.flags;
-
-import android.content.res.Resources;
-import android.util.SparseArray;
-
-import androidx.annotation.BoolRes;
-import androidx.annotation.NonNull;
-import androidx.annotation.Nullable;
-
-import com.android.systemui.Dumpable;
-import com.android.systemui.R;
-import com.android.systemui.dagger.SysUISingleton;
-import com.android.systemui.dagger.qualifiers.Main;
-import com.android.systemui.dump.DumpManager;
-import com.android.systemui.util.wrapper.BuildInfo;
-
-import java.io.FileDescriptor;
-import java.io.PrintWriter;
-import java.util.ArrayList;
-
-import javax.inject.Inject;
-/**
- * Reads and caches feature flags for quick access
- *
- * Feature flags must be defined as boolean resources. For example:
- *
- * {@code
- * <bool name="flag_foo_bar_baz">false</bool>
- * }
- *
- * It is strongly recommended that the name of the resource begin with "flag_".
- *
- * Flags can be overridden via adb on development builds. For example, to override the flag from the
- * previous example, do the following:
- *
- * {@code
- * $ adb shell setprop persist.systemui.flag_foo_bar_baz 1
- * }
- *
- * Note that all storage keys begin with "flag_", even if their associated resId does not.
- *
- * Calls to this class should probably be wrapped by a method in {@link FeatureFlags}.
- */
-@SysUISingleton
-public class FeatureFlagReader implements Dumpable {
- private final Resources mResources;
- private final boolean mAreFlagsOverrideable;
- private final SystemPropertiesHelper mSystemPropertiesHelper;
- private final SparseArray<CachedFlag> mCachedFlags = new SparseArray<>();
-
- private FlagReader mFlagReader;
-
- @Inject
- public FeatureFlagReader(
- @Main Resources resources,
- BuildInfo build,
- DumpManager dumpManager,
- SystemPropertiesHelper systemPropertiesHelper,
- FlagReader reader) {
- mResources = resources;
- mFlagReader = reader;
- mSystemPropertiesHelper = systemPropertiesHelper;
- mAreFlagsOverrideable =
- build.isDebuggable() && mResources.getBoolean(R.bool.are_flags_overrideable);
- dumpManager.registerDumpable("FeatureFlags", this);
- }
-
- boolean isEnabled(BooleanFlag flag) {
- return mFlagReader.isEnabled(flag.getId(), flag.getDefault());
- }
-
- void addListener(FlagReader.Listener listener) {
- mFlagReader.addListener(listener);
- }
-
- void removeListener(FlagReader.Listener listener) {
- mFlagReader.removeListener(listener);
- }
-
- /**
- * Returns true if the specified feature flag has been enabled.
- *
- * @param resId The backing boolean resource that determines the value of the flag. This value
- * can be overridden via DeviceConfig on development builds.
- */
- public boolean isEnabled(@BoolRes int resId) {
- synchronized (mCachedFlags) {
- CachedFlag cachedFlag = mCachedFlags.get(resId);
-
- if (cachedFlag == null) {
- String name = resourceIdToFlagName(resId);
- boolean value = mResources.getBoolean(resId);
- if (mAreFlagsOverrideable) {
- value = mSystemPropertiesHelper.getBoolean(flagNameToStorageKey(name), value);
- }
-
- cachedFlag = new CachedFlag(name, value);
- mCachedFlags.put(resId, cachedFlag);
- }
-
- return cachedFlag.value;
- }
- }
-
- private String resourceIdToFlagName(@BoolRes int resId) {
- String resName = mResources.getResourceEntryName(resId);
- if (resName.startsWith(RESNAME_PREFIX)) {
- resName = resName.substring(RESNAME_PREFIX.length());
- }
- return resName;
- }
-
- private String flagNameToStorageKey(String flagName) {
- if (flagName.startsWith(STORAGE_KEY_PREFIX)) {
- return flagName;
- } else {
- return STORAGE_KEY_PREFIX + flagName;
- }
- }
-
- @Nullable
- private String storageKeyToFlagName(String configName) {
- if (configName.startsWith(STORAGE_KEY_PREFIX)) {
- return configName.substring(STORAGE_KEY_PREFIX.length());
- } else {
- return null;
- }
- }
-
- @Override
- public void dump(@NonNull FileDescriptor fd, @NonNull PrintWriter pw, @NonNull String[] args) {
- ArrayList<String> flagStrings = new ArrayList<>(mCachedFlags.size());
- for (int i = 0; i < mCachedFlags.size(); i++) {
- int key = mCachedFlags.keyAt(i);
- // get the object by the key.
- CachedFlag flag = mCachedFlags.get(key);
- flagStrings.add(" " + RESNAME_PREFIX + flag.name + ": " + flag.value + "\n");
- }
- flagStrings.sort(String.CASE_INSENSITIVE_ORDER);
- pw.println("AreFlagsOverrideable: " + mAreFlagsOverrideable);
- pw.println("Cached FeatureFlags:");
- for (String flagString : flagStrings) {
- pw.print(flagString);
- }
- }
-
- private static class CachedFlag {
- public final String name;
- public final boolean value;
-
- private CachedFlag(String name, boolean value) {
- this.name = name;
- this.value = value;
- }
- }
-
- private static final String STORAGE_KEY_PREFIX = "persist.systemui.flag_";
- private static final String RESNAME_PREFIX = "flag_";
-}
diff --git a/packages/SystemUI/src/com/android/systemui/flags/FeatureFlags.java b/packages/SystemUI/src/com/android/systemui/flags/FeatureFlags.java
index 77e907c..6880674 100644
--- a/packages/SystemUI/src/com/android/systemui/flags/FeatureFlags.java
+++ b/packages/SystemUI/src/com/android/systemui/flags/FeatureFlags.java
@@ -17,13 +17,17 @@
package com.android.systemui.flags;
import android.content.Context;
+import android.content.res.Resources;
import android.util.FeatureFlagUtils;
import android.util.Log;
+import android.util.SparseArray;
import android.widget.Toast;
+import androidx.annotation.BoolRes;
+
import com.android.internal.annotations.VisibleForTesting;
-import com.android.systemui.R;
import com.android.systemui.dagger.SysUISingleton;
+import com.android.systemui.dagger.qualifiers.Main;
import java.util.ArrayList;
import java.util.HashMap;
@@ -39,13 +43,16 @@
*/
@SysUISingleton
public class FeatureFlags {
- private final FeatureFlagReader mFlagReader;
+ private final Resources mResources;
+ private final FlagReader mFlagReader;
private final Context mContext;
private final Map<Integer, Flag<?>> mFlagMap = new HashMap<>();
private final Map<Integer, List<Listener>> mListeners = new HashMap<>();
+ private final SparseArray<Boolean> mCachedFlags = new SparseArray<>();
@Inject
- public FeatureFlags(FeatureFlagReader flagReader, Context context) {
+ public FeatureFlags(@Main Resources resources, FlagReader flagReader, Context context) {
+ mResources = resources;
mFlagReader = flagReader;
mContext = context;
@@ -59,7 +66,7 @@
};
@VisibleForTesting
- void addFlag(Flag flag) {
+ void addFlag(Flag<?> flag) {
mFlagMap.put(flag.getId(), flag);
}
@@ -68,7 +75,15 @@
* @return The value of the flag.
*/
public boolean isEnabled(BooleanFlag flag) {
- return mFlagReader.isEnabled(flag);
+ boolean def = flag.getDefault();
+ if (flag.hasResourceOverride()) {
+ try {
+ def = isEnabledInOverlay(flag.getResourceOverride());
+ } catch (Resources.NotFoundException e) {
+ // no-op
+ }
+ }
+ return mFlagReader.isEnabled(flag.getId(), def);
}
/**
@@ -118,13 +133,11 @@
}
public boolean isPeopleTileEnabled() {
- // TODO(b/202860494): different resource overlays have different values.
- return mFlagReader.isEnabled(R.bool.flag_conversations);
+ return isEnabled(Flags.PEOPLE_TILE);
}
public boolean isMonetEnabled() {
- // TODO(b/202860494): used in wallpaper picker. Always true, maybe delete.
- return mFlagReader.isEnabled(R.bool.flag_monet);
+ return isEnabled(Flags.MONET);
}
public boolean isPMLiteEnabled() {
@@ -132,8 +145,7 @@
}
public boolean isChargingRippleEnabled() {
- // TODO(b/202860494): different resource overlays have different values.
- return mFlagReader.isEnabled(R.bool.flag_charging_ripple);
+ return isEnabled(Flags.CHARGING_RIPPLE);
}
public boolean isOngoingCallStatusBarChipEnabled() {
@@ -150,8 +162,7 @@
}
public boolean isSmartspaceEnabled() {
- // TODO(b/202860494): different resource overlays have different values.
- return mFlagReader.isEnabled(R.bool.flag_smartspace);
+ return isEnabled(Flags.SMARTSPACE);
}
public boolean isSmartspaceDedupingEnabled() {
@@ -163,7 +174,7 @@
}
public boolean isKeyguardQsUserDetailsShortcutEnabled() {
- return mFlagReader.isEnabled(R.bool.flag_lockscreen_qs_user_detail_shortcut);
+ return isEnabled(Flags.QS_USER_DETAIL_SHORTCUT);
}
public boolean isSmartSpaceSharedElementTransitionEnabled() {
@@ -199,6 +210,16 @@
return FeatureFlagUtils.isEnabled(context, FeatureFlagUtils.SETTINGS_PROVIDER_MODEL);
}
+ private boolean isEnabledInOverlay(@BoolRes int resId) {
+ synchronized (mCachedFlags) {
+ if (!mCachedFlags.contains(resId)) {
+ mCachedFlags.put(resId, mResources.getBoolean(resId));
+ }
+
+ return mCachedFlags.get(resId);
+ }
+ }
+
/** Simple interface for beinga alerted when a specific flag changes value. */
public interface Listener {
/** */
diff --git a/packages/SystemUI/src/com/android/systemui/flags/Flags.java b/packages/SystemUI/src/com/android/systemui/flags/Flags.java
index f09c797..5be1cdf 100644
--- a/packages/SystemUI/src/com/android/systemui/flags/Flags.java
+++ b/packages/SystemUI/src/com/android/systemui/flags/Flags.java
@@ -16,6 +16,8 @@
package com.android.systemui.flags;
+import com.android.systemui.R;
+
import java.lang.reflect.Field;
import java.util.HashMap;
import java.util.Map;
@@ -47,7 +49,6 @@
public static final BooleanFlag NOTIFICATION_UPDATES =
new BooleanFlag(102, true);
-
/***************************************/
// 200 - keyguard/lockscreen
public static final BooleanFlag KEYGUARD_LAYOUT =
@@ -59,6 +60,9 @@
public static final BooleanFlag NEW_UNLOCK_SWIPE_ANIMATION =
new BooleanFlag(202, true);
+ public static final BooleanFlag CHARGING_RIPPLE =
+ new BooleanFlag(203, false, R.bool.flag_charging_ripple);
+
/***************************************/
// 300 - power menu
public static final BooleanFlag POWER_MENU_LITE =
@@ -72,6 +76,9 @@
public static final BooleanFlag SMARTSPACE_SHARED_ELEMENT_TRANSITION_ENABLED =
new BooleanFlag(401, false);
+ public static final BooleanFlag SMARTSPACE =
+ new BooleanFlag(402, false, R.bool.flag_smartspace);
+
/***************************************/
// 500 - quick settings
public static final BooleanFlag NEW_USER_SWITCHER =
@@ -80,6 +87,12 @@
public static final BooleanFlag COMBINED_QS_HEADERS =
new BooleanFlag(501, false);
+ public static final BooleanFlag PEOPLE_TILE =
+ new BooleanFlag(502, false, R.bool.flag_conversations);
+
+ public static final BooleanFlag QS_USER_DETAIL_SHORTCUT =
+ new BooleanFlag(503, false, R.bool.flag_lockscreen_qs_user_detail_shortcut);
+
/***************************************/
// 600- status bar
public static final BooleanFlag COMBINED_STATUS_BAR_SIGNAL_ICONS =
@@ -96,6 +109,11 @@
public static final BooleanFlag ONGOING_CALL_IN_IMMERSIVE_CHIP_TAP =
new BooleanFlag(702, true);
+ /***************************************/
+ // 800 - general visual/theme
+ public static final BooleanFlag MONET =
+ new BooleanFlag(800, true, R.bool.flag_monet);
+
// Pay no attention to the reflection behind the curtain.
// ========================== Curtain ==========================
// | |
diff --git a/packages/SystemUI/src/com/android/systemui/people/widget/LaunchConversationActivity.java b/packages/SystemUI/src/com/android/systemui/people/widget/LaunchConversationActivity.java
index 79318d6..e1b97a4 100644
--- a/packages/SystemUI/src/com/android/systemui/people/widget/LaunchConversationActivity.java
+++ b/packages/SystemUI/src/com/android/systemui/people/widget/LaunchConversationActivity.java
@@ -36,8 +36,9 @@
import com.android.internal.statusbar.NotificationVisibility;
import com.android.systemui.people.PeopleSpaceUtils;
import com.android.systemui.statusbar.CommandQueue;
-import com.android.systemui.statusbar.notification.NotificationEntryManager;
import com.android.systemui.statusbar.notification.collection.NotificationEntry;
+import com.android.systemui.statusbar.notification.collection.notifcollection.CommonNotifCollection;
+import com.android.systemui.statusbar.notification.collection.render.NotificationVisibilityProvider;
import com.android.systemui.wmshell.BubblesManager;
import com.android.wm.shell.bubbles.Bubble;
@@ -50,7 +51,8 @@
private static final String TAG = "PeopleSpaceLaunchConv";
private static final boolean DEBUG = PeopleSpaceUtils.DEBUG;
private UiEventLogger mUiEventLogger = new UiEventLoggerImpl();
- private NotificationEntryManager mNotificationEntryManager;
+ private NotificationVisibilityProvider mVisibilityProvider;
+ private CommonNotifCollection mCommonNotifCollection;
private final Optional<BubblesManager> mBubblesManagerOptional;
private final UserManager mUserManager;
private boolean mIsForTesting;
@@ -60,11 +62,16 @@
private NotificationEntry mEntryToBubble;
@Inject
- public LaunchConversationActivity(NotificationEntryManager notificationEntryManager,
- Optional<BubblesManager> bubblesManagerOptional, UserManager userManager,
- CommandQueue commandQueue) {
+ public LaunchConversationActivity(
+ NotificationVisibilityProvider visibilityProvider,
+ CommonNotifCollection commonNotifCollection,
+ Optional<BubblesManager> bubblesManagerOptional,
+ UserManager userManager,
+ CommandQueue commandQueue
+ ) {
super();
- mNotificationEntryManager = notificationEntryManager;
+ mVisibilityProvider = visibilityProvider;
+ mCommonNotifCollection = commonNotifCollection;
mBubblesManagerOptional = bubblesManagerOptional;
mUserManager = userManager;
mCommandQueue = commandQueue;
@@ -128,8 +135,7 @@
// shortcutId, fallback to notificationKey if it exists.
if (mBubblesManagerOptional.isPresent()) {
mBubble = mBubblesManagerOptional.get().getBubbleWithShortcutId(tileId);
- NotificationEntry entry = mNotificationEntryManager.getPendingOrActiveNotif(
- notificationKey);
+ NotificationEntry entry = mCommonNotifCollection.getEntry(notificationKey);
if (mBubble != null || (entry != null && entry.canBubble())) {
mEntryToBubble = entry;
if (DEBUG) {
@@ -167,14 +173,14 @@
}
try {
- if (mIStatusBarService == null || mNotificationEntryManager == null) {
+ if (mIStatusBarService == null || mCommonNotifCollection == null) {
if (DEBUG) {
Log.d(TAG, "Skipping clear notification: null services, key: " + notifKey);
}
return;
}
- NotificationEntry entry = mNotificationEntryManager.getPendingOrActiveNotif(notifKey);
+ NotificationEntry entry = mCommonNotifCollection.getEntry(notifKey);
if (entry == null || entry.getRanking() == null) {
if (DEBUG) {
Log.d(TAG, "Skipping clear notification: NotificationEntry or its Ranking"
@@ -183,10 +189,8 @@
return;
}
- int count = mNotificationEntryManager.getActiveNotificationsCount();
- int rank = entry.getRanking().getRank();
- NotificationVisibility notifVisibility = NotificationVisibility.obtain(notifKey,
- rank, count, true);
+ NotificationVisibility notifVisibility = mVisibilityProvider.obtain(entry, true);
+ int rank = notifVisibility.rank;
if (DEBUG) Log.d(TAG, "Clearing notification, key: " + notifKey + ", rank: " + rank);
mIStatusBarService.onNotificationClear(
diff --git a/packages/SystemUI/src/com/android/systemui/qs/carrier/QSCarrierGroupController.java b/packages/SystemUI/src/com/android/systemui/qs/carrier/QSCarrierGroupController.java
index fec61d9..141c246 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/carrier/QSCarrierGroupController.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/carrier/QSCarrierGroupController.java
@@ -19,6 +19,7 @@
import static android.view.View.IMPORTANT_FOR_ACCESSIBILITY_YES;
import android.annotation.MainThread;
+import android.annotation.NonNull;
import android.content.Context;
import android.content.Intent;
import android.os.Handler;
@@ -42,8 +43,10 @@
import com.android.systemui.dagger.qualifiers.Main;
import com.android.systemui.flags.FeatureFlags;
import com.android.systemui.plugins.ActivityStarter;
+import com.android.systemui.statusbar.connectivity.IconState;
+import com.android.systemui.statusbar.connectivity.MobileDataIndicators;
import com.android.systemui.statusbar.connectivity.NetworkController;
-import com.android.systemui.statusbar.connectivity.NetworkController.MobileDataIndicators;
+import com.android.systemui.statusbar.connectivity.SignalCallback;
import com.android.systemui.util.CarrierConfigTracker;
import java.util.function.Consumer;
@@ -81,10 +84,9 @@
private final SlotIndexResolver mSlotIndexResolver;
- private final NetworkController.SignalCallback mSignalCallback =
- new NetworkController.SignalCallback() {
+ private final SignalCallback mSignalCallback = new SignalCallback() {
@Override
- public void setMobileDataIndicators(MobileDataIndicators indicators) {
+ public void setMobileDataIndicators(@NonNull MobileDataIndicators indicators) {
if (mProviderModel) {
return;
}
@@ -109,7 +111,7 @@
}
@Override
- public void setCallIndicator(NetworkController.IconState statusIcon, int subId) {
+ public void setCallIndicator(@NonNull IconState statusIcon, int subId) {
if (!mProviderModel) {
return;
}
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/CastTile.java b/packages/SystemUI/src/com/android/systemui/qs/tiles/CastTile.java
index 9de6ceb..0427e38 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/CastTile.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/CastTile.java
@@ -18,6 +18,7 @@
import static android.media.MediaRouter.ROUTE_TYPE_REMOTE_DISPLAY;
+import android.annotation.NonNull;
import android.app.Dialog;
import android.content.Context;
import android.content.Intent;
@@ -52,7 +53,8 @@
import com.android.systemui.qs.logging.QSLogger;
import com.android.systemui.qs.tileimpl.QSTileImpl;
import com.android.systemui.statusbar.connectivity.NetworkController;
-import com.android.systemui.statusbar.connectivity.NetworkController.WifiIndicators;
+import com.android.systemui.statusbar.connectivity.SignalCallback;
+import com.android.systemui.statusbar.connectivity.WifiIndicators;
import com.android.systemui.statusbar.phone.SystemUIDialog;
import com.android.systemui.statusbar.policy.CastController;
import com.android.systemui.statusbar.policy.CastController.CastDevice;
@@ -273,10 +275,9 @@
return mWifiConnected || mHotspotConnected;
}
- private final NetworkController.SignalCallback mSignalCallback =
- new NetworkController.SignalCallback() {
+ private final SignalCallback mSignalCallback = new SignalCallback() {
@Override
- public void setWifiIndicators(WifiIndicators indicators) {
+ public void setWifiIndicators(@NonNull WifiIndicators indicators) {
// statusIcon.visible has the connected status information
boolean enabledAndConnected = indicators.enabled
&& (indicators.qsIcon == null ? false : indicators.qsIcon.visible);
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/CellularTile.java b/packages/SystemUI/src/com/android/systemui/qs/tiles/CellularTile.java
index 35dadd4..e5601f2 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/CellularTile.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/CellularTile.java
@@ -18,6 +18,7 @@
import static com.android.systemui.Prefs.Key.QS_HAS_TURNED_OFF_MOBILE_DATA;
+import android.annotation.NonNull;
import android.app.AlertDialog;
import android.app.AlertDialog.Builder;
import android.content.Context;
@@ -56,10 +57,10 @@
import com.android.systemui.qs.SignalTileView;
import com.android.systemui.qs.logging.QSLogger;
import com.android.systemui.qs.tileimpl.QSTileImpl;
+import com.android.systemui.statusbar.connectivity.IconState;
+import com.android.systemui.statusbar.connectivity.MobileDataIndicators;
import com.android.systemui.statusbar.connectivity.NetworkController;
-import com.android.systemui.statusbar.connectivity.NetworkController.IconState;
-import com.android.systemui.statusbar.connectivity.NetworkController.MobileDataIndicators;
-import com.android.systemui.statusbar.connectivity.NetworkController.SignalCallback;
+import com.android.systemui.statusbar.connectivity.SignalCallback;
import com.android.systemui.statusbar.phone.SystemUIDialog;
import javax.inject.Inject;
@@ -269,7 +270,7 @@
private final CallbackInfo mInfo = new CallbackInfo();
@Override
- public void setMobileDataIndicators(MobileDataIndicators indicators) {
+ public void setMobileDataIndicators(@NonNull MobileDataIndicators indicators) {
if (indicators.qsIcon == null) {
// Not data sim, don't display.
return;
@@ -291,7 +292,7 @@
}
@Override
- public void setIsAirplaneMode(IconState icon) {
+ public void setIsAirplaneMode(@NonNull IconState icon) {
mInfo.airplaneModeEnabled = icon.visible;
refreshState(mInfo);
}
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/DataSaverTile.java b/packages/SystemUI/src/com/android/systemui/qs/tiles/DataSaverTile.java
index 5a11ff8..7ba9cc2 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/DataSaverTile.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/DataSaverTile.java
@@ -14,7 +14,6 @@
package com.android.systemui.qs.tiles;
-import android.content.DialogInterface.OnClickListener;
import android.content.Intent;
import android.os.Handler;
import android.os.Looper;
@@ -29,6 +28,7 @@
import com.android.internal.logging.nano.MetricsProto.MetricsEvent;
import com.android.systemui.Prefs;
import com.android.systemui.R;
+import com.android.systemui.animation.DialogLaunchAnimator;
import com.android.systemui.dagger.qualifiers.Background;
import com.android.systemui.dagger.qualifiers.Main;
import com.android.systemui.plugins.ActivityStarter;
@@ -47,6 +47,7 @@
DataSaverController.Listener{
private final DataSaverController mDataSaverController;
+ private final DialogLaunchAnimator mDialogLaunchAnimator;
@Inject
public DataSaverTile(
@@ -58,11 +59,13 @@
StatusBarStateController statusBarStateController,
ActivityStarter activityStarter,
QSLogger qsLogger,
- DataSaverController dataSaverController
+ DataSaverController dataSaverController,
+ DialogLaunchAnimator dialogLaunchAnimator
) {
super(host, backgroundLooper, mainHandler, falsingManager, metricsLogger,
statusBarStateController, activityStarter, qsLogger);
mDataSaverController = dataSaverController;
+ mDialogLaunchAnimator = dialogLaunchAnimator;
mDataSaverController.observe(getLifecycle(), this);
}
@@ -83,18 +86,27 @@
toggleDataSaver();
return;
}
- // Shows dialog first
- SystemUIDialog dialog = new SystemUIDialog(mContext);
- dialog.setTitle(com.android.internal.R.string.data_saver_enable_title);
- dialog.setMessage(com.android.internal.R.string.data_saver_description);
- dialog.setPositiveButton(com.android.internal.R.string.data_saver_enable_button,
- (OnClickListener) (dialogInterface, which) -> {
- toggleDataSaver();
- Prefs.putBoolean(mContext, Prefs.Key.QS_DATA_SAVER_DIALOG_SHOWN, true);
- });
- dialog.setNegativeButton(com.android.internal.R.string.cancel, null);
- dialog.setShowForAllUsers(true);
- dialog.show();
+
+ // Show a dialog to confirm first. Dialogs shown by the DialogLaunchAnimator must be created
+ // and shown on the main thread, so we post it to the UI handler.
+ mUiHandler.post(() -> {
+ SystemUIDialog dialog = new SystemUIDialog(mContext);
+ dialog.setTitle(com.android.internal.R.string.data_saver_enable_title);
+ dialog.setMessage(com.android.internal.R.string.data_saver_description);
+ dialog.setPositiveButton(com.android.internal.R.string.data_saver_enable_button,
+ (dialogInterface, which) -> {
+ toggleDataSaver();
+ Prefs.putBoolean(mContext, Prefs.Key.QS_DATA_SAVER_DIALOG_SHOWN, true);
+ });
+ dialog.setNegativeButton(com.android.internal.R.string.cancel, null);
+ dialog.setShowForAllUsers(true);
+
+ if (view != null) {
+ mDialogLaunchAnimator.showFromView(dialog, view);
+ } else {
+ dialog.show();
+ }
+ });
}
private void toggleDataSaver() {
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/InternetTile.java b/packages/SystemUI/src/com/android/systemui/qs/tiles/InternetTile.java
index 23b2a76..cd81b4a 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/InternetTile.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/InternetTile.java
@@ -16,6 +16,7 @@
package com.android.systemui.qs.tiles;
+import android.annotation.NonNull;
import android.content.Context;
import android.content.Intent;
import android.content.pm.PackageManager;
@@ -52,13 +53,13 @@
import com.android.systemui.qs.logging.QSLogger;
import com.android.systemui.qs.tileimpl.QSTileImpl;
import com.android.systemui.qs.tiles.dialog.InternetDialogFactory;
+import com.android.systemui.statusbar.connectivity.AccessPointController;
+import com.android.systemui.statusbar.connectivity.IconState;
+import com.android.systemui.statusbar.connectivity.MobileDataIndicators;
import com.android.systemui.statusbar.connectivity.NetworkController;
-import com.android.systemui.statusbar.connectivity.NetworkController.AccessPointController;
-import com.android.systemui.statusbar.connectivity.NetworkController.IconState;
-import com.android.systemui.statusbar.connectivity.NetworkController.MobileDataIndicators;
-import com.android.systemui.statusbar.connectivity.NetworkController.SignalCallback;
-import com.android.systemui.statusbar.connectivity.NetworkController.WifiIndicators;
+import com.android.systemui.statusbar.connectivity.SignalCallback;
import com.android.systemui.statusbar.connectivity.WifiIcons;
+import com.android.systemui.statusbar.connectivity.WifiIndicators;
import java.io.FileDescriptor;
import java.io.PrintWriter;
@@ -250,7 +251,7 @@
@Override
- public void setWifiIndicators(WifiIndicators indicators) {
+ public void setWifiIndicators(@NonNull WifiIndicators indicators) {
if (DEBUG) {
Log.d(TAG, "setWifiIndicators: " + indicators);
}
@@ -271,7 +272,7 @@
}
@Override
- public void setMobileDataIndicators(MobileDataIndicators indicators) {
+ public void setMobileDataIndicators(@NonNull MobileDataIndicators indicators) {
if (DEBUG) {
Log.d(TAG, "setMobileDataIndicators: " + indicators);
}
@@ -293,7 +294,7 @@
}
@Override
- public void setEthernetIndicators(IconState icon) {
+ public void setEthernetIndicators(@NonNull IconState icon) {
if (DEBUG) {
Log.d(TAG, "setEthernetIndicators: "
+ "icon = " + (icon == null ? "" : icon.toString()));
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/ScreenRecordTile.java b/packages/SystemUI/src/com/android/systemui/qs/tiles/ScreenRecordTile.java
index 24b9208..8ff75cb 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/ScreenRecordTile.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/ScreenRecordTile.java
@@ -29,6 +29,7 @@
import com.android.internal.logging.MetricsLogger;
import com.android.systemui.R;
+import com.android.systemui.animation.DialogLaunchAnimator;
import com.android.systemui.dagger.qualifiers.Background;
import com.android.systemui.dagger.qualifiers.Main;
import com.android.systemui.plugins.ActivityStarter;
@@ -39,7 +40,9 @@
import com.android.systemui.qs.logging.QSLogger;
import com.android.systemui.qs.tileimpl.QSTileImpl;
import com.android.systemui.screenrecord.RecordingController;
+import com.android.systemui.screenrecord.ScreenRecordDialog;
import com.android.systemui.statusbar.phone.KeyguardDismissUtil;
+import com.android.systemui.statusbar.policy.KeyguardStateController;
import javax.inject.Inject;
@@ -49,10 +52,13 @@
public class ScreenRecordTile extends QSTileImpl<QSTile.BooleanState>
implements RecordingController.RecordingStateChangeCallback {
private static final String TAG = "ScreenRecordTile";
- private RecordingController mController;
- private KeyguardDismissUtil mKeyguardDismissUtil;
+ private final RecordingController mController;
+ private final KeyguardDismissUtil mKeyguardDismissUtil;
+ private final KeyguardStateController mKeyguardStateController;
+ private final Callback mCallback = new Callback();
+ private final DialogLaunchAnimator mDialogLaunchAnimator;
+
private long mMillisUntilFinished = 0;
- private Callback mCallback = new Callback();
@Inject
public ScreenRecordTile(
@@ -65,13 +71,17 @@
ActivityStarter activityStarter,
QSLogger qsLogger,
RecordingController controller,
- KeyguardDismissUtil keyguardDismissUtil
+ KeyguardDismissUtil keyguardDismissUtil,
+ KeyguardStateController keyguardStateController,
+ DialogLaunchAnimator dialogLaunchAnimator
) {
super(host, backgroundLooper, mainHandler, falsingManager, metricsLogger,
statusBarStateController, activityStarter, qsLogger);
mController = controller;
mController.observe(this, mCallback);
mKeyguardDismissUtil = keyguardDismissUtil;
+ mKeyguardStateController = keyguardStateController;
+ mDialogLaunchAnimator = dialogLaunchAnimator;
}
@Override
@@ -89,7 +99,7 @@
} else if (mController.isRecording()) {
stopRecording();
} else {
- mUiHandler.post(() -> showPrompt());
+ mUiHandler.post(() -> showPrompt(view));
}
refreshState();
}
@@ -136,15 +146,33 @@
return mContext.getString(R.string.quick_settings_screen_record_label);
}
- private void showPrompt() {
- // Close QS, otherwise the dialog appears beneath it
- getHost().collapsePanels();
- Intent intent = mController.getPromptIntent();
+ private void showPrompt(@Nullable View view) {
+ // We animate from the touched view only if we are not on the keyguard, given that if we
+ // are we will dismiss it which will also collapse the shade.
+ boolean shouldAnimateFromView = view != null && !mKeyguardStateController.isShowing();
+
+ // Create the recording dialog that will collapse the shade only if we start the recording.
+ Runnable onStartRecordingClicked = () -> {
+ // We dismiss the shade. Since starting the recording will also dismiss the dialog, we
+ // disable the exit animation which looks weird when it happens at the same time as the
+ // shade collapsing.
+ mDialogLaunchAnimator.disableAllCurrentDialogsExitAnimations();
+ getHost().collapsePanels();
+ };
+ ScreenRecordDialog dialog = mController.createScreenRecordDialog(mContext,
+ onStartRecordingClicked);
+
ActivityStarter.OnDismissAction dismissAction = () -> {
- mHost.getUserContext().startActivity(intent);
+ if (shouldAnimateFromView) {
+ mDialogLaunchAnimator.showFromView(dialog, view);
+ } else {
+ dialog.show();
+ }
return false;
};
- mKeyguardDismissUtil.executeWhenUnlocked(dismissAction, false, false);
+
+ mKeyguardDismissUtil.executeWhenUnlocked(dismissAction, false /* requiresShadeOpen */,
+ true /* afterKeyguardDone */);
}
private void cancelCountdown() {
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/WifiTile.java b/packages/SystemUI/src/com/android/systemui/qs/tiles/WifiTile.java
index e6e7e21..e79ca0c 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/WifiTile.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/WifiTile.java
@@ -16,6 +16,7 @@
package com.android.systemui.qs.tiles;
+import android.annotation.NonNull;
import android.content.Context;
import android.content.Intent;
import android.content.pm.PackageManager;
@@ -52,11 +53,11 @@
import com.android.systemui.qs.logging.QSLogger;
import com.android.systemui.qs.tileimpl.QSIconViewImpl;
import com.android.systemui.qs.tileimpl.QSTileImpl;
+import com.android.systemui.statusbar.connectivity.AccessPointController;
import com.android.systemui.statusbar.connectivity.NetworkController;
-import com.android.systemui.statusbar.connectivity.NetworkController.AccessPointController;
-import com.android.systemui.statusbar.connectivity.NetworkController.SignalCallback;
-import com.android.systemui.statusbar.connectivity.NetworkController.WifiIndicators;
+import com.android.systemui.statusbar.connectivity.SignalCallback;
import com.android.systemui.statusbar.connectivity.WifiIcons;
+import com.android.systemui.statusbar.connectivity.WifiIndicators;
import com.android.wifitrackerlib.WifiEntry;
import java.util.List;
@@ -310,7 +311,7 @@
final CallbackInfo mInfo = new CallbackInfo();
@Override
- public void setWifiIndicators(WifiIndicators indicators) {
+ public void setWifiIndicators(@NonNull WifiIndicators indicators) {
if (DEBUG) Log.d(TAG, "onWifiSignalChanged enabled=" + indicators.enabled);
if (indicators.qsIcon == null) {
return;
@@ -332,7 +333,7 @@
}
protected class WifiDetailAdapter implements DetailAdapter,
- NetworkController.AccessPointController.AccessPointCallback, QSDetailItems.Callback {
+ AccessPointController.AccessPointCallback, QSDetailItems.Callback {
private QSDetailItems mItems;
private WifiEntry[] mAccessPoints;
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/dialog/InternetDialogController.java b/packages/SystemUI/src/com/android/systemui/qs/tiles/dialog/InternetDialogController.java
index 5673136..1c8bd78 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/dialog/InternetDialogController.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/dialog/InternetDialogController.java
@@ -76,8 +76,7 @@
import com.android.systemui.dagger.qualifiers.Background;
import com.android.systemui.dagger.qualifiers.Main;
import com.android.systemui.plugins.ActivityStarter;
-import com.android.systemui.statusbar.connectivity.NetworkController;
-import com.android.systemui.statusbar.connectivity.NetworkController.AccessPointController;
+import com.android.systemui.statusbar.connectivity.AccessPointController;
import com.android.systemui.statusbar.policy.KeyguardStateController;
import com.android.systemui.statusbar.policy.LocationController;
import com.android.systemui.toast.SystemUIToast;
@@ -100,7 +99,7 @@
import javax.inject.Inject;
public class InternetDialogController implements WifiEntry.DisconnectCallback,
- NetworkController.AccessPointController.AccessPointCallback {
+ AccessPointController.AccessPointCallback {
private static final String TAG = "InternetDialogController";
private static final String ACTION_NETWORK_PROVIDER_SETTINGS =
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/dialog/InternetDialogFactory.kt b/packages/SystemUI/src/com/android/systemui/qs/tiles/dialog/InternetDialogFactory.kt
index 93828b3..79f7ac3 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/dialog/InternetDialogFactory.kt
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/dialog/InternetDialogFactory.kt
@@ -63,7 +63,8 @@
canConfigMobileData, canConfigWifi, aboveStatusBar, uiEventLogger, handler,
executor)
if (view != null) {
- dialogLaunchAnimator.showFromView(internetDialog!!, view)
+ dialogLaunchAnimator.showFromView(internetDialog!!, view,
+ animateBackgroundBoundsChange = true)
} else {
internetDialog?.show()
}
diff --git a/packages/SystemUI/src/com/android/systemui/screenrecord/RecordingController.java b/packages/SystemUI/src/com/android/systemui/screenrecord/RecordingController.java
index 060d7b1..1a08878 100644
--- a/packages/SystemUI/src/com/android/systemui/screenrecord/RecordingController.java
+++ b/packages/SystemUI/src/com/android/systemui/screenrecord/RecordingController.java
@@ -18,7 +18,6 @@
import android.app.PendingIntent;
import android.content.BroadcastReceiver;
-import android.content.ComponentName;
import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
@@ -27,10 +26,12 @@
import android.util.Log;
import androidx.annotation.NonNull;
+import androidx.annotation.Nullable;
import com.android.internal.annotations.VisibleForTesting;
import com.android.systemui.broadcast.BroadcastDispatcher;
import com.android.systemui.dagger.SysUISingleton;
+import com.android.systemui.settings.UserContextProvider;
import com.android.systemui.statusbar.policy.CallbackController;
import java.util.concurrent.CopyOnWriteArrayList;
@@ -44,15 +45,13 @@
public class RecordingController
implements CallbackController<RecordingController.RecordingStateChangeCallback> {
private static final String TAG = "RecordingController";
- private static final String SYSUI_PACKAGE = "com.android.systemui";
- private static final String SYSUI_SCREENRECORD_LAUNCHER =
- "com.android.systemui.screenrecord.ScreenRecordDialog";
private boolean mIsStarting;
private boolean mIsRecording;
private PendingIntent mStopIntent;
private CountDownTimer mCountDownTimer = null;
private BroadcastDispatcher mBroadcastDispatcher;
+ private UserContextProvider mUserContextProvider;
protected static final String INTENT_UPDATE_STATE =
"com.android.systemui.screenrecord.UPDATE_STATE";
@@ -88,20 +87,16 @@
* Create a new RecordingController
*/
@Inject
- public RecordingController(BroadcastDispatcher broadcastDispatcher) {
+ public RecordingController(BroadcastDispatcher broadcastDispatcher,
+ UserContextProvider userContextProvider) {
mBroadcastDispatcher = broadcastDispatcher;
+ mUserContextProvider = userContextProvider;
}
- /**
- * Get an intent to show screen recording options to the user.
- */
- public Intent getPromptIntent() {
- final ComponentName launcherComponent = new ComponentName(SYSUI_PACKAGE,
- SYSUI_SCREENRECORD_LAUNCHER);
- final Intent intent = new Intent();
- intent.setComponent(launcherComponent);
- intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
- return intent;
+ /** Create a dialog to show screen recording options to the user. */
+ public ScreenRecordDialog createScreenRecordDialog(Context context,
+ @Nullable Runnable onStartRecordingClicked) {
+ return new ScreenRecordDialog(context, this, mUserContextProvider, onStartRecordingClicked);
}
/**
diff --git a/packages/SystemUI/src/com/android/systemui/screenrecord/ScreenRecordDialog.java b/packages/SystemUI/src/com/android/systemui/screenrecord/ScreenRecordDialog.java
index df766f3..1fb88df 100644
--- a/packages/SystemUI/src/com/android/systemui/screenrecord/ScreenRecordDialog.java
+++ b/packages/SystemUI/src/com/android/systemui/screenrecord/ScreenRecordDialog.java
@@ -26,7 +26,6 @@
import android.content.Context;
import android.os.Bundle;
import android.view.Gravity;
-import android.view.ViewGroup;
import android.view.Window;
import android.view.WindowManager;
import android.widget.ArrayAdapter;
@@ -34,34 +33,38 @@
import android.widget.Switch;
import android.widget.TextView;
+import androidx.annotation.Nullable;
+
import com.android.systemui.R;
import com.android.systemui.settings.UserContextProvider;
+import com.android.systemui.statusbar.phone.SystemUIDialog;
-import java.util.ArrayList;
+import java.util.Arrays;
import java.util.List;
-import javax.inject.Inject;
-
/**
- * Activity to select screen recording options
+ * Dialog to select screen recording options
*/
-public class ScreenRecordDialog extends Activity {
+public class ScreenRecordDialog extends SystemUIDialog {
+ private static final List<ScreenRecordingAudioSource> MODES = Arrays.asList(INTERNAL, MIC,
+ MIC_AND_INTERNAL);
private static final long DELAY_MS = 3000;
private static final long INTERVAL_MS = 1000;
- private static final String TAG = "ScreenRecordDialog";
private final RecordingController mController;
private final UserContextProvider mUserContextProvider;
+ @Nullable
+ private final Runnable mOnStartRecordingClicked;
private Switch mTapsSwitch;
private Switch mAudioSwitch;
private Spinner mOptions;
- private List<ScreenRecordingAudioSource> mModes;
- @Inject
- public ScreenRecordDialog(RecordingController controller,
- UserContextProvider userContextProvider) {
+ public ScreenRecordDialog(Context context, RecordingController controller,
+ UserContextProvider userContextProvider, @Nullable Runnable onStartRecordingClicked) {
+ super(context);
mController = controller;
mUserContextProvider = userContextProvider;
+ mOnStartRecordingClicked = onStartRecordingClicked;
}
@Override
@@ -69,37 +72,35 @@
super.onCreate(savedInstanceState);
Window window = getWindow();
- // Inflate the decor view, so the attributes below are not overwritten by the theme.
- window.getDecorView();
- window.setLayout(ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.WRAP_CONTENT);
+
window.addPrivateFlags(WindowManager.LayoutParams.SYSTEM_FLAG_SHOW_FOR_ALL_USERS);
- window.setGravity(Gravity.TOP);
+
+ window.setGravity(Gravity.CENTER);
setTitle(R.string.screenrecord_name);
setContentView(R.layout.screen_record_dialog);
TextView cancelBtn = findViewById(R.id.button_cancel);
- cancelBtn.setOnClickListener(v -> {
- finish();
- });
+ cancelBtn.setOnClickListener(v -> dismiss());
TextView startBtn = findViewById(R.id.button_start);
startBtn.setOnClickListener(v -> {
- requestScreenCapture();
- finish();
- });
+ if (mOnStartRecordingClicked != null) {
+ // Note that it is important to run this callback before dismissing, so that the
+ // callback can disable the dialog exit animation if it wants to.
+ mOnStartRecordingClicked.run();
+ }
- mModes = new ArrayList<>();
- mModes.add(INTERNAL);
- mModes.add(MIC);
- mModes.add(MIC_AND_INTERNAL);
+ requestScreenCapture();
+ dismiss();
+ });
mAudioSwitch = findViewById(R.id.screenrecord_audio_switch);
mTapsSwitch = findViewById(R.id.screenrecord_taps_switch);
mOptions = findViewById(R.id.screen_recording_options);
- ArrayAdapter a = new ScreenRecordingAdapter(getApplicationContext(),
+ ArrayAdapter a = new ScreenRecordingAdapter(getContext().getApplicationContext(),
android.R.layout.simple_spinner_dropdown_item,
- mModes);
+ MODES);
a.setDropDownViewResource(android.R.layout.simple_spinner_dropdown_item);
mOptions.setAdapter(a);
mOptions.setOnItemClickListenerInt((parent, view, position, id) -> {
@@ -116,7 +117,7 @@
PendingIntent startIntent = PendingIntent.getForegroundService(userContext,
RecordingService.REQUEST_CODE,
RecordingService.getStartIntent(
- userContext, RESULT_OK,
+ userContext, Activity.RESULT_OK,
audioMode.ordinal(), showTaps),
PendingIntent.FLAG_UPDATE_CURRENT | PendingIntent.FLAG_IMMUTABLE);
PendingIntent stopIntent = PendingIntent.getService(userContext,
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationLockscreenUserManagerImpl.java b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationLockscreenUserManagerImpl.java
index fca2a18c..fa2ef30 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationLockscreenUserManagerImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationLockscreenUserManagerImpl.java
@@ -56,7 +56,8 @@
import com.android.systemui.recents.OverviewProxyService;
import com.android.systemui.statusbar.notification.NotificationEntryManager;
import com.android.systemui.statusbar.notification.collection.NotificationEntry;
-import com.android.systemui.statusbar.notification.logging.NotificationLogger;
+import com.android.systemui.statusbar.notification.collection.notifcollection.CommonNotifCollection;
+import com.android.systemui.statusbar.notification.collection.render.NotificationVisibilityProvider;
import com.android.systemui.statusbar.policy.DeviceProvisionedController;
import com.android.systemui.statusbar.policy.KeyguardStateController;
@@ -67,6 +68,8 @@
import javax.inject.Inject;
+import dagger.Lazy;
+
/**
* Handles keeping track of the current user, profiles, and various things related to hiding
* contents, redacting notifications, and the lockscreen.
@@ -86,6 +89,8 @@
// Lazy
private NotificationEntryManager mEntryManager;
+ private final Lazy<NotificationVisibilityProvider> mVisibilityProviderLazy;
+ private final Lazy<CommonNotifCollection> mCommonNotifCollectionLazy;
private final DevicePolicyManager mDevicePolicyManager;
private final SparseBooleanArray mLockscreenPublicMode = new SparseBooleanArray();
private final SparseBooleanArray mUsersWithSeperateWorkChallenge = new SparseBooleanArray();
@@ -162,15 +167,8 @@
}
}
if (notificationKey != null) {
- NotificationEntry entry =
- getEntryManager().getActiveNotificationUnfiltered(notificationKey);
- final int count = getEntryManager().getActiveNotificationsCount();
- final int rank = entry != null ? entry.getRanking().getRank() : 0;
- NotificationVisibility.NotificationLocation location =
- NotificationLogger.getNotificationLocation(entry);
- final NotificationVisibility nv = NotificationVisibility.obtain(
- notificationKey,
- rank, count, true, location);
+ final NotificationVisibility nv = mVisibilityProviderLazy.get()
+ .obtain(notificationKey, true);
mClickNotifier.onNotificationClick(notificationKey, nv);
}
break;
@@ -200,6 +198,8 @@
BroadcastDispatcher broadcastDispatcher,
DevicePolicyManager devicePolicyManager,
UserManager userManager,
+ Lazy<NotificationVisibilityProvider> visibilityProviderLazy,
+ Lazy<CommonNotifCollection> commonNotifCollectionLazy,
NotificationClickNotifier clickNotifier,
KeyguardManager keyguardManager,
StatusBarStateController statusBarStateController,
@@ -212,6 +212,8 @@
mDevicePolicyManager = devicePolicyManager;
mUserManager = userManager;
mCurrentUserId = ActivityManager.getCurrentUser();
+ mVisibilityProviderLazy = visibilityProviderLazy;
+ mCommonNotifCollectionLazy = commonNotifCollectionLazy;
mClickNotifier = clickNotifier;
statusBarStateController.addCallback(this);
mLockPatternUtils = new LockPatternUtils(context);
@@ -337,18 +339,18 @@
* package-specific override.
*/
public boolean shouldHideNotifications(String key) {
- if (getEntryManager() == null) {
- Log.wtf(TAG, "mEntryManager was null!", new Throwable());
+ if (mCommonNotifCollectionLazy.get() == null) {
+ Log.wtf(TAG, "mCommonNotifCollectionLazy was null!", new Throwable());
return true;
}
- NotificationEntry visibleEntry = getEntryManager().getActiveNotificationUnfiltered(key);
+ NotificationEntry visibleEntry = mCommonNotifCollectionLazy.get().getEntry(key);
return isLockscreenPublicMode(mCurrentUserId) && visibleEntry != null
&& visibleEntry.getRanking().getLockscreenVisibilityOverride() == VISIBILITY_SECRET;
}
public boolean shouldShowOnKeyguard(NotificationEntry entry) {
- if (getEntryManager() == null) {
- Log.wtf(TAG, "mEntryManager was null!", new Throwable());
+ if (mCommonNotifCollectionLazy.get() == null) {
+ Log.wtf(TAG, "mCommonNotifCollectionLazy was null!", new Throwable());
return false;
}
for (int i = 0; i < mKeyguardSuppressors.size(); i++) {
@@ -520,11 +522,11 @@
}
private boolean packageHasVisibilityOverride(String key) {
- if (getEntryManager() == null) {
+ if (mCommonNotifCollectionLazy.get() == null) {
Log.wtf(TAG, "mEntryManager was null!", new Throwable());
return true;
}
- NotificationEntry entry = getEntryManager().getActiveNotificationUnfiltered(key);
+ NotificationEntry entry = mCommonNotifCollectionLazy.get().getEntry(key);
return entry != null
&& entry.getRanking().getLockscreenVisibilityOverride()
== Notification.VISIBILITY_PRIVATE;
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationMediaManager.java b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationMediaManager.java
index dcb1e4f..7cfa830 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationMediaManager.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationMediaManager.java
@@ -66,7 +66,7 @@
import com.android.systemui.statusbar.notification.collection.NotificationEntry;
import com.android.systemui.statusbar.notification.collection.notifcollection.DismissedByUserStats;
import com.android.systemui.statusbar.notification.collection.notifcollection.NotifCollectionListener;
-import com.android.systemui.statusbar.notification.logging.NotificationLogger;
+import com.android.systemui.statusbar.notification.collection.render.NotificationVisibilityProvider;
import com.android.systemui.statusbar.phone.BiometricUnlockController;
import com.android.systemui.statusbar.phone.KeyguardBypassController;
import com.android.systemui.statusbar.phone.LockscreenWallpaper;
@@ -113,6 +113,7 @@
PAUSED_MEDIA_STATES.add(PlaybackState.STATE_CONNECTING);
}
+ private final NotificationVisibilityProvider mVisibilityProvider;
private final NotificationEntryManager mEntryManager;
private final MediaDataManager mMediaDataManager;
private final NotifPipeline mNotifPipeline;
@@ -181,6 +182,7 @@
Context context,
Lazy<Optional<StatusBar>> statusBarOptionalLazy,
Lazy<NotificationShadeWindowController> notificationShadeWindowController,
+ NotificationVisibilityProvider visibilityProvider,
NotificationEntryManager notificationEntryManager,
MediaArtworkProcessor mediaArtworkProcessor,
KeyguardBypassController keyguardBypassController,
@@ -201,6 +203,7 @@
// TODO: use KeyguardStateController#isOccluded to remove this dependency
mStatusBarOptionalLazy = statusBarOptionalLazy;
mNotificationShadeWindowController = notificationShadeWindowController;
+ mVisibilityProvider = visibilityProvider;
mEntryManager = notificationEntryManager;
mMainExecutor = mainExecutor;
mMediaDataManager = mediaDataManager;
@@ -351,21 +354,10 @@
}
private DismissedByUserStats getDismissedByUserStats(NotificationEntry entry) {
- final int activeNotificationsCount;
- if (mUsingNotifPipeline) {
- activeNotificationsCount = mNotifPipeline.getShadeListCount();
- } else {
- activeNotificationsCount = mEntryManager.getActiveNotificationsCount();
- }
return new DismissedByUserStats(
NotificationStats.DISMISSAL_SHADE, // Add DISMISSAL_MEDIA?
NotificationStats.DISMISS_SENTIMENT_NEUTRAL,
- NotificationVisibility.obtain(
- entry.getKey(),
- entry.getRanking().getRank(),
- activeNotificationsCount,
- /* visible= */ true,
- NotificationLogger.getNotificationLocation(entry)));
+ mVisibilityProvider.obtain(entry, /* visible= */ true));
}
private void removeEntry(NotificationEntry entry) {
@@ -406,10 +398,7 @@
return null;
}
if (mUsingNotifPipeline) {
- // TODO(b/169655596): Either add O(1) lookup, or cache this icon?
- return mNotifPipeline.getAllNotifs().stream()
- .filter(entry -> Objects.equals(entry.getKey(), mMediaNotificationKey))
- .findAny()
+ return Optional.ofNullable(mNotifPipeline.getEntry(mMediaNotificationKey))
.map(entry -> entry.getIcons().getShelfIcon())
.map(StatusBarIconView::getSourceIcon)
.orElse(null);
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationRemoteInputManager.java b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationRemoteInputManager.java
index 1ce7f03..dd44f72 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationRemoteInputManager.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationRemoteInputManager.java
@@ -60,6 +60,7 @@
import com.android.systemui.statusbar.notification.NotificationEntryManager;
import com.android.systemui.statusbar.notification.collection.NotificationEntry;
import com.android.systemui.statusbar.notification.collection.NotificationEntry.EditedSuggestionInfo;
+import com.android.systemui.statusbar.notification.collection.render.NotificationVisibilityProvider;
import com.android.systemui.statusbar.notification.logging.NotificationLogger;
import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow;
import com.android.systemui.statusbar.phone.StatusBar;
@@ -95,6 +96,7 @@
// Dependencies:
private final NotificationLockscreenUserManager mLockscreenUserManager;
private final SmartReplyController mSmartReplyController;
+ private final NotificationVisibilityProvider mVisibilityProvider;
private final NotificationEntryManager mEntryManager;
private final Handler mMainHandler;
private final ActionClickLogger mLogger;
@@ -202,14 +204,7 @@
ViewGroup actionGroup = (ViewGroup) parent;
buttonIndex = actionGroup.indexOfChild(view);
}
- // TODO(b/204183781): get this from the current pipeline
- final int count = mEntryManager.getActiveNotificationsCount();
- final int rank = entry.getRanking().getRank();
-
- NotificationVisibility.NotificationLocation location =
- NotificationLogger.getNotificationLocation(entry);
- final NotificationVisibility nv =
- NotificationVisibility.obtain(key, rank, count, true, location);
+ final NotificationVisibility nv = mVisibilityProvider.obtain(entry, true);
mClickNotifier.onNotificationActionClick(key, buttonIndex, action, nv, false);
}
@@ -263,6 +258,7 @@
FeatureFlags featureFlags,
NotificationLockscreenUserManager lockscreenUserManager,
SmartReplyController smartReplyController,
+ NotificationVisibilityProvider visibilityProvider,
NotificationEntryManager notificationEntryManager,
RemoteInputNotificationRebuilder rebuilder,
Lazy<Optional<StatusBar>> statusBarOptionalLazy,
@@ -276,6 +272,7 @@
mFeatureFlags = featureFlags;
mLockscreenUserManager = lockscreenUserManager;
mSmartReplyController = smartReplyController;
+ mVisibilityProvider = visibilityProvider;
mEntryManager = notificationEntryManager;
mStatusBarOptionalLazy = statusBarOptionalLazy;
mMainHandler = mainHandler;
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/OperatorNameViewController.java b/packages/SystemUI/src/com/android/systemui/statusbar/OperatorNameViewController.java
index bcba5cc..8a4c4b5 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/OperatorNameViewController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/OperatorNameViewController.java
@@ -16,6 +16,7 @@
package com.android.systemui.statusbar;
+import android.annotation.NonNull;
import android.os.Bundle;
import android.telephony.ServiceState;
import android.telephony.SubscriptionInfo;
@@ -26,7 +27,9 @@
import com.android.keyguard.KeyguardUpdateMonitorCallback;
import com.android.systemui.demomode.DemoModeCommandReceiver;
import com.android.systemui.plugins.DarkIconDispatcher;
+import com.android.systemui.statusbar.connectivity.IconState;
import com.android.systemui.statusbar.connectivity.NetworkController;
+import com.android.systemui.statusbar.connectivity.SignalCallback;
import com.android.systemui.tuner.TunerService;
import com.android.systemui.util.ViewController;
@@ -135,10 +138,9 @@
(area, darkIntensity, tint) ->
mView.setTextColor(DarkIconDispatcher.getTint(area, mView, tint));
- private final NetworkController.SignalCallback mSignalCallback =
- new NetworkController.SignalCallback() {
+ private final SignalCallback mSignalCallback = new SignalCallback() {
@Override
- public void setIsAirplaneMode(NetworkController.IconState icon) {
+ public void setIsAirplaneMode(@NonNull IconState icon) {
update();
}
};
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/SmartReplyController.java b/packages/SystemUI/src/com/android/systemui/statusbar/SmartReplyController.java
index e288b1530..4ad01aa 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/SmartReplyController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/SmartReplyController.java
@@ -26,9 +26,8 @@
import com.android.systemui.Dumpable;
import com.android.systemui.dump.DumpManager;
import com.android.systemui.statusbar.dagger.StatusBarModule;
-import com.android.systemui.statusbar.notification.NotificationEntryManager;
import com.android.systemui.statusbar.notification.collection.NotificationEntry;
-import com.android.systemui.statusbar.notification.logging.NotificationLogger;
+import com.android.systemui.statusbar.notification.collection.render.NotificationVisibilityProvider;
import java.io.FileDescriptor;
import java.io.PrintWriter;
@@ -40,7 +39,7 @@
*/
public class SmartReplyController implements Dumpable {
private final IStatusBarService mBarService;
- private final NotificationEntryManager mEntryManager;
+ private final NotificationVisibilityProvider mVisibilityProvider;
private final NotificationClickNotifier mClickNotifier;
private final Set<String> mSendingKeys = new ArraySet<>();
private Callback mCallback;
@@ -50,11 +49,11 @@
*/
public SmartReplyController(
DumpManager dumpManager,
- NotificationEntryManager entryManager,
+ NotificationVisibilityProvider visibilityProvider,
IStatusBarService statusBarService,
NotificationClickNotifier clickNotifier) {
mBarService = statusBarService;
- mEntryManager = entryManager;
+ mVisibilityProvider = visibilityProvider;
mClickNotifier = clickNotifier;
dumpManager.registerDumpable(this);
}
@@ -84,13 +83,7 @@
public void smartActionClicked(
NotificationEntry entry, int actionIndex, Notification.Action action,
boolean generatedByAssistant) {
- // TODO(b/204183781): get this from the current pipeline
- final int count = mEntryManager.getActiveNotificationsCount();
- final int rank = entry.getRanking().getRank();
- NotificationVisibility.NotificationLocation location =
- NotificationLogger.getNotificationLocation(entry);
- final NotificationVisibility nv = NotificationVisibility.obtain(
- entry.getKey(), rank, count, true, location);
+ final NotificationVisibility nv = mVisibilityProvider.obtain(entry, true);
mClickNotifier.onNotificationActionClick(
entry.getKey(), actionIndex, action, nv, generatedByAssistant);
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/connectivity/AccessPointController.kt b/packages/SystemUI/src/com/android/systemui/statusbar/connectivity/AccessPointController.kt
new file mode 100644
index 0000000..490994d
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/connectivity/AccessPointController.kt
@@ -0,0 +1,93 @@
+/*
+ * 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.statusbar.connectivity
+
+import android.content.Intent
+import android.os.UserManager
+import android.provider.Settings
+
+import com.android.wifitrackerlib.MergedCarrierEntry
+import com.android.wifitrackerlib.WifiEntry
+
+/**
+ * Tracks changes in access points. Allows listening for changes, scanning for new APs,
+ * and connecting to new ones.
+ */
+interface AccessPointController {
+ fun addAccessPointCallback(callback: AccessPointCallback)
+ fun removeAccessPointCallback(callback: AccessPointCallback)
+
+ /**
+ * Request an updated list of available access points
+ *
+ * This method will trigger a call to [AccessPointCallback.onAccessPointsChanged]
+ */
+ fun scanForAccessPoints()
+
+ /**
+ * Gets the current [MergedCarrierEntry]. If null, this call generates a call to
+ * [AccessPointCallback.onAccessPointsChanged]
+ *
+ * @return the current [MergedCarrierEntry], if one exists
+ */
+ fun getMergedCarrierEntry(): MergedCarrierEntry?
+
+ /** @return the appropriate icon id for the given [WifiEntry]'s level */
+ fun getIcon(ap: WifiEntry): Int
+
+ /**
+ * Connects to a [WifiEntry] if it's saved or does not require security.
+ *
+ * If the entry is not saved and requires security, will trigger
+ * [AccessPointCallback.onSettingsActivityTriggered].
+ *
+ * @param ap
+ * @return `true` if [AccessPointCallback.onSettingsActivityTriggered] is triggered
+ */
+ fun connect(ap: WifiEntry?): Boolean
+
+ /**
+ * `true` if the current user does not have the [UserManager.DISALLOW_CONFIG_WIFI] restriction
+ */
+ fun canConfigWifi(): Boolean
+
+ /**
+ * `true` if the current user does not have the [UserManager.DISALLOW_CONFIG_MOBILE_NETWORKS]
+ * restriction set
+ */
+ fun canConfigMobileData(): Boolean
+
+ interface AccessPointCallback {
+ /**
+ * Called whenever [scanForAccessPoints] is called, or [getMergedCarrierEntry] is called
+ * with a null entry
+ *
+ * @param accessPoints the list of available access points, including the current connected
+ * one if it exists
+ */
+ fun onAccessPointsChanged(accessPoints: List<@JvmSuppressWildcards WifiEntry>)
+
+ /**
+ * Called whenever [connecting][connect] to an unknown access point which has security.
+ * Implementers should launch the intent in the appropriate context
+ *
+ * @param settingsIntent an intent for [Settings.ACTION_WIFI_SETTINGS] with
+ * "wifi_start_connect_ssid" set as an extra
+ */
+ fun onSettingsActivityTriggered(settingsIntent: Intent?)
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/connectivity/AccessPointControllerImpl.java b/packages/SystemUI/src/com/android/systemui/statusbar/connectivity/AccessPointControllerImpl.java
index 828a129..7bf710c 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/connectivity/AccessPointControllerImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/connectivity/AccessPointControllerImpl.java
@@ -55,9 +55,9 @@
import javax.inject.Inject;
/** */
-public class AccessPointControllerImpl
- implements NetworkController.AccessPointController,
- WifiPickerTracker.WifiPickerTrackerCallback, LifecycleOwner {
+public class AccessPointControllerImpl implements AccessPointController,
+ WifiPickerTracker.WifiPickerTrackerCallback,
+ LifecycleOwner {
private static final String TAG = "AccessPointController";
private static final boolean DEBUG = Log.isLoggable(TAG, Log.DEBUG);
@@ -116,13 +116,11 @@
super.finalize();
}
- /** */
public boolean canConfigWifi() {
return !mUserManager.hasUserRestriction(UserManager.DISALLOW_CONFIG_WIFI,
new UserHandle(mCurrentUser));
}
- /** */
public boolean canConfigMobileData() {
return !mUserManager.hasUserRestriction(UserManager.DISALLOW_CONFIG_MOBILE_NETWORKS,
UserHandle.of(mCurrentUser)) && mUserTracker.getUserInfo().isAdmin();
@@ -155,7 +153,7 @@
@Override
public void scanForAccessPoints() {
if (mWifiPickerTracker == null) {
- fireAcccessPointsCallback(Collections.emptyList());
+ fireAccessPointsCallback(Collections.emptyList());
return;
}
List<WifiEntry> entries = mWifiPickerTracker.getWifiEntries();
@@ -163,13 +161,13 @@
if (connectedEntry != null) {
entries.add(0, connectedEntry);
}
- fireAcccessPointsCallback(entries);
+ fireAccessPointsCallback(entries);
}
@Override
public MergedCarrierEntry getMergedCarrierEntry() {
if (mWifiPickerTracker == null) {
- fireAcccessPointsCallback(Collections.emptyList());
+ fireAccessPointsCallback(Collections.emptyList());
return null;
}
return mWifiPickerTracker.getMergedCarrierEntry();
@@ -189,7 +187,7 @@
* @param ap
* @return {@code true} if {@link AccessPointCallback#onSettingsActivityTriggered} is triggered
*/
- public boolean connect(WifiEntry ap) {
+ public boolean connect(@Nullable WifiEntry ap) {
if (ap == null) return false;
if (DEBUG) {
if (ap.getWifiConfiguration() != null) {
@@ -221,7 +219,7 @@
}
}
- private void fireAcccessPointsCallback(List<WifiEntry> aps) {
+ private void fireAccessPointsCallback(List<WifiEntry> aps) {
for (AccessPointCallback callback : mCallbacks) {
callback.onAccessPointsChanged(aps);
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/connectivity/CallbackHandler.java b/packages/SystemUI/src/com/android/systemui/statusbar/connectivity/CallbackHandler.java
index 052a789..6914ae6 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/connectivity/CallbackHandler.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/connectivity/CallbackHandler.java
@@ -23,10 +23,6 @@
import com.android.internal.annotations.VisibleForTesting;
import com.android.systemui.dagger.qualifiers.Main;
import com.android.systemui.statusbar.connectivity.NetworkController.EmergencyListener;
-import com.android.systemui.statusbar.connectivity.NetworkController.IconState;
-import com.android.systemui.statusbar.connectivity.NetworkController.MobileDataIndicators;
-import com.android.systemui.statusbar.connectivity.NetworkController.SignalCallback;
-import com.android.systemui.statusbar.connectivity.NetworkController.WifiIndicators;
import java.io.PrintWriter;
import java.text.SimpleDateFormat;
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/connectivity/ConnectivityState.kt b/packages/SystemUI/src/com/android/systemui/statusbar/connectivity/ConnectivityState.kt
new file mode 100644
index 0000000..9c3c10c
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/connectivity/ConnectivityState.kt
@@ -0,0 +1,104 @@
+/*
+ * 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.statusbar.connectivity
+
+import android.annotation.SuppressLint
+import com.android.settingslib.SignalIcon.IconGroup
+import java.text.SimpleDateFormat
+
+/**
+ * Base type for various connectivity states, for use with [SignalController] and its subtypes
+ */
+open class ConnectivityState {
+ @JvmField var connected = false
+ @JvmField var enabled = false
+ @JvmField var activityIn = false
+ @JvmField var activityOut = false
+ @JvmField var level = 0
+ @JvmField var iconGroup: IconGroup? = null
+ @JvmField var inetCondition = 0
+ // Only for logging.
+ @JvmField var rssi = 0
+ // Not used for comparison, just used for logging.
+ @JvmField var time: Long = 0
+
+ override fun toString(): String {
+ return if (time != 0L) {
+ val builder = StringBuilder()
+ toString(builder)
+ builder.toString()
+ } else {
+ "Empty " + javaClass.simpleName
+ }
+ }
+
+ protected open fun copyFrom(other: ConnectivityState) {
+ connected = other.connected
+ enabled = other.enabled
+ activityIn = other.activityIn
+ activityOut = other.activityOut
+ level = other.level
+ iconGroup = other.iconGroup
+ inetCondition = other.inetCondition
+ rssi = other.rssi
+ time = other.time
+ }
+
+ protected open fun toString(builder: StringBuilder) {
+ builder.append("connected=$connected,")
+ .append("enabled=$enabled,")
+ .append("level=$level,")
+ .append("inetCondition=$inetCondition,")
+ .append("iconGroup=$iconGroup,")
+ .append("activityIn=$activityIn,")
+ .append("activityOut=$activityOut,")
+ .append("rssi=$rssi,")
+ .append("lastModified=${sSDF.format(time)}")
+ }
+
+ override fun equals(other: Any?): Boolean {
+ if (other == null) return false
+ if (other.javaClass != javaClass) return false
+
+ val o = other as ConnectivityState
+ return o.connected == connected &&
+ o.enabled == enabled &&
+ o.level == level &&
+ o.inetCondition == inetCondition &&
+ o.iconGroup === iconGroup &&
+ o.activityIn == activityIn &&
+ o.activityOut == activityOut &&
+ o.rssi == rssi
+ }
+
+ override fun hashCode(): Int {
+ var result = connected.hashCode()
+ result = 31 * result + enabled.hashCode()
+ result = 31 * result + activityIn.hashCode()
+ result = 31 * result + activityOut.hashCode()
+ result = 31 * result + level
+ result = 31 * result + (iconGroup?.hashCode() ?: 0)
+ result = 31 * result + inetCondition
+ result = 31 * result + rssi
+ result = 31 * result + time.hashCode()
+ return result
+ }
+}
+
+// No locale as it's only used for logging purposes
+@SuppressLint("SimpleDateFormat")
+private val sSDF = SimpleDateFormat("MM-dd HH:mm:ss.SSS")
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/connectivity/EthernetSignalController.java b/packages/SystemUI/src/com/android/systemui/statusbar/connectivity/EthernetSignalController.java
index c9d40ad..acd9779 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/connectivity/EthernetSignalController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/connectivity/EthernetSignalController.java
@@ -20,15 +20,12 @@
import com.android.settingslib.AccessibilityContentDescriptions;
import com.android.settingslib.SignalIcon.IconGroup;
-import com.android.settingslib.SignalIcon.State;
-import com.android.systemui.statusbar.connectivity.NetworkController.IconState;
-import com.android.systemui.statusbar.connectivity.NetworkController.SignalCallback;
import java.util.BitSet;
/** */
public class EthernetSignalController extends
- SignalController<State, IconGroup> {
+ SignalController<ConnectivityState, IconGroup> {
public EthernetSignalController(Context context,
CallbackHandler callbackHandler, NetworkControllerImpl networkController) {
@@ -68,7 +65,7 @@
}
@Override
- public State cleanState() {
- return new State();
+ public ConnectivityState cleanState() {
+ return new ConnectivityState();
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/connectivity/MobileSignalController.java b/packages/SystemUI/src/com/android/systemui/statusbar/connectivity/MobileSignalController.java
index 20ef4ee..9ae7ea2 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/connectivity/MobileSignalController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/connectivity/MobileSignalController.java
@@ -33,7 +33,6 @@
import android.telephony.SignalStrength;
import android.telephony.SubscriptionInfo;
import android.telephony.SubscriptionManager;
-import android.telephony.TelephonyDisplayInfo;
import android.telephony.TelephonyManager;
import android.telephony.ims.ImsException;
import android.telephony.ims.ImsMmTelManager;
@@ -47,8 +46,6 @@
import com.android.internal.annotations.VisibleForTesting;
import com.android.settingslib.AccessibilityContentDescriptions;
import com.android.settingslib.SignalIcon.MobileIconGroup;
-import com.android.settingslib.SignalIcon.MobileState;
-import com.android.settingslib.Utils;
import com.android.settingslib.graph.SignalDrawable;
import com.android.settingslib.mobile.MobileMappings.Config;
import com.android.settingslib.mobile.MobileStatusTracker;
@@ -58,9 +55,6 @@
import com.android.settingslib.net.SignalStrengthUtil;
import com.android.systemui.R;
import com.android.systemui.flags.FeatureFlags;
-import com.android.systemui.statusbar.connectivity.NetworkController.IconState;
-import com.android.systemui.statusbar.connectivity.NetworkController.MobileDataIndicators;
-import com.android.systemui.statusbar.connectivity.NetworkController.SignalCallback;
import com.android.systemui.util.CarrierConfigTracker;
import java.io.PrintWriter;
@@ -93,15 +87,6 @@
final SubscriptionInfo mSubscriptionInfo;
private Map<String, MobileIconGroup> mNetworkToIconLookup;
- // Since some pieces of the phone state are interdependent we store it locally,
- // this could potentially become part of MobileState for simplification/complication
- // of code.
- private int mDataState = TelephonyManager.DATA_DISCONNECTED;
- private TelephonyDisplayInfo mTelephonyDisplayInfo =
- new TelephonyDisplayInfo(TelephonyManager.NETWORK_TYPE_UNKNOWN,
- TelephonyDisplayInfo.OVERRIDE_NETWORK_TYPE_NONE);
- private ServiceState mServiceState;
- private SignalStrength mSignalStrength;
private int mLastLevel;
private MobileIconGroup mDefaultIcons;
private Config mConfig;
@@ -468,16 +453,8 @@
return new MobileState();
}
- private boolean isCdma() {
- return (mSignalStrength != null) && !mSignalStrength.isGsm();
- }
-
- public boolean isEmergencyOnly() {
- return (mServiceState != null && mServiceState.isEmergencyOnly());
- }
-
public boolean isInService() {
- return Utils.isInService(mServiceState);
+ return mCurrentState.isInService();
}
String getNetworkNameForCarrierWiFi() {
@@ -485,15 +462,15 @@
}
private boolean isRoaming() {
- // During a carrier change, roaming indications need to be supressed.
+ // During a carrier change, roaming indications need to be suppressed.
if (isCarrierNetworkChangeActive()) {
return false;
}
- if (isCdma()) {
+ if (mCurrentState.isCdma()) {
return mPhone.getCdmaEnhancedRoamingIndicatorDisplayNumber()
!= TelephonyManager.ERI_OFF;
} else {
- return mServiceState != null && mServiceState.getRoaming();
+ return mCurrentState.isRoaming();
}
}
@@ -585,27 +562,29 @@
}
private void updateMobileStatus(MobileStatus mobileStatus) {
- mCurrentState.activityIn = mobileStatus.activityIn;
- mCurrentState.activityOut = mobileStatus.activityOut;
- mCurrentState.dataSim = mobileStatus.dataSim;
- mCurrentState.carrierNetworkChangeMode = mobileStatus.carrierNetworkChangeMode;
- mDataState = mobileStatus.dataState;
+ int lastVoiceState = mCurrentState.getVoiceServiceState();
+ mCurrentState.setFromMobileStatus(mobileStatus);
+
notifyMobileLevelChangeIfNecessary(mobileStatus.signalStrength);
- mSignalStrength = mobileStatus.signalStrength;
- mTelephonyDisplayInfo = mobileStatus.telephonyDisplayInfo;
- int lastVoiceState = mServiceState != null ? mServiceState.getState() : -1;
- mServiceState = mobileStatus.serviceState;
- int currentVoiceState = mServiceState != null ? mServiceState.getState() : -1;
+ if (mProviderModelBehavior) {
+ maybeNotifyCallStateChanged(lastVoiceState);
+ }
+ }
+
+ /** Call state changed is only applicable when provider model behavior is true */
+ private void maybeNotifyCallStateChanged(int lastVoiceState) {
+ int currentVoiceState = mCurrentState.getVoiceServiceState();
+ if (lastVoiceState == currentVoiceState) {
+ return;
+ }
// Only update the no calling Status in the below scenarios
// 1. The first valid voice state has been received
// 2. The voice state has been changed and either the last or current state is
// ServiceState.STATE_IN_SERVICE
- if (mProviderModelBehavior
- && lastVoiceState != currentVoiceState
- && (lastVoiceState == -1
- || (lastVoiceState == ServiceState.STATE_IN_SERVICE
- || currentVoiceState == ServiceState.STATE_IN_SERVICE))) {
- boolean isNoCalling = currentVoiceState != ServiceState.STATE_IN_SERVICE;
+ if (lastVoiceState == -1
+ || (lastVoiceState == ServiceState.STATE_IN_SERVICE
+ || currentVoiceState == ServiceState.STATE_IN_SERVICE)) {
+ boolean isNoCalling = mCurrentState.isNoCalling();
isNoCalling &= !hideNoCalling();
IconState statusIcon = new IconState(isNoCalling,
R.drawable.ic_qs_no_calling_sms,
@@ -615,7 +594,7 @@
}
void updateNoCallingState() {
- int currentVoiceState = mServiceState != null ? mServiceState.getState() : -1;
+ int currentVoiceState = mCurrentState.getVoiceServiceState();
boolean isNoCalling = currentVoiceState != ServiceState.STATE_IN_SERVICE;
isNoCalling &= !hideNoCalling();
IconState statusIcon = new IconState(isNoCalling,
@@ -643,8 +622,7 @@
}
void refreshCallIndicator(SignalCallback callback) {
- boolean isNoCalling = mServiceState != null
- && mServiceState.getState() != ServiceState.STATE_IN_SERVICE;
+ boolean isNoCalling = mCurrentState.isNoCalling();
isNoCalling &= !hideNoCalling();
IconState statusIcon = new IconState(isNoCalling,
R.drawable.ic_qs_no_calling_sms,
@@ -736,30 +714,30 @@
}
/**
- * Updates the current state based on mServiceState, mSignalStrength, mDataState,
- * mTelephonyDisplayInfo, and mSimState. It should be called any time one of these is updated.
+ * Updates the current state based on ServiceState, SignalStrength, DataState,
+ * TelephonyDisplayInfo, and sim state. It should be called any time one of these is updated.
* This will call listeners if necessary.
*/
private void updateTelephony() {
if (Log.isLoggable(mTag, Log.DEBUG)) {
Log.d(mTag, "updateTelephonySignalStrength: hasService="
- + Utils.isInService(mServiceState) + " ss=" + mSignalStrength
- + " displayInfo=" + mTelephonyDisplayInfo);
+ + mCurrentState.isInService()
+ + " ss=" + mCurrentState.signalStrength
+ + " displayInfo=" + mCurrentState.telephonyDisplayInfo);
}
checkDefaultData();
- mCurrentState.connected = Utils.isInService(mServiceState) && mSignalStrength != null;
+ mCurrentState.connected = mCurrentState.isInService();
if (mCurrentState.connected) {
- mCurrentState.level = getSignalLevel(mSignalStrength);
+ mCurrentState.level = getSignalLevel(mCurrentState.signalStrength);
}
- String iconKey = getIconKey(mTelephonyDisplayInfo);
+ String iconKey = getIconKey(mCurrentState.telephonyDisplayInfo);
if (mNetworkToIconLookup.get(iconKey) != null) {
mCurrentState.iconGroup = mNetworkToIconLookup.get(iconKey);
} else {
mCurrentState.iconGroup = mDefaultIcons;
}
- mCurrentState.dataConnected = mCurrentState.connected
- && mDataState == TelephonyManager.DATA_CONNECTED;
+ mCurrentState.dataConnected = mCurrentState.isDataConnected();
mCurrentState.roaming = isRoaming();
if (isCarrierNetworkChangeActive()) {
@@ -771,20 +749,20 @@
mCurrentState.iconGroup = TelephonyIcons.DATA_DISABLED;
}
}
- if (isEmergencyOnly() != mCurrentState.isEmergency) {
- mCurrentState.isEmergency = isEmergencyOnly();
+ if (mCurrentState.isEmergencyOnly() != mCurrentState.isEmergency) {
+ mCurrentState.isEmergency = mCurrentState.isEmergencyOnly();
mNetworkController.recalculateEmergency();
}
// Fill in the network name if we think we have it.
- if (mCurrentState.networkName.equals(mNetworkNameDefault) && mServiceState != null
- && !TextUtils.isEmpty(mServiceState.getOperatorAlphaShort())) {
- mCurrentState.networkName = mServiceState.getOperatorAlphaShort();
+ if (mCurrentState.networkName.equals(mNetworkNameDefault)
+ && !TextUtils.isEmpty(mCurrentState.getOperatorAlphaShort())) {
+ mCurrentState.networkName = mCurrentState.getOperatorAlphaShort();
}
// If this is the data subscription, update the currentState data name
- if (mCurrentState.networkNameData.equals(mNetworkNameDefault) && mServiceState != null
+ if (mCurrentState.networkNameData.equals(mNetworkNameDefault)
&& mCurrentState.dataSim
- && !TextUtils.isEmpty(mServiceState.getOperatorAlphaShort())) {
- mCurrentState.networkNameData = mServiceState.getOperatorAlphaShort();
+ && !TextUtils.isEmpty(mCurrentState.getOperatorAlphaShort())) {
+ mCurrentState.networkNameData = mCurrentState.getOperatorAlphaShort();
}
notifyListenersIfNecessary();
@@ -836,10 +814,6 @@
pw.println(" mSubscription=" + mSubscriptionInfo + ",");
pw.println(" mProviderModelSetting=" + mProviderModelSetting + ",");
pw.println(" mProviderModelBehavior=" + mProviderModelBehavior + ",");
- pw.println(" mServiceState=" + mServiceState + ",");
- pw.println(" mSignalStrength=" + mSignalStrength + ",");
- pw.println(" mTelephonyDisplayInfo=" + mTelephonyDisplayInfo + ",");
- pw.println(" mDataState=" + mDataState + ",");
pw.println(" mInflateSignalStrengths=" + mInflateSignalStrengths + ",");
pw.println(" isDataDisabled=" + isDataDisabled() + ",");
pw.println(" mNetworkToIconLookup=" + mNetworkToIconLookup + ",");
@@ -884,5 +858,4 @@
icon = iconState;
}
}
-
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/connectivity/MobileState.kt b/packages/SystemUI/src/com/android/systemui/statusbar/connectivity/MobileState.kt
new file mode 100644
index 0000000..8a3b006
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/connectivity/MobileState.kt
@@ -0,0 +1,225 @@
+/*
+ * 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.statusbar.connectivity
+
+import android.telephony.ServiceState
+import android.telephony.SignalStrength
+import android.telephony.TelephonyDisplayInfo
+import android.telephony.TelephonyManager
+import com.android.settingslib.Utils
+import com.android.settingslib.mobile.MobileStatusTracker.MobileStatus
+import com.android.settingslib.mobile.TelephonyIcons
+import java.lang.IllegalArgumentException
+
+/**
+ * Box for all policy-related state used in [MobileSignalController]
+ */
+internal class MobileState(
+ @JvmField var networkName: String? = null,
+ @JvmField var networkNameData: String? = null,
+ @JvmField var dataSim: Boolean = false,
+ @JvmField var dataConnected: Boolean = false,
+ @JvmField var isEmergency: Boolean = false,
+ @JvmField var airplaneMode: Boolean = false,
+ @JvmField var carrierNetworkChangeMode: Boolean = false,
+ @JvmField var isDefault: Boolean = false,
+ @JvmField var userSetup: Boolean = false,
+ @JvmField var roaming: Boolean = false,
+ @JvmField var dataState: Int = TelephonyManager.DATA_DISCONNECTED,
+ // Tracks the on/off state of the defaultDataSubscription
+ @JvmField var defaultDataOff: Boolean = false
+) : ConnectivityState() {
+
+ @JvmField var telephonyDisplayInfo = TelephonyDisplayInfo(TelephonyManager.NETWORK_TYPE_UNKNOWN,
+ TelephonyDisplayInfo.OVERRIDE_NETWORK_TYPE_NONE)
+ @JvmField var serviceState: ServiceState? = null
+ @JvmField var signalStrength: SignalStrength? = null
+
+ /** @return true if this state is disabled or not default data */
+ val isDataDisabledOrNotDefault: Boolean
+ get() = (iconGroup === TelephonyIcons.DATA_DISABLED ||
+ iconGroup === TelephonyIcons.NOT_DEFAULT_DATA) && userSetup
+
+ /** @return if this state is considered to have inbound activity */
+ fun hasActivityIn(): Boolean {
+ return dataConnected && !carrierNetworkChangeMode && activityIn
+ }
+
+ /** @return if this state is considered to have outbound activity */
+ fun hasActivityOut(): Boolean {
+ return dataConnected && !carrierNetworkChangeMode && activityOut
+ }
+
+ /** @return true if this state should show a RAT icon in quick settings */
+ fun showQuickSettingsRatIcon(): Boolean {
+ return dataConnected || isDataDisabledOrNotDefault
+ }
+
+ override fun copyFrom(other: ConnectivityState) {
+ val o = other as? MobileState ?: throw IllegalArgumentException(
+ "MobileState can only update from another MobileState")
+
+ super.copyFrom(o)
+ networkName = o.networkName
+ networkNameData = o.networkNameData
+ dataSim = o.dataSim
+ dataConnected = o.dataConnected
+ isEmergency = o.isEmergency
+ airplaneMode = o.airplaneMode
+ carrierNetworkChangeMode = o.carrierNetworkChangeMode
+ isDefault = o.isDefault
+ userSetup = o.userSetup
+ roaming = o.roaming
+ dataState = o.dataState
+ defaultDataOff = o.defaultDataOff
+
+ telephonyDisplayInfo = o.telephonyDisplayInfo
+ serviceState = o.serviceState
+ signalStrength = o.signalStrength
+ }
+
+ fun isDataConnected(): Boolean {
+ return connected && dataState == TelephonyManager.DATA_CONNECTED
+ }
+
+ /** @return the current voice service state, or -1 if null */
+ fun getVoiceServiceState(): Int {
+ return serviceState?.state ?: -1
+ }
+
+ fun isNoCalling(): Boolean {
+ return serviceState?.state != ServiceState.STATE_IN_SERVICE
+ }
+
+ fun getOperatorAlphaShort(): String {
+ return serviceState?.operatorAlphaShort ?: ""
+ }
+
+ fun isCdma(): Boolean {
+ return signalStrength != null && !signalStrength!!.isGsm
+ }
+
+ fun isEmergencyOnly(): Boolean {
+ return serviceState != null && serviceState!!.isEmergencyOnly
+ }
+
+ fun isInService(): Boolean {
+ return Utils.isInService(serviceState)
+ }
+
+ fun isRoaming(): Boolean {
+ return serviceState != null && serviceState!!.roaming
+ }
+
+ fun setFromMobileStatus(mobileStatus: MobileStatus) {
+ activityIn = mobileStatus.activityIn
+ activityOut = mobileStatus.activityOut
+ dataSim = mobileStatus.dataSim
+ carrierNetworkChangeMode = mobileStatus.carrierNetworkChangeMode
+ dataState = mobileStatus.dataState
+ signalStrength = mobileStatus.signalStrength
+ telephonyDisplayInfo = mobileStatus.telephonyDisplayInfo
+ serviceState = mobileStatus.serviceState
+ }
+
+ override fun toString(builder: StringBuilder) {
+ super.toString(builder)
+ builder.append(',')
+ builder.append("dataSim=$dataSim,")
+ builder.append("networkName=$networkName,")
+ builder.append("networkNameData=$networkNameData,")
+ builder.append("dataConnected=$dataConnected,")
+ builder.append("roaming=$roaming,")
+ builder.append("isDefault=$isDefault,")
+ builder.append("isEmergency=$isEmergency,")
+ builder.append("airplaneMode=$airplaneMode,")
+ builder.append("carrierNetworkChangeMode=$carrierNetworkChangeMode,")
+ builder.append("userSetup=$userSetup,")
+ builder.append("dataState=$dataState,")
+ builder.append("defaultDataOff=$defaultDataOff,")
+
+ // Computed properties
+ builder.append("showQuickSettingsRatIcon=${showQuickSettingsRatIcon()},")
+ builder.append("voiceServiceState=${getVoiceServiceState()},")
+ builder.append("isInService=${isInService()},")
+
+ builder.append("serviceState=${serviceState?.minLog() ?: "(null)"},")
+ builder.append("signalStrength=${signalStrength?.minLog() ?: "(null)"},")
+ builder.append("displayInfo=$telephonyDisplayInfo")
+ }
+
+ override fun equals(other: Any?): Boolean {
+ if (this === other) return true
+ if (javaClass != other?.javaClass) return false
+ if (!super.equals(other)) return false
+
+ other as MobileState
+
+ if (networkName != other.networkName) return false
+ if (networkNameData != other.networkNameData) return false
+ if (dataSim != other.dataSim) return false
+ if (dataConnected != other.dataConnected) return false
+ if (isEmergency != other.isEmergency) return false
+ if (airplaneMode != other.airplaneMode) return false
+ if (carrierNetworkChangeMode != other.carrierNetworkChangeMode) return false
+ if (isDefault != other.isDefault) return false
+ if (userSetup != other.userSetup) return false
+ if (roaming != other.roaming) return false
+ if (dataState != other.dataState) return false
+ if (defaultDataOff != other.defaultDataOff) return false
+ if (telephonyDisplayInfo != other.telephonyDisplayInfo) return false
+ if (serviceState != other.serviceState) return false
+ if (signalStrength != other.signalStrength) return false
+
+ return true
+ }
+
+ override fun hashCode(): Int {
+ var result = super.hashCode()
+ result = 31 * result + (networkName?.hashCode() ?: 0)
+ result = 31 * result + (networkNameData?.hashCode() ?: 0)
+ result = 31 * result + dataSim.hashCode()
+ result = 31 * result + dataConnected.hashCode()
+ result = 31 * result + isEmergency.hashCode()
+ result = 31 * result + airplaneMode.hashCode()
+ result = 31 * result + carrierNetworkChangeMode.hashCode()
+ result = 31 * result + isDefault.hashCode()
+ result = 31 * result + userSetup.hashCode()
+ result = 31 * result + roaming.hashCode()
+ result = 31 * result + dataState
+ result = 31 * result + defaultDataOff.hashCode()
+ result = 31 * result + telephonyDisplayInfo.hashCode()
+ result = 31 * result + (serviceState?.hashCode() ?: 0)
+ result = 31 * result + (signalStrength?.hashCode() ?: 0)
+ return result
+ }
+}
+
+/** toString() is a little more verbose than we need. Just log the fields we read */
+private fun ServiceState.minLog(): String {
+ return "serviceState={" +
+ "state=$state," +
+ "isEmergencyOnly=$isEmergencyOnly," +
+ "roaming=$roaming," +
+ "operatorNameAlphaShort=$operatorAlphaShort}"
+}
+
+private fun SignalStrength.minLog(): String {
+ return "signalStrength={" +
+ "isGsm=$isGsm," +
+ "level=$level}"
+}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/connectivity/NetworkController.java b/packages/SystemUI/src/com/android/systemui/statusbar/connectivity/NetworkController.java
index 1433096..f960eb7 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/connectivity/NetworkController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/connectivity/NetworkController.java
@@ -16,19 +16,10 @@
package com.android.systemui.statusbar.connectivity;
-import android.content.Context;
-import android.content.Intent;
-import android.telephony.SubscriptionInfo;
-
import com.android.settingslib.net.DataUsageController;
import com.android.systemui.demomode.DemoMode;
-import com.android.systemui.statusbar.connectivity.NetworkController.SignalCallback;
import com.android.systemui.statusbar.policy.CallbackController;
import com.android.systemui.statusbar.policy.DataSaverController;
-import com.android.wifitrackerlib.MergedCarrierEntry;
-import com.android.wifitrackerlib.WifiEntry;
-
-import java.util.List;
/** */
public interface NetworkController extends CallbackController<SignalCallback>, DemoMode {
@@ -62,197 +53,8 @@
/** */
boolean isRadioOn();
- /**
- * Wrapper class for all the WiFi signals used for WiFi indicators.
- */
- final class WifiIndicators {
- public boolean enabled;
- public IconState statusIcon;
- public IconState qsIcon;
- public boolean activityIn;
- public boolean activityOut;
- public String description;
- public boolean isTransient;
- public String statusLabel;
-
- public WifiIndicators(boolean enabled, IconState statusIcon, IconState qsIcon,
- boolean activityIn, boolean activityOut, String description,
- boolean isTransient, String statusLabel) {
- this.enabled = enabled;
- this.statusIcon = statusIcon;
- this.qsIcon = qsIcon;
- this.activityIn = activityIn;
- this.activityOut = activityOut;
- this.description = description;
- this.isTransient = isTransient;
- this.statusLabel = statusLabel;
- }
-
- @Override
- public String toString() {
- return new StringBuilder("WifiIndicators[")
- .append("enabled=").append(enabled)
- .append(",statusIcon=").append(statusIcon == null ? "" : statusIcon.toString())
- .append(",qsIcon=").append(qsIcon == null ? "" : qsIcon.toString())
- .append(",activityIn=").append(activityIn)
- .append(",activityOut=").append(activityOut)
- .append(",qsDescription=").append(description)
- .append(",isTransient=").append(isTransient)
- .append(",statusLabel=").append(statusLabel)
- .append(']').toString();
- }
- }
-
- /**
- * Wrapper class for all the mobile signals used for mobile data indicators.
- */
- final class MobileDataIndicators {
- public IconState statusIcon;
- public IconState qsIcon;
- public int statusType;
- public int qsType;
- public boolean activityIn;
- public boolean activityOut;
- public CharSequence typeContentDescription;
- public CharSequence typeContentDescriptionHtml;
- public CharSequence qsDescription;
- public int subId;
- public boolean roaming;
- public boolean showTriangle;
-
- public MobileDataIndicators(IconState statusIcon, IconState qsIcon, int statusType,
- int qsType, boolean activityIn, boolean activityOut,
- CharSequence typeContentDescription, CharSequence typeContentDescriptionHtml,
- CharSequence qsDescription, int subId, boolean roaming,
- boolean showTriangle) {
- this.statusIcon = statusIcon;
- this.qsIcon = qsIcon;
- this.statusType = statusType;
- this.qsType = qsType;
- this.activityIn = activityIn;
- this.activityOut = activityOut;
- this.typeContentDescription = typeContentDescription;
- this.typeContentDescriptionHtml = typeContentDescriptionHtml;
- this.qsDescription = qsDescription;
- this.subId = subId;
- this.roaming = roaming;
- this.showTriangle = showTriangle;
- }
-
- @Override
- public String toString() {
- return new StringBuilder("MobileDataIndicators[")
- .append("statusIcon=").append(statusIcon == null ? "" : statusIcon.toString())
- .append(",qsIcon=").append(qsIcon == null ? "" : qsIcon.toString())
- .append(",statusType=").append(statusType)
- .append(",qsType=").append(qsType)
- .append(",activityIn=").append(activityIn)
- .append(",activityOut=").append(activityOut)
- .append(",typeContentDescription=").append(typeContentDescription)
- .append(",typeContentDescriptionHtml=").append(typeContentDescriptionHtml)
- .append(",description=").append(qsDescription)
- .append(",subId=").append(subId)
- .append(",roaming=").append(roaming)
- .append(",showTriangle=").append(showTriangle)
- .append(']').toString();
- }
- }
-
- /** */
- interface SignalCallback {
- /**
- * Callback for listeners to be able to update the state of any UI tracking connectivity of
- * WiFi networks.
- */
- default void setWifiIndicators(WifiIndicators wifiIndicators) {}
-
- /**
- * Callback for listeners to be able to update the state of any UI tracking connectivity
- * of Mobile networks.
- */
- default void setMobileDataIndicators(MobileDataIndicators mobileDataIndicators) {}
-
- /** */
- default void setSubs(List<SubscriptionInfo> subs) {}
-
- /** */
- default void setNoSims(boolean show, boolean simDetected) {}
-
- /** */
- default void setEthernetIndicators(IconState icon) {}
-
- /** */
- default void setIsAirplaneMode(IconState icon) {}
-
- /** */
- default void setMobileDataEnabled(boolean enabled) {}
-
- /**
- * Callback for listeners to be able to update the connectivity status
- * @param noDefaultNetwork whether there is any default network.
- * @param noValidatedNetwork whether there is any validated network.
- * @param noNetworksAvailable whether there is any WiFi networks available.
- */
- default void setConnectivityStatus(boolean noDefaultNetwork, boolean noValidatedNetwork,
- boolean noNetworksAvailable) {}
-
- /**
- * Callback for listeners to be able to update the call indicator
- * @param statusIcon the icon for the call indicator
- * @param subId subscription ID for which to update the UI
- */
- default void setCallIndicator(IconState statusIcon, int subId) {}
- }
-
/** */
interface EmergencyListener {
void setEmergencyCallsOnly(boolean emergencyOnly);
}
-
- /** */
- class IconState {
- public final boolean visible;
- public final int icon;
- public final String contentDescription;
-
- public IconState(boolean visible, int icon, String contentDescription) {
- this.visible = visible;
- this.icon = icon;
- this.contentDescription = contentDescription;
- }
-
- public IconState(boolean visible, int icon, int contentDescription,
- Context context) {
- this(visible, icon, context.getString(contentDescription));
- }
-
- @Override
- public String toString() {
- StringBuilder builder = new StringBuilder();
- return builder.append("[visible=").append(visible).append(',')
- .append("icon=").append(icon).append(',')
- .append("contentDescription=").append(contentDescription).append(']')
- .toString();
- }
- }
-
- /**
- * Tracks changes in access points. Allows listening for changes, scanning for new APs,
- * and connecting to new ones.
- */
- interface AccessPointController {
- void addAccessPointCallback(AccessPointCallback callback);
- void removeAccessPointCallback(AccessPointCallback callback);
- void scanForAccessPoints();
- MergedCarrierEntry getMergedCarrierEntry();
- int getIcon(WifiEntry ap);
- boolean connect(WifiEntry ap);
- boolean canConfigWifi();
- boolean canConfigMobileData();
-
- interface AccessPointCallback {
- void onAccessPointsChanged(List<WifiEntry> accessPoints);
- void onSettingsActivityTriggered(Intent settingsIntent);
- }
- }
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/connectivity/NetworkControllerImpl.java b/packages/SystemUI/src/com/android/systemui/statusbar/connectivity/NetworkControllerImpl.java
index a4a648e..26780eb 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/connectivity/NetworkControllerImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/connectivity/NetworkControllerImpl.java
@@ -721,8 +721,11 @@
@Override
public void addCallback(@NonNull SignalCallback cb) {
cb.setSubs(mCurrentSubscriptions);
- cb.setIsAirplaneMode(new IconState(mAirplaneMode,
- TelephonyIcons.FLIGHT_MODE_ICON, R.string.accessibility_airplane_mode, mContext));
+ cb.setIsAirplaneMode(
+ new IconState(
+ mAirplaneMode,
+ TelephonyIcons.FLIGHT_MODE_ICON,
+ mContext.getString(R.string.accessibility_airplane_mode)));
cb.setNoSims(mHasNoSubs, mSimDetected);
if (mProviderModelSetting) {
cb.setConnectivityStatus(mNoDefaultNetwork, !mInetCondition, mNoNetworksAvailable);
@@ -1056,8 +1059,11 @@
* notifyAllListeners.
*/
private void notifyListeners() {
- mCallbackHandler.setIsAirplaneMode(new IconState(mAirplaneMode,
- TelephonyIcons.FLIGHT_MODE_ICON, R.string.accessibility_airplane_mode, mContext));
+ mCallbackHandler.setIsAirplaneMode(
+ new IconState(
+ mAirplaneMode,
+ TelephonyIcons.FLIGHT_MODE_ICON,
+ mContext.getString(R.string.accessibility_airplane_mode)));
mCallbackHandler.setNoSims(mHasNoSubs, mSimDetected);
}
@@ -1206,7 +1212,7 @@
}
private boolean mDemoInetCondition;
- private WifiSignalController.WifiState mDemoWifiState;
+ private WifiState mDemoWifiState;
@Override
public void onDemoModeStarted() {
@@ -1241,9 +1247,11 @@
String airplane = args.getString("airplane");
if (airplane != null) {
boolean show = airplane.equals("show");
- mCallbackHandler.setIsAirplaneMode(new IconState(show,
- TelephonyIcons.FLIGHT_MODE_ICON, R.string.accessibility_airplane_mode,
- mContext));
+ mCallbackHandler.setIsAirplaneMode(
+ new IconState(
+ show,
+ TelephonyIcons.FLIGHT_MODE_ICON,
+ mContext.getString(R.string.accessibility_airplane_mode)));
}
String fully = args.getString("fully");
if (fully != null) {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/connectivity/SignalCallback.kt b/packages/SystemUI/src/com/android/systemui/statusbar/connectivity/SignalCallback.kt
new file mode 100644
index 0000000..599beec
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/connectivity/SignalCallback.kt
@@ -0,0 +1,187 @@
+/*
+ * 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.statusbar.connectivity
+
+import android.telephony.SubscriptionInfo
+
+/**
+ * SignalCallback contains all of the connectivity updates from [NetworkController]. Implement this
+ * interface to be able to draw iconography for Wi-Fi, mobile data, ethernet, call strength
+ * indicators, etc.
+ */
+interface SignalCallback {
+ /**
+ * Called when the Wi-Fi iconography has been updated. Implement this method to draw Wi-Fi icons
+ *
+ * @param wifiIndicators a box type containing enough information to properly draw a Wi-Fi icon
+ */
+ @JvmDefault
+ fun setWifiIndicators(wifiIndicators: WifiIndicators) {}
+
+ /**
+ * Called when the mobile iconography has been updated. Implement this method to draw mobile
+ * indicators
+ *
+ * @param mobileDataIndicators a box type containing enough information to properly draw
+ * mobile data icons
+ *
+ * NOTE: phones can have multiple subscriptions, so this [mobileDataIndicators] object should be
+ * indexed based on its [subId][MobileDataIndicators.subId]
+ */
+ @JvmDefault
+ fun setMobileDataIndicators(mobileDataIndicators: MobileDataIndicators) {}
+
+ /**
+ * Called when the list of mobile data subscriptions has changed. Use this method as a chance
+ * to remove views that are no longer needed, or to make room for new icons to come in
+ *
+ * @param subs a [SubscriptionInfo] for each subscription that we know about
+ */
+ @JvmDefault
+ fun setSubs(subs: List<@JvmSuppressWildcards SubscriptionInfo>) {}
+
+ /**
+ * Called when:
+ * 1. The number of [MobileSignalController]s goes to 0 while mobile data is enabled
+ * OR
+ * 2. The presence of any SIM changes
+ *
+ * @param show whether or not to show a "no sim" view
+ * @param simDetected whether any SIM is detected or not
+ */
+ @JvmDefault
+ fun setNoSims(show: Boolean, simDetected: Boolean) {}
+
+ /**
+ * Called when there is any update to the ethernet iconography. Implement this method to set an
+ * ethernet icon
+ *
+ * @param icon an [IconState] for the current ethernet status
+ */
+ @JvmDefault
+ fun setEthernetIndicators(icon: IconState) {}
+
+ /**
+ * Called whenever airplane mode changes
+ *
+ * @param icon an [IconState] for the current airplane mode status
+ */
+ @JvmDefault
+ fun setIsAirplaneMode(icon: IconState) {}
+
+ /**
+ * Called whenever the mobile data feature enabled state changes
+ *
+ * @param enabled the current mobile data feature ennabled state
+ */
+ @JvmDefault
+ fun setMobileDataEnabled(enabled: Boolean) {}
+
+ /**
+ * Callback for listeners to be able to update the connectivity status
+ * @param noDefaultNetwork whether there is any default network.
+ * @param noValidatedNetwork whether there is any validated network.
+ * @param noNetworksAvailable whether there is any WiFi networks available.
+ */
+ @JvmDefault
+ fun setConnectivityStatus(
+ noDefaultNetwork: Boolean,
+ noValidatedNetwork: Boolean,
+ noNetworksAvailable: Boolean
+ ) { }
+
+ /**
+ * Callback for listeners to be able to update the call indicator
+ * @param statusIcon the icon for the call indicator
+ * @param subId subscription ID for which to update the UI
+ */
+ @JvmDefault
+ fun setCallIndicator(statusIcon: IconState, subId: Int) {}
+}
+
+/** Box type for [SignalCallback.setWifiIndicators] */
+data class WifiIndicators(
+ @JvmField val enabled: Boolean,
+ @JvmField val statusIcon: IconState?,
+ @JvmField val qsIcon: IconState?,
+ @JvmField val activityIn: Boolean,
+ @JvmField val activityOut: Boolean,
+ @JvmField val description: String?,
+ @JvmField val isTransient: Boolean,
+ @JvmField val statusLabel: String?
+) {
+ override fun toString(): String {
+ return StringBuilder("WifiIndicators[")
+ .append("enabled=").append(enabled)
+ .append(",statusIcon=").append(statusIcon?.toString() ?: "")
+ .append(",qsIcon=").append(qsIcon?.toString() ?: "")
+ .append(",activityIn=").append(activityIn)
+ .append(",activityOut=").append(activityOut)
+ .append(",qsDescription=").append(description)
+ .append(",isTransient=").append(isTransient)
+ .append(",statusLabel=").append(statusLabel)
+ .append(']').toString()
+ }
+}
+
+/** Box type for [SignalCallback.setMobileDataIndicators] */
+data class MobileDataIndicators(
+ @JvmField val statusIcon: IconState?,
+ @JvmField val qsIcon: IconState?,
+ @JvmField val statusType: Int,
+ @JvmField val qsType: Int,
+ @JvmField val activityIn: Boolean,
+ @JvmField val activityOut: Boolean,
+ @JvmField val typeContentDescription: CharSequence?,
+ @JvmField val typeContentDescriptionHtml: CharSequence?,
+ @JvmField val qsDescription: CharSequence?,
+ @JvmField val subId: Int,
+ @JvmField val roaming: Boolean,
+ @JvmField val showTriangle: Boolean
+) {
+ override fun toString(): String {
+ return java.lang.StringBuilder("MobileDataIndicators[")
+ .append("statusIcon=").append(statusIcon?.toString() ?: "")
+ .append(",qsIcon=").append(qsIcon?.toString() ?: "")
+ .append(",statusType=").append(statusType)
+ .append(",qsType=").append(qsType)
+ .append(",activityIn=").append(activityIn)
+ .append(",activityOut=").append(activityOut)
+ .append(",typeContentDescription=").append(typeContentDescription)
+ .append(",typeContentDescriptionHtml=").append(typeContentDescriptionHtml)
+ .append(",description=").append(qsDescription)
+ .append(",subId=").append(subId)
+ .append(",roaming=").append(roaming)
+ .append(",showTriangle=").append(showTriangle)
+ .append(']').toString()
+ }
+}
+
+/** Box for an icon with its visibility and content description */
+data class IconState(
+ @JvmField val visible: Boolean,
+ @JvmField val icon: Int,
+ @JvmField val contentDescription: String
+) {
+ override fun toString(): String {
+ val builder = java.lang.StringBuilder()
+ return builder.append("[visible=").append(visible).append(',')
+ .append("icon=").append(icon).append(',')
+ .append("contentDescription=").append(contentDescription).append(']')
+ .toString()
+ }
+}
\ No newline at end of file
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/connectivity/SignalController.java b/packages/SystemUI/src/com/android/systemui/statusbar/connectivity/SignalController.java
index d23dba5..cd20068 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/connectivity/SignalController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/connectivity/SignalController.java
@@ -22,9 +22,6 @@
import android.util.Log;
import com.android.settingslib.SignalIcon.IconGroup;
-import com.android.settingslib.SignalIcon.State;
-import com.android.systemui.statusbar.connectivity.NetworkController.IconState;
-import com.android.systemui.statusbar.connectivity.NetworkController.SignalCallback;
import java.io.PrintWriter;
import java.util.BitSet;
@@ -36,7 +33,7 @@
* @param <T> State of the SysUI controller.
* @param <I> Icon groups of the SysUI controller for a given State.
*/
-public abstract class SignalController<T extends State, I extends IconGroup> {
+public abstract class SignalController<T extends ConnectivityState, I extends IconGroup> {
// Save the previous SignalController.States of all SignalControllers for dumps.
static final boolean RECORD_HISTORY = true;
// If RECORD_HISTORY how many to save, must be a power of 2.
@@ -58,7 +55,7 @@
private final CallbackHandler mCallbackHandler;
// Save the previous HISTORY_SIZE states for logging.
- private final State[] mHistory;
+ private final ConnectivityState[] mHistory;
// Where to copy the next state into.
private int mHistoryIndex;
@@ -72,7 +69,7 @@
mCurrentState = cleanState();
mLastState = cleanState();
if (RECORD_HISTORY) {
- mHistory = new State[HISTORY_SIZE];
+ mHistory = new ConnectivityState[HISTORY_SIZE];
for (int i = 0; i < HISTORY_SIZE; i++) {
mHistory[i] = cleanState();
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/connectivity/WifiSignalController.java b/packages/SystemUI/src/com/android/systemui/statusbar/connectivity/WifiSignalController.java
index 3622a66..103ca0e 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/connectivity/WifiSignalController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/connectivity/WifiSignalController.java
@@ -26,28 +26,20 @@
import android.net.NetworkScoreManager;
import android.net.wifi.WifiManager;
import android.text.Html;
-import android.text.TextUtils;
import com.android.internal.annotations.VisibleForTesting;
import com.android.settingslib.SignalIcon.IconGroup;
import com.android.settingslib.SignalIcon.MobileIconGroup;
-import com.android.settingslib.SignalIcon.State;
import com.android.settingslib.graph.SignalDrawable;
import com.android.settingslib.mobile.TelephonyIcons;
import com.android.settingslib.wifi.WifiStatusTracker;
import com.android.systemui.R;
import com.android.systemui.flags.FeatureFlags;
-import com.android.systemui.statusbar.connectivity.NetworkController.IconState;
-import com.android.systemui.statusbar.connectivity.NetworkController.MobileDataIndicators;
-import com.android.systemui.statusbar.connectivity.NetworkController.SignalCallback;
-import com.android.systemui.statusbar.connectivity.NetworkController.WifiIndicators;
import java.io.PrintWriter;
-import java.util.Objects;
/** */
-public class WifiSignalController extends
- SignalController<WifiSignalController.WifiState, IconGroup> {
+public class WifiSignalController extends SignalController<WifiState, IconGroup> {
private final boolean mHasMobileDataFeature;
private final WifiStatusTracker mWifiTracker;
private final IconGroup mUnmergedWifiIconGroup = WifiIcons.UNMERGED_WIFI;
@@ -272,50 +264,4 @@
setActivity(state);
}
}
-
- static class WifiState extends State {
- public String ssid;
- public boolean isTransient;
- public boolean isDefault;
- public String statusLabel;
- public boolean isCarrierMerged;
- public int subId;
-
- @Override
- public void copyFrom(State s) {
- super.copyFrom(s);
- WifiState state = (WifiState) s;
- ssid = state.ssid;
- isTransient = state.isTransient;
- isDefault = state.isDefault;
- statusLabel = state.statusLabel;
- isCarrierMerged = state.isCarrierMerged;
- subId = state.subId;
- }
-
- @Override
- protected void toString(StringBuilder builder) {
- super.toString(builder);
- builder.append(",ssid=").append(ssid)
- .append(",isTransient=").append(isTransient)
- .append(",isDefault=").append(isDefault)
- .append(",statusLabel=").append(statusLabel)
- .append(",isCarrierMerged=").append(isCarrierMerged)
- .append(",subId=").append(subId);
- }
-
- @Override
- public boolean equals(Object o) {
- if (!super.equals(o)) {
- return false;
- }
- WifiState other = (WifiState) o;
- return Objects.equals(other.ssid, ssid)
- && other.isTransient == isTransient
- && other.isDefault == isDefault
- && TextUtils.equals(other.statusLabel, statusLabel)
- && other.isCarrierMerged == isCarrierMerged
- && other.subId == subId;
- }
- }
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/connectivity/WifiState.kt b/packages/SystemUI/src/com/android/systemui/statusbar/connectivity/WifiState.kt
new file mode 100644
index 0000000..ac15f78
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/connectivity/WifiState.kt
@@ -0,0 +1,78 @@
+/*
+ * 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.statusbar.connectivity
+
+import java.lang.StringBuilder
+
+internal class WifiState(
+ @JvmField var ssid: String? = null,
+ @JvmField var isTransient: Boolean = false,
+ @JvmField var isDefault: Boolean = false,
+ @JvmField var statusLabel: String? = null,
+ @JvmField var isCarrierMerged: Boolean = false,
+ @JvmField var subId: Int = 0
+) : ConnectivityState() {
+
+ public override fun copyFrom(s: ConnectivityState) {
+ super.copyFrom(s)
+ val state = s as WifiState
+ ssid = state.ssid
+ isTransient = state.isTransient
+ isDefault = state.isDefault
+ statusLabel = state.statusLabel
+ isCarrierMerged = state.isCarrierMerged
+ subId = state.subId
+ }
+
+ override fun toString(builder: StringBuilder) {
+ super.toString(builder)
+ builder.append(",ssid=").append(ssid)
+ .append(",isTransient=").append(isTransient)
+ .append(",isDefault=").append(isDefault)
+ .append(",statusLabel=").append(statusLabel)
+ .append(",isCarrierMerged=").append(isCarrierMerged)
+ .append(",subId=").append(subId)
+ }
+
+ override fun equals(other: Any?): Boolean {
+ if (this === other) return true
+ if (javaClass != other?.javaClass) return false
+ if (!super.equals(other)) return false
+
+ other as WifiState
+
+ if (ssid != other.ssid) return false
+ if (isTransient != other.isTransient) return false
+ if (isDefault != other.isDefault) return false
+ if (statusLabel != other.statusLabel) return false
+ if (isCarrierMerged != other.isCarrierMerged) return false
+ if (subId != other.subId) return false
+
+ return true
+ }
+
+ override fun hashCode(): Int {
+ var result = super.hashCode()
+ result = 31 * result + (ssid?.hashCode() ?: 0)
+ result = 31 * result + isTransient.hashCode()
+ result = 31 * result + isDefault.hashCode()
+ result = 31 * result + (statusLabel?.hashCode() ?: 0)
+ result = 31 * result + isCarrierMerged.hashCode()
+ result = 31 * result + subId
+ return result
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/dagger/StatusBarDependenciesModule.java b/packages/SystemUI/src/com/android/systemui/statusbar/dagger/StatusBarDependenciesModule.java
index 74ea19f..aa86daa 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/dagger/StatusBarDependenciesModule.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/dagger/StatusBarDependenciesModule.java
@@ -59,6 +59,7 @@
import com.android.systemui.statusbar.notification.collection.legacy.NotificationGroupManagerLegacy;
import com.android.systemui.statusbar.notification.collection.legacy.VisualStabilityManager;
import com.android.systemui.statusbar.notification.collection.notifcollection.CommonNotifCollection;
+import com.android.systemui.statusbar.notification.collection.render.NotificationVisibilityProvider;
import com.android.systemui.statusbar.notification.stack.ForegroundServiceSectionController;
import com.android.systemui.statusbar.phone.KeyguardBypassController;
import com.android.systemui.statusbar.phone.ManagedProfileController;
@@ -102,6 +103,7 @@
FeatureFlags featureFlags,
NotificationLockscreenUserManager lockscreenUserManager,
SmartReplyController smartReplyController,
+ NotificationVisibilityProvider visibilityProvider,
NotificationEntryManager notificationEntryManager,
RemoteInputNotificationRebuilder rebuilder,
Lazy<Optional<StatusBar>> statusBarOptionalLazy,
@@ -116,6 +118,7 @@
featureFlags,
lockscreenUserManager,
smartReplyController,
+ visibilityProvider,
notificationEntryManager,
rebuilder,
statusBarOptionalLazy,
@@ -134,6 +137,7 @@
Context context,
Lazy<Optional<StatusBar>> statusBarOptionalLazy,
Lazy<NotificationShadeWindowController> notificationShadeWindowController,
+ NotificationVisibilityProvider visibilityProvider,
NotificationEntryManager notificationEntryManager,
MediaArtworkProcessor mediaArtworkProcessor,
KeyguardBypassController keyguardBypassController,
@@ -147,6 +151,7 @@
context,
statusBarOptionalLazy,
notificationShadeWindowController,
+ visibilityProvider,
notificationEntryManager,
mediaArtworkProcessor,
keyguardBypassController,
@@ -174,10 +179,14 @@
@Provides
static SmartReplyController provideSmartReplyController(
DumpManager dumpManager,
- NotificationEntryManager entryManager,
+ NotificationVisibilityProvider visibilityProvider,
IStatusBarService statusBarService,
NotificationClickNotifier clickNotifier) {
- return new SmartReplyController(dumpManager, entryManager, statusBarService, clickNotifier);
+ return new SmartReplyController(
+ dumpManager,
+ visibilityProvider,
+ statusBarService,
+ clickNotifier);
}
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 8bc41c2..82f35a8 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationEntryManager.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationEntryManager.java
@@ -721,8 +721,12 @@
* @param reason why the notifications are updating
*/
public void updateNotifications(String reason) {
+ if (mFeatureFlags.isNewNotifPipelineRenderingEnabled()) {
+ mLogger.logUseWhileNewPipelineActive("updateNotifications", reason);
+ return;
+ }
reapplyFilterAndSort(reason);
- if (mPresenter != null && !mFeatureFlags.isNewNotifPipelineRenderingEnabled()) {
+ if (mPresenter != null) {
mPresenter.updateNotificationViews(reason);
}
}
@@ -808,11 +812,11 @@
* notification doesn't exist.
*/
public NotificationEntry getPendingOrActiveNotif(String key) {
- if (mPendingNotifications.containsKey(key)) {
- return mPendingNotifications.get(key);
- } else {
- return mActiveNotifications.get(key);
+ NotificationEntry entry = mPendingNotifications.get(key);
+ if (entry != null) {
+ return entry;
}
+ return mActiveNotifications.get(key);
}
private void extendLifetime(NotificationEntry entry, NotificationLifetimeExtender extender) {
@@ -880,11 +884,19 @@
/** Resorts / filters the current notification set with the current RankingMap */
public void reapplyFilterAndSort(String reason) {
+ if (mFeatureFlags.isNewNotifPipelineRenderingEnabled()) {
+ mLogger.logUseWhileNewPipelineActive("reapplyFilterAndSort", reason);
+ return;
+ }
updateRankingAndSort(mRanker.getRankingMap(), reason);
}
/** Calls to NotificationRankingManager and updates mSortedAndFiltered */
private void updateRankingAndSort(@NonNull RankingMap rankingMap, String reason) {
+ if (mFeatureFlags.isNewNotifPipelineRenderingEnabled()) {
+ mLogger.logUseWhileNewPipelineActive("updateRankingAndSort", reason);
+ return;
+ }
mSortedAndFiltered.clear();
mSortedAndFiltered.addAll(mRanker.updateRanking(
rankingMap, mActiveNotifications.values(), reason));
@@ -892,7 +904,7 @@
/** dump the current active notification list. Called from StatusBar */
public void dump(PrintWriter pw, String indent) {
- pw.println("NotificationEntryManager");
+ pw.println("NotificationEntryManager (Legacy)");
int filteredLen = mSortedAndFiltered.size();
pw.print(indent);
pw.println("active notifications: " + filteredLen);
@@ -946,6 +958,12 @@
return mReadOnlyAllNotifications;
}
+ @Nullable
+ @Override
+ public NotificationEntry getEntry(String key) {
+ return getPendingOrActiveNotif(key);
+ }
+
/** @return A count of the active notifications */
public int getActiveNotificationsCount() {
return mReadOnlyNotifications.size();
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationEntryManagerLogger.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationEntryManagerLogger.kt
index 4382ab5..2397005 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationEntryManagerLogger.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationEntryManagerLogger.kt
@@ -19,6 +19,7 @@
import com.android.systemui.log.LogBuffer
import com.android.systemui.log.LogLevel.DEBUG
import com.android.systemui.log.LogLevel.INFO
+import com.android.systemui.log.LogLevel.WARNING
import com.android.systemui.log.dagger.NotificationLog
import javax.inject.Inject
@@ -95,6 +96,15 @@
"FILTER AND SORT reason=$str1"
})
}
+
+ fun logUseWhileNewPipelineActive(method: String, reason: String) {
+ buffer.log(TAG, WARNING, {
+ str1 = method
+ str2 = reason
+ }, {
+ "While running New Pipeline: $str1(reason=$str2)"
+ })
+ }
}
private const val TAG = "NotificationEntryMgr"
\ No newline at end of file
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/NotifPipeline.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/NotifPipeline.kt
similarity index 61%
rename from packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/NotifPipeline.java
rename to packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/NotifPipeline.kt
index 27ba4c2..50d7324 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/NotifPipeline.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/NotifPipeline.kt
@@ -13,34 +13,25 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
+package com.android.systemui.statusbar.notification.collection
-package com.android.systemui.statusbar.notification.collection;
-
-import android.os.Handler;
-
-import androidx.annotation.Nullable;
-
-import com.android.systemui.dagger.SysUISingleton;
-import com.android.systemui.statusbar.notification.collection.listbuilder.OnBeforeFinalizeFilterListener;
-import com.android.systemui.statusbar.notification.collection.listbuilder.OnBeforeRenderListListener;
-import com.android.systemui.statusbar.notification.collection.listbuilder.OnBeforeSortListener;
-import com.android.systemui.statusbar.notification.collection.listbuilder.OnBeforeTransformGroupsListener;
-import com.android.systemui.statusbar.notification.collection.listbuilder.pluggable.Invalidator;
-import com.android.systemui.statusbar.notification.collection.listbuilder.pluggable.NotifComparator;
-import com.android.systemui.statusbar.notification.collection.listbuilder.pluggable.NotifFilter;
-import com.android.systemui.statusbar.notification.collection.listbuilder.pluggable.NotifPromoter;
-import com.android.systemui.statusbar.notification.collection.listbuilder.pluggable.NotifSectioner;
-import com.android.systemui.statusbar.notification.collection.listbuilder.pluggable.NotifStabilityManager;
-import com.android.systemui.statusbar.notification.collection.notifcollection.CommonNotifCollection;
-import com.android.systemui.statusbar.notification.collection.notifcollection.InternalNotifUpdater;
-import com.android.systemui.statusbar.notification.collection.notifcollection.NotifCollectionListener;
-import com.android.systemui.statusbar.notification.collection.notifcollection.NotifDismissInterceptor;
-import com.android.systemui.statusbar.notification.collection.notifcollection.NotifLifetimeExtender;
-
-import java.util.Collection;
-import java.util.List;
-
-import javax.inject.Inject;
+import com.android.systemui.dagger.SysUISingleton
+import com.android.systemui.statusbar.notification.collection.listbuilder.OnBeforeFinalizeFilterListener
+import com.android.systemui.statusbar.notification.collection.listbuilder.OnBeforeRenderListListener
+import com.android.systemui.statusbar.notification.collection.listbuilder.OnBeforeSortListener
+import com.android.systemui.statusbar.notification.collection.listbuilder.OnBeforeTransformGroupsListener
+import com.android.systemui.statusbar.notification.collection.listbuilder.pluggable.Invalidator
+import com.android.systemui.statusbar.notification.collection.listbuilder.pluggable.NotifComparator
+import com.android.systemui.statusbar.notification.collection.listbuilder.pluggable.NotifFilter
+import com.android.systemui.statusbar.notification.collection.listbuilder.pluggable.NotifPromoter
+import com.android.systemui.statusbar.notification.collection.listbuilder.pluggable.NotifSectioner
+import com.android.systemui.statusbar.notification.collection.listbuilder.pluggable.NotifStabilityManager
+import com.android.systemui.statusbar.notification.collection.notifcollection.CommonNotifCollection
+import com.android.systemui.statusbar.notification.collection.notifcollection.InternalNotifUpdater
+import com.android.systemui.statusbar.notification.collection.notifcollection.NotifCollectionListener
+import com.android.systemui.statusbar.notification.collection.notifcollection.NotifDismissInterceptor
+import com.android.systemui.statusbar.notification.collection.notifcollection.NotifLifetimeExtender
+import javax.inject.Inject
/**
* The system that constructs the "shade list", the filtered, grouped, and sorted list of
@@ -52,42 +43,33 @@
* This list differs from the canonical one we receive from system server in a few ways:
* - Filtered: Some notifications are filtered out. For example, we filter out notifications whose
* views haven't been inflated yet. We also filter out some notifications if we're on the lock
- * screen and notifications for other users. So participate, see
- * {@link #addPreGroupFilter} and similar methods.
+ * screen and notifications for other users. To participate, see
+ * [.addPreGroupFilter] and similar methods.
* - Grouped: Notifications that are part of the same group are clustered together into a single
* GroupEntry. These groups are then transformed in order to remove children or completely split
- * them apart. To participate, see {@link #addPromoter}.
+ * them apart. To participate, see [.addPromoter].
* - Sorted: All top-level notifications are sorted. To participate, see
- * {@link #setSections} and {@link #setComparators}
+ * [.setSections] and [.setComparators]
*
* The exact order of all hooks is as follows:
- * 0. Collection listeners are fired ({@link #addCollectionListener}).
- * 1. Pre-group filters are fired on each notification ({@link #addPreGroupFilter}).
+ * 0. Collection listeners are fired ([.addCollectionListener]).
+ * 1. Pre-group filters are fired on each notification ([.addPreGroupFilter]).
* 2. Initial grouping is performed (NotificationEntries will have their parents set
* appropriately).
- * 3. OnBeforeTransformGroupListeners are fired ({@link #addOnBeforeTransformGroupsListener})
- * 4. NotifPromoters are called on each notification with a parent ({@link #addPromoter})
- * 5. OnBeforeSortListeners are fired ({@link #addOnBeforeSortListener})
- * 6. Top-level entries are assigned sections by NotifSections ({@link #setSections})
- * 7. Top-level entries within the same section are sorted by NotifComparators
- * ({@link #setComparators})
- * 8. Finalize filters are fired on each notification ({@link #addFinalizeFilter})
- * 9. OnBeforeRenderListListeners are fired ({@link #addOnBeforeRenderListListener})
- * 9. The list is handed off to the view layer to be rendered
+ * 3. OnBeforeTransformGroupListeners are fired ([.addOnBeforeTransformGroupsListener])
+ * 4. NotifPromoters are called on each notification with a parent ([.addPromoter])
+ * 5. Finalize filters are fired on each notification ([.addFinalizeFilter])
+ * 6. OnBeforeSortListeners are fired ([.addOnBeforeSortListener])
+ * 7. Top-level entries are assigned sections by NotifSections ([.setSections])
+ * 8. Top-level entries within the same section are sorted by NotifComparators ([.setComparators])
+ * 9. OnBeforeRenderListListeners are fired ([.addOnBeforeRenderListListener])
+ * 10. The list is handed off to the view layer to be rendered
*/
@SysUISingleton
-public class NotifPipeline implements CommonNotifCollection {
- private final NotifCollection mNotifCollection;
- private final ShadeListBuilder mShadeListBuilder;
-
- @Inject
- public NotifPipeline(
- NotifCollection notifCollection,
- ShadeListBuilder shadeListBuilder) {
- mNotifCollection = notifCollection;
- mShadeListBuilder = shadeListBuilder;
- }
-
+class NotifPipeline @Inject constructor(
+ private val mNotifCollection: NotifCollection,
+ private val mShadeListBuilder: ShadeListBuilder
+) : CommonNotifCollection {
/**
* Returns the list of all known notifications, i.e. the notifications that are currently posted
* to the phone. In general, this tracks closely to the list maintained by NotificationManager,
@@ -95,38 +77,35 @@
*
* The returned collection is read-only, unsorted, unfiltered, and ungrouped.
*/
- @Override
- public Collection<NotificationEntry> getAllNotifs() {
- return mNotifCollection.getAllNotifs();
+ override fun getAllNotifs(): Collection<NotificationEntry> {
+ return mNotifCollection.allNotifs
}
- @Override
- public void addCollectionListener(NotifCollectionListener listener) {
- mNotifCollection.addCollectionListener(listener);
+ override fun addCollectionListener(listener: NotifCollectionListener) {
+ mNotifCollection.addCollectionListener(listener)
}
/**
* Returns the NotificationEntry associated with [key].
*/
- @Nullable
- public NotificationEntry getEntry(String key) {
- return mNotifCollection.getEntry(key);
+ override fun getEntry(key: String): NotificationEntry? {
+ return mNotifCollection.getEntry(key)
}
/**
* Registers a lifetime extender. Lifetime extenders can cause notifications that have been
* dismissed or retracted by system server to be temporarily retained in the collection.
*/
- public void addNotificationLifetimeExtender(NotifLifetimeExtender extender) {
- mNotifCollection.addNotificationLifetimeExtender(extender);
+ fun addNotificationLifetimeExtender(extender: NotifLifetimeExtender) {
+ mNotifCollection.addNotificationLifetimeExtender(extender)
}
/**
* Registers a dismiss interceptor. Dismiss interceptors can cause notifications that have been
* dismissed by the user to be retained (won't send a dismissal to system server).
*/
- public void addNotificationDismissInterceptor(NotifDismissInterceptor interceptor) {
- mNotifCollection.addNotificationDismissInterceptor(interceptor);
+ fun addNotificationDismissInterceptor(interceptor: NotifDismissInterceptor) {
+ mNotifCollection.addNotificationDismissInterceptor(interceptor)
}
/**
@@ -135,16 +114,16 @@
* returns true, the notification is removed from the pipeline (and no other filters are
* called on that notif).
*/
- public void addPreGroupFilter(NotifFilter filter) {
- mShadeListBuilder.addPreGroupFilter(filter);
+ fun addPreGroupFilter(filter: NotifFilter) {
+ mShadeListBuilder.addPreGroupFilter(filter)
}
/**
* Called after notifications have been filtered and after the initial grouping has been
* performed but before NotifPromoters have had a chance to promote children out of groups.
*/
- public void addOnBeforeTransformGroupsListener(OnBeforeTransformGroupsListener listener) {
- mShadeListBuilder.addOnBeforeTransformGroupsListener(listener);
+ fun addOnBeforeTransformGroupsListener(listener: OnBeforeTransformGroupsListener) {
+ mShadeListBuilder.addOnBeforeTransformGroupsListener(listener)
}
/**
@@ -154,34 +133,34 @@
* registered. If any promoter returns true, the notification is removed from the group (and no
* other promoters are called on it).
*/
- public void addPromoter(NotifPromoter promoter) {
- mShadeListBuilder.addPromoter(promoter);
+ fun addPromoter(promoter: NotifPromoter) {
+ mShadeListBuilder.addPromoter(promoter)
}
/**
* Called after notifs have been filtered and groups have been determined but before sections
* have been determined or the notifs have been sorted.
*/
- public void addOnBeforeSortListener(OnBeforeSortListener listener) {
- mShadeListBuilder.addOnBeforeSortListener(listener);
+ fun addOnBeforeSortListener(listener: OnBeforeSortListener) {
+ mShadeListBuilder.addOnBeforeSortListener(listener)
}
/**
* Sections that are used to sort top-level entries. If two entries have the same section,
* NotifComparators are consulted. Sections from this list are called in order for each
* notification passed through the pipeline. The first NotifSection to return true for
- * {@link NotifSectioner#isInSection(ListEntry)} sets the entry as part of its Section.
+ * [NotifSectioner.isInSection] sets the entry as part of its Section.
*/
- public void setSections(List<NotifSectioner> sections) {
- mShadeListBuilder.setSectioners(sections);
+ fun setSections(sections: List<NotifSectioner>) {
+ mShadeListBuilder.setSectioners(sections)
}
/**
* StabilityManager that is used to determine whether to suppress group and section changes.
* This should only be set once.
*/
- public void setVisualStabilityManager(NotifStabilityManager notifStabilityManager) {
- mShadeListBuilder.setNotifStabilityManager(notifStabilityManager);
+ fun setVisualStabilityManager(notifStabilityManager: NotifStabilityManager) {
+ mShadeListBuilder.setNotifStabilityManager(notifStabilityManager)
}
/**
@@ -189,16 +168,16 @@
* comparators are executed in order until one of them returns a non-zero result. If all return
* zero, the pipeline falls back to sorting by rank (and, failing that, Notification.when).
*/
- public void setComparators(List<NotifComparator> comparators) {
- mShadeListBuilder.setComparators(comparators);
+ fun setComparators(comparators: List<NotifComparator>) {
+ mShadeListBuilder.setComparators(comparators)
}
/**
* Called after notifs have been filtered once, grouped, and sorted but before the final
* filtering.
*/
- public void addOnBeforeFinalizeFilterListener(OnBeforeFinalizeFilterListener listener) {
- mShadeListBuilder.addOnBeforeFinalizeFilterListener(listener);
+ fun addOnBeforeFinalizeFilterListener(listener: OnBeforeFinalizeFilterListener) {
+ mShadeListBuilder.addOnBeforeFinalizeFilterListener(listener)
}
/**
@@ -208,21 +187,21 @@
* true, the notification is removed from the pipeline (and no other filters are called on that
* notif).
*/
- public void addFinalizeFilter(NotifFilter filter) {
- mShadeListBuilder.addFinalizeFilter(filter);
+ fun addFinalizeFilter(filter: NotifFilter) {
+ mShadeListBuilder.addFinalizeFilter(filter)
}
/**
* Called at the end of the pipeline after the notif list has been finalized but before it has
* been handed off to the view layer.
*/
- public void addOnBeforeRenderListListener(OnBeforeRenderListListener listener) {
- mShadeListBuilder.addOnBeforeRenderListListener(listener);
+ fun addOnBeforeRenderListListener(listener: OnBeforeRenderListListener) {
+ mShadeListBuilder.addOnBeforeRenderListListener(listener)
}
/** Registers an invalidator that can be used to invalidate the entire notif list. */
- public void addPreRenderInvalidator(Invalidator invalidator) {
- mShadeListBuilder.addPreRenderInvalidator(invalidator);
+ fun addPreRenderInvalidator(invalidator: Invalidator) {
+ mShadeListBuilder.addPreRenderInvalidator(invalidator)
}
/**
@@ -232,8 +211,8 @@
* @param name the name of the component that will update notifiations
* @return an updater
*/
- public InternalNotifUpdater getInternalNotifUpdater(String name) {
- return mNotifCollection.getInternalNotifUpdater(name);
+ fun getInternalNotifUpdater(name: String?): InternalNotifUpdater {
+ return mNotifCollection.getInternalNotifUpdater(name)
}
/**
@@ -241,8 +220,20 @@
* are currently present in the shade. If this method is called during pipeline execution it
* will return the current state of the list, which will likely be only partially-generated.
*/
- public List<ListEntry> getShadeList() {
- return mShadeListBuilder.getShadeList();
+ val shadeList: List<ListEntry>
+ get() = mShadeListBuilder.shadeList
+
+ /**
+ * Constructs a flattened representation of the notification tree, where each group will have
+ * the summary (if present) followed by the children.
+ */
+ fun getFlatShadeList(): List<NotificationEntry> = shadeList.flatMap { entry ->
+ when (entry) {
+ is NotificationEntry -> sequenceOf(entry)
+ is GroupEntry -> (entry.summary?.let { sequenceOf(it) }.orEmpty() +
+ entry.children)
+ else -> throw RuntimeException("Unexpected entry $entry")
+ }
}
/**
@@ -251,20 +242,9 @@
* will return the number of notifications in its current state, which will likely be only
* partially-generated.
*/
- public int getShadeListCount() {
- final List<ListEntry> entries = getShadeList();
- int numNotifs = 0;
- for (int i = 0; i < entries.size(); i++) {
- final ListEntry entry = entries.get(i);
- if (entry instanceof GroupEntry) {
- final GroupEntry parentEntry = (GroupEntry) entry;
- numNotifs++; // include the summary in the count
- numNotifs += parentEntry.getChildren().size();
- } else {
- numNotifs++;
- }
- }
-
- return numNotifs;
+ fun getShadeListCount(): Int = shadeList.sumOf { entry ->
+ // include the summary in the count
+ if (entry is GroupEntry) 1 + entry.children.size
+ else 1
}
-}
+}
\ No newline at end of file
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 15f0d88..d013261 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
@@ -134,7 +134,7 @@
mInterceptedDismissalEntries.remove(entry.getKey());
mOnEndDismissInterception.onEndDismissInterception(mDismissInterceptor, entry,
dismissedByUserStats);
- } else if (mNotifPipeline.getAllNotifs().contains(entry)) {
+ } else if (mNotifPipeline.getEntry(entry.getKey()) != null) {
// Bubbles are hiding the notifications from the shade, but the bubble was
// deleted; therefore, the notification should be cancelled as if it were a user
// dismissal (this won't re-enter handleInterceptDimissal because Bubbles
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/inflation/OnUserInteractionCallbackImpl.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/inflation/OnUserInteractionCallbackImpl.java
index 23417fe..66ec30d 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/inflation/OnUserInteractionCallbackImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/inflation/OnUserInteractionCallbackImpl.java
@@ -23,15 +23,13 @@
import android.service.notification.NotificationListenerService;
import android.service.notification.NotificationStats;
-import com.android.internal.statusbar.NotificationVisibility;
import com.android.systemui.plugins.statusbar.StatusBarStateController;
import com.android.systemui.statusbar.notification.collection.NotifCollection;
-import com.android.systemui.statusbar.notification.collection.NotifPipeline;
import com.android.systemui.statusbar.notification.collection.NotificationEntry;
import com.android.systemui.statusbar.notification.collection.coordinator.VisualStabilityCoordinator;
import com.android.systemui.statusbar.notification.collection.notifcollection.DismissedByUserStats;
import com.android.systemui.statusbar.notification.collection.render.GroupMembershipManager;
-import com.android.systemui.statusbar.notification.logging.NotificationLogger;
+import com.android.systemui.statusbar.notification.collection.render.NotificationVisibilityProvider;
import com.android.systemui.statusbar.notification.row.OnUserInteractionCallback;
import com.android.systemui.statusbar.policy.HeadsUpManager;
@@ -40,7 +38,7 @@
* information about the interaction to the notification pipeline.
*/
public class OnUserInteractionCallbackImpl implements OnUserInteractionCallback {
- private final NotifPipeline mNotifPipeline;
+ private final NotificationVisibilityProvider mVisibilityProvider;
private final NotifCollection mNotifCollection;
private final HeadsUpManager mHeadsUpManager;
private final StatusBarStateController mStatusBarStateController;
@@ -48,14 +46,14 @@
private final GroupMembershipManager mGroupMembershipManager;
public OnUserInteractionCallbackImpl(
- NotifPipeline notifPipeline,
+ NotificationVisibilityProvider visibilityProvider,
NotifCollection notifCollection,
HeadsUpManager headsUpManager,
StatusBarStateController statusBarStateController,
VisualStabilityCoordinator visualStabilityCoordinator,
GroupMembershipManager groupMembershipManager
) {
- mNotifPipeline = notifPipeline;
+ mVisibilityProvider = visibilityProvider;
mNotifCollection = notifCollection;
mHeadsUpManager = headsUpManager;
mStatusBarStateController = statusBarStateController;
@@ -91,12 +89,7 @@
new DismissedByUserStats(
dismissalSurface,
DISMISS_SENTIMENT_NEUTRAL,
- NotificationVisibility.obtain(
- entry.getKey(),
- entry.getRanking().getRank(),
- mNotifPipeline.getShadeListCount(),
- true,
- NotificationLogger.getNotificationLocation(entry)))
+ mVisibilityProvider.obtain(entry, true))
);
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/legacy/LegacyNotificationVisibilityProvider.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/legacy/LegacyNotificationVisibilityProvider.kt
new file mode 100644
index 0000000..5c70f32
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/legacy/LegacyNotificationVisibilityProvider.kt
@@ -0,0 +1,50 @@
+/*
+ * 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.statusbar.notification.collection.legacy
+
+import com.android.internal.statusbar.NotificationVisibility
+import com.android.systemui.statusbar.notification.NotificationEntryManager
+import com.android.systemui.statusbar.notification.collection.NotificationEntry
+import com.android.systemui.statusbar.notification.collection.render.NotificationVisibilityProvider
+import com.android.systemui.statusbar.notification.logging.NotificationLogger
+import javax.inject.Inject
+
+/** Legacy pipeline implementation for getting [NotificationVisibility]. */
+class LegacyNotificationVisibilityProvider @Inject constructor(
+ private val notifEntryManager: NotificationEntryManager
+) : NotificationVisibilityProvider {
+ override fun obtain(entry: NotificationEntry, visible: Boolean): NotificationVisibility {
+ val count: Int = notifEntryManager.activeNotificationsCount
+ val rank = entry.ranking.rank
+ val hasRow = entry.row != null
+ val location = NotificationLogger.getNotificationLocation(entry)
+ return NotificationVisibility.obtain(entry.key, rank, count, visible && hasRow, location)
+ }
+
+ override fun obtain(key: String, visible: Boolean): NotificationVisibility {
+ val entry: NotificationEntry? = notifEntryManager.getActiveNotificationUnfiltered(key)
+ val count: Int = notifEntryManager.activeNotificationsCount
+ val rank = entry?.ranking?.rank ?: -1
+ val hasRow = entry?.row != null
+ val location = NotificationLogger.getNotificationLocation(entry)
+ return NotificationVisibility.obtain(key, rank, count, visible && hasRow, location)
+ }
+
+ override fun getLocation(key: String): NotificationVisibility.NotificationLocation =
+ NotificationLogger.getNotificationLocation(
+ notifEntryManager.getActiveNotificationUnfiltered(key))
+}
\ No newline at end of file
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/legacy/OnUserInteractionCallbackImplLegacy.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/legacy/OnUserInteractionCallbackImplLegacy.java
index 11f22dd..3b114bb 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/legacy/OnUserInteractionCallbackImplLegacy.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/legacy/OnUserInteractionCallbackImplLegacy.java
@@ -22,13 +22,12 @@
import android.service.notification.NotificationListenerService;
import android.service.notification.NotificationStats;
-import com.android.internal.statusbar.NotificationVisibility;
import com.android.systemui.plugins.statusbar.StatusBarStateController;
import com.android.systemui.statusbar.notification.NotificationEntryManager;
import com.android.systemui.statusbar.notification.collection.NotificationEntry;
import com.android.systemui.statusbar.notification.collection.notifcollection.DismissedByUserStats;
import com.android.systemui.statusbar.notification.collection.render.GroupMembershipManager;
-import com.android.systemui.statusbar.notification.logging.NotificationLogger;
+import com.android.systemui.statusbar.notification.collection.render.NotificationVisibilityProvider;
import com.android.systemui.statusbar.notification.row.OnUserInteractionCallback;
import com.android.systemui.statusbar.policy.HeadsUpManager;
@@ -37,6 +36,7 @@
*/
public class OnUserInteractionCallbackImplLegacy implements OnUserInteractionCallback {
private final NotificationEntryManager mNotificationEntryManager;
+ private final NotificationVisibilityProvider mVisibilityProvider;
private final HeadsUpManager mHeadsUpManager;
private final StatusBarStateController mStatusBarStateController;
private final VisualStabilityManager mVisualStabilityManager;
@@ -44,12 +44,14 @@
public OnUserInteractionCallbackImplLegacy(
NotificationEntryManager notificationEntryManager,
+ NotificationVisibilityProvider visibilityProvider,
HeadsUpManager headsUpManager,
StatusBarStateController statusBarStateController,
VisualStabilityManager visualStabilityManager,
GroupMembershipManager groupMembershipManager
) {
mNotificationEntryManager = notificationEntryManager;
+ mVisibilityProvider = visibilityProvider;
mHeadsUpManager = headsUpManager;
mStatusBarStateController = statusBarStateController;
mVisualStabilityManager = visualStabilityManager;
@@ -88,12 +90,7 @@
new DismissedByUserStats(
dismissalSurface,
DISMISS_SENTIMENT_NEUTRAL,
- NotificationVisibility.obtain(
- entry.getKey(),
- entry.getRanking().getRank(),
- mNotificationEntryManager.getActiveNotificationsCount(),
- true,
- NotificationLogger.getNotificationLocation(entry))),
+ mVisibilityProvider.obtain(entry, true)),
cancellationReason
);
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 b4c2bb8..471c357 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,8 @@
package com.android.systemui.statusbar.notification.collection.notifcollection;
+import androidx.annotation.Nullable;
+
import com.android.systemui.statusbar.notification.NotificationEntryManager;
import com.android.systemui.statusbar.notification.collection.NotifPipeline;
import com.android.systemui.statusbar.notification.collection.NotificationEntry;
@@ -45,4 +47,10 @@
* The returned collection is read-only, unsorted, unfiltered, and ungrouped.
*/
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);
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/render/NotificationVisibilityProvider.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/render/NotificationVisibilityProvider.kt
new file mode 100644
index 0000000..c492d14
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/render/NotificationVisibilityProvider.kt
@@ -0,0 +1,32 @@
+/*
+ * 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.statusbar.notification.collection.render
+
+import com.android.internal.statusbar.NotificationVisibility
+import com.android.systemui.statusbar.notification.collection.NotificationEntry
+
+/**
+ * An interface for getting the current [NotificationVisibility] object for a notification.
+ */
+interface NotificationVisibilityProvider {
+ /** Given a notification entry, return the visibility object */
+ fun obtain(entry: NotificationEntry, visible: Boolean): NotificationVisibility
+ /** Given a notification key, return the visibility object */
+ fun obtain(key: String, visible: Boolean): NotificationVisibility
+ /** Given a notification key, return the location */
+ fun getLocation(key: String): NotificationVisibility.NotificationLocation
+}
\ No newline at end of file
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/render/NotificationVisibilityProviderImpl.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/render/NotificationVisibilityProviderImpl.kt
new file mode 100644
index 0000000..51de08d
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/render/NotificationVisibilityProviderImpl.kt
@@ -0,0 +1,43 @@
+/*
+ * 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.statusbar.notification.collection.render
+
+import com.android.internal.statusbar.NotificationVisibility
+import com.android.systemui.statusbar.notification.collection.NotifPipeline
+import com.android.systemui.statusbar.notification.collection.NotificationEntry
+import com.android.systemui.statusbar.notification.logging.NotificationLogger
+import javax.inject.Inject
+
+/** New pipeline implementation for getting [NotificationVisibility]. */
+class NotificationVisibilityProviderImpl @Inject constructor(
+ private val notifPipeline: NotifPipeline
+) : NotificationVisibilityProvider {
+ override fun obtain(entry: NotificationEntry, visible: Boolean): NotificationVisibility {
+ val count: Int = notifPipeline.getShadeListCount()
+ val rank = entry.ranking.rank
+ val hasRow = entry.row != null
+ val location = NotificationLogger.getNotificationLocation(entry)
+ return NotificationVisibility.obtain(entry.key, rank, count, visible && hasRow, location)
+ }
+
+ override fun obtain(key: String, visible: Boolean): NotificationVisibility =
+ notifPipeline.getEntry(key)?.let { return obtain(it, visible) }
+ ?: NotificationVisibility.obtain(key, -1, notifPipeline.getShadeListCount(), false)
+
+ override fun getLocation(key: String): NotificationVisibility.NotificationLocation =
+ NotificationLogger.getNotificationLocation(notifPipeline.getEntry(key))
+}
\ No newline at end of file
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/dagger/NotificationsModule.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/dagger/NotificationsModule.java
index 1eb007e..a19549c5 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/dagger/NotificationsModule.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/dagger/NotificationsModule.java
@@ -52,6 +52,7 @@
import com.android.systemui.statusbar.notification.collection.inflation.NotificationRowBinder;
import com.android.systemui.statusbar.notification.collection.inflation.OnUserInteractionCallbackImpl;
import com.android.systemui.statusbar.notification.collection.legacy.LegacyNotificationPresenterExtensions;
+import com.android.systemui.statusbar.notification.collection.legacy.LegacyNotificationVisibilityProvider;
import com.android.systemui.statusbar.notification.collection.legacy.NotificationGroupManagerLegacy;
import com.android.systemui.statusbar.notification.collection.legacy.OnUserInteractionCallbackImplLegacy;
import com.android.systemui.statusbar.notification.collection.legacy.VisualStabilityManager;
@@ -63,6 +64,8 @@
import com.android.systemui.statusbar.notification.collection.render.GroupMembershipManagerImpl;
import com.android.systemui.statusbar.notification.collection.render.NotifGutsViewManager;
import com.android.systemui.statusbar.notification.collection.render.NotifShadeEventSource;
+import com.android.systemui.statusbar.notification.collection.render.NotificationVisibilityProvider;
+import com.android.systemui.statusbar.notification.collection.render.NotificationVisibilityProviderImpl;
import com.android.systemui.statusbar.notification.init.NotificationsController;
import com.android.systemui.statusbar.notification.init.NotificationsControllerImpl;
import com.android.systemui.statusbar.notification.init.NotificationsControllerStub;
@@ -208,14 +211,20 @@
static NotificationLogger provideNotificationLogger(
NotificationListener notificationListener,
@UiBackground Executor uiBgExecutor,
+ FeatureFlags featureFlags,
+ NotificationVisibilityProvider visibilityProvider,
NotificationEntryManager entryManager,
+ NotifPipeline notifPipeline,
StatusBarStateController statusBarStateController,
NotificationLogger.ExpansionStateLogger expansionStateLogger,
NotificationPanelLogger notificationPanelLogger) {
return new NotificationLogger(
notificationListener,
uiBgExecutor,
+ featureFlags,
+ visibilityProvider,
entryManager,
+ notifPipeline,
statusBarStateController,
expansionStateLogger,
notificationPanelLogger);
@@ -278,6 +287,20 @@
}
/**
+ * Provide the object which can be used to obtain NotificationVisibility objects.
+ */
+ @Provides
+ @SysUISingleton
+ static NotificationVisibilityProvider provideNotificationVisibilityProvider(
+ FeatureFlags featureFlags,
+ Lazy<NotificationVisibilityProviderImpl> newProvider,
+ Lazy<LegacyNotificationVisibilityProvider> legacyProvider) {
+ return featureFlags.isNewNotifPipelineRenderingEnabled()
+ ? newProvider.get()
+ : legacyProvider.get();
+ }
+
+ /**
* Provide the active implementation for presenting notifications.
*/
@Provides
@@ -301,15 +324,15 @@
FeatureFlags featureFlags,
HeadsUpManager headsUpManager,
StatusBarStateController statusBarStateController,
- Lazy<NotifPipeline> pipeline,
Lazy<NotifCollection> notifCollection,
+ Lazy<NotificationVisibilityProvider> visibilityProvider,
Lazy<VisualStabilityCoordinator> visualStabilityCoordinator,
NotificationEntryManager entryManager,
VisualStabilityManager visualStabilityManager,
Lazy<GroupMembershipManager> groupMembershipManagerLazy) {
return featureFlags.isNewNotifPipelineRenderingEnabled()
? new OnUserInteractionCallbackImpl(
- pipeline.get(),
+ visibilityProvider.get(),
notifCollection.get(),
headsUpManager,
statusBarStateController,
@@ -317,6 +340,7 @@
groupMembershipManagerLazy.get())
: new OnUserInteractionCallbackImplLegacy(
entryManager,
+ visibilityProvider.get(),
headsUpManager,
statusBarStateController,
visualStabilityManager,
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/logging/NotificationLogger.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/logging/NotificationLogger.java
index 4441270..993e38d 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/logging/NotificationLogger.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/logging/NotificationLogger.java
@@ -26,6 +26,7 @@
import android.util.ArraySet;
import android.util.Log;
+import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import com.android.internal.annotations.GuardedBy;
@@ -33,13 +34,17 @@
import com.android.internal.statusbar.IStatusBarService;
import com.android.internal.statusbar.NotificationVisibility;
import com.android.systemui.dagger.qualifiers.UiBackground;
+import com.android.systemui.flags.FeatureFlags;
import com.android.systemui.plugins.statusbar.StatusBarStateController;
import com.android.systemui.plugins.statusbar.StatusBarStateController.StateListener;
import com.android.systemui.statusbar.NotificationListener;
import com.android.systemui.statusbar.StatusBarState;
import com.android.systemui.statusbar.notification.NotificationEntryListener;
import com.android.systemui.statusbar.notification.NotificationEntryManager;
+import com.android.systemui.statusbar.notification.collection.NotifPipeline;
import com.android.systemui.statusbar.notification.collection.NotificationEntry;
+import com.android.systemui.statusbar.notification.collection.notifcollection.NotifCollectionListener;
+import com.android.systemui.statusbar.notification.collection.render.NotificationVisibilityProvider;
import com.android.systemui.statusbar.notification.dagger.NotificationsModule;
import com.android.systemui.statusbar.notification.stack.ExpandableViewState;
import com.android.systemui.statusbar.notification.stack.NotificationListContainer;
@@ -70,7 +75,10 @@
// Dependencies:
private final NotificationListenerService mNotificationListener;
private final Executor mUiBgExecutor;
+ private final FeatureFlags mFeatureFlags;
+ private final NotificationVisibilityProvider mVisibilityProvider;
private final NotificationEntryManager mEntryManager;
+ private final NotifPipeline mNotifPipeline;
private final NotificationPanelLogger mNotificationPanelLogger;
private final ExpansionStateLogger mExpansionStateLogger;
@@ -127,7 +135,7 @@
// notifications.
// 3. Report newly visible and no-longer visible notifications.
// 4. Keep currently visible notifications for next report.
- List<NotificationEntry> activeNotifications = mEntryManager.getVisibleNotifications();
+ List<NotificationEntry> activeNotifications = getVisibleNotifications();
int N = activeNotifications.size();
for (int i = 0; i < N; i++) {
NotificationEntry entry = activeNotifications.get(i);
@@ -166,6 +174,14 @@
}
};
+ private List<NotificationEntry> getVisibleNotifications() {
+ if (mFeatureFlags.isNewNotifPipelineRenderingEnabled()) {
+ return mNotifPipeline.getFlatShadeList();
+ } else {
+ return mEntryManager.getVisibleNotifications();
+ }
+ }
+
/**
* Returns the location of the notification referenced by the given {@link NotificationEntry}.
*/
@@ -202,13 +218,19 @@
*/
public NotificationLogger(NotificationListener notificationListener,
@UiBackground Executor uiBgExecutor,
+ FeatureFlags featureFlags,
+ NotificationVisibilityProvider visibilityProvider,
NotificationEntryManager entryManager,
+ NotifPipeline notifPipeline,
StatusBarStateController statusBarStateController,
ExpansionStateLogger expansionStateLogger,
NotificationPanelLogger notificationPanelLogger) {
mNotificationListener = notificationListener;
mUiBgExecutor = uiBgExecutor;
+ mFeatureFlags = featureFlags;
+ mVisibilityProvider = visibilityProvider;
mEntryManager = entryManager;
+ mNotifPipeline = notifPipeline;
mBarService = IStatusBarService.Stub.asInterface(
ServiceManager.getService(Context.STATUS_BAR_SERVICE));
mExpansionStateLogger = expansionStateLogger;
@@ -216,7 +238,15 @@
// Not expected to be destroyed, don't need to unsubscribe
statusBarStateController.addCallback(this);
- entryManager.addNotificationEntryListener(new NotificationEntryListener() {
+ if (mFeatureFlags.isNewNotifPipelineRenderingEnabled()) {
+ registerNewPipelineListener();
+ } else {
+ registerLegacyListener();
+ }
+ }
+
+ private void registerLegacyListener() {
+ mEntryManager.addNotificationEntryListener(new NotificationEntryListener() {
@Override
public void onEntryRemoved(
NotificationEntry entry,
@@ -240,6 +270,20 @@
});
}
+ private void registerNewPipelineListener() {
+ mNotifPipeline.addCollectionListener(new NotifCollectionListener() {
+ @Override
+ public void onEntryUpdated(@NonNull NotificationEntry entry, boolean fromSystem) {
+ mExpansionStateLogger.onEntryUpdated(entry.getKey());
+ }
+
+ @Override
+ public void onEntryRemoved(@NonNull NotificationEntry entry, int reason) {
+ mExpansionStateLogger.onEntryRemoved(entry.getKey());
+ }
+ });
+ }
+
public void setUpWithContainer(NotificationListContainer listContainer) {
mListContainer = listContainer;
}
@@ -407,8 +451,7 @@
// Once we know panelExpanded and Dozing, turn logging on & off when appropriate
boolean lockscreen = mLockscreen == null ? false : mLockscreen;
if (mPanelExpanded && !mDozing) {
- mNotificationPanelLogger.logPanelShown(lockscreen,
- mEntryManager.getVisibleNotifications());
+ mNotificationPanelLogger.logPanelShown(lockscreen, getVisibleNotifications());
if (DEBUG) {
Log.i(TAG, "Notification panel shown, lockscreen=" + lockscreen);
}
@@ -440,8 +483,7 @@
* Called when the notification is expanded / collapsed.
*/
public void onExpansionChanged(String key, boolean isUserAction, boolean isExpanded) {
- NotificationVisibility.NotificationLocation location =
- getNotificationLocation(mEntryManager.getActiveNotificationUnfiltered(key));
+ NotificationVisibility.NotificationLocation location = mVisibilityProvider.getLocation(key);
mExpansionStateLogger.onExpansionChanged(key, isUserAction, isExpanded, location);
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayoutController.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayoutController.java
index 8b8b64d..9492996 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayoutController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayoutController.java
@@ -58,7 +58,6 @@
import com.android.internal.logging.UiEventLogger;
import com.android.internal.logging.nano.MetricsProto.MetricsEvent;
import com.android.internal.statusbar.IStatusBarService;
-import com.android.internal.statusbar.NotificationVisibility;
import com.android.systemui.ExpandHelper;
import com.android.systemui.Gefingerpoken;
import com.android.systemui.R;
@@ -99,6 +98,7 @@
import com.android.systemui.statusbar.notification.collection.notifcollection.DismissedByUserStats;
import com.android.systemui.statusbar.notification.collection.notifcollection.NotifCollectionListener;
import com.android.systemui.statusbar.notification.collection.render.GroupExpansionManager;
+import com.android.systemui.statusbar.notification.collection.render.NotificationVisibilityProvider;
import com.android.systemui.statusbar.notification.collection.render.SectionHeaderController;
import com.android.systemui.statusbar.notification.dagger.SilentHeader;
import com.android.systemui.statusbar.notification.logging.NotificationLogger;
@@ -145,6 +145,7 @@
private final boolean mAllowLongPress;
private final NotificationGutsManager mNotificationGutsManager;
+ private final NotificationVisibilityProvider mVisibilityProvider;
private final HeadsUpManagerPhone mHeadsUpManager;
private final NotificationRoundnessManager mNotificationRoundnessManager;
private final TunerService mTunerService;
@@ -618,6 +619,7 @@
public NotificationStackScrollLayoutController(
@Named(ALLOW_NOTIFICATION_LONG_PRESS_NAME) boolean allowLongPress,
NotificationGutsManager notificationGutsManager,
+ NotificationVisibilityProvider visibilityProvider,
HeadsUpManagerPhone headsUpManager,
NotificationRoundnessManager notificationRoundnessManager,
TunerService tunerService,
@@ -655,6 +657,7 @@
ShadeController shadeController) {
mAllowLongPress = allowLongPress;
mNotificationGutsManager = notificationGutsManager;
+ mVisibilityProvider = visibilityProvider;
mHeadsUpManager = headsUpManager;
mNotificationRoundnessManager = notificationRoundnessManager;
mTunerService = tunerService;
@@ -1384,19 +1387,11 @@
mView.resetCheckSnoozeLeavebehind();
}
- private DismissedByUserStats getDismissedByUserStats(
- NotificationEntry entry,
- int numVisibleEntries
- ) {
+ private DismissedByUserStats getDismissedByUserStats(NotificationEntry entry) {
return new DismissedByUserStats(
DISMISSAL_SHADE,
DISMISS_SENTIMENT_NEUTRAL,
- NotificationVisibility.obtain(
- entry.getKey(),
- entry.getRanking().getRank(),
- numVisibleEntries,
- true,
- NotificationLogger.getNotificationLocation(entry)));
+ mVisibilityProvider.obtain(entry, true));
}
/**
@@ -1447,13 +1442,10 @@
} else {
final List<Pair<NotificationEntry, DismissedByUserStats>>
entriesWithRowsDismissedFromShade = new ArrayList<>();
- final int numVisibleEntries = mNotifPipeline.getShadeListCount();
for (ExpandableNotificationRow row : viewsToRemove) {
final NotificationEntry entry = row.getEntry();
entriesWithRowsDismissedFromShade.add(
- new Pair<>(
- entry,
- getDismissedByUserStats(entry, numVisibleEntries)));
+ new Pair<>(entry, getDismissedByUserStats(entry)));
}
mNotifCollection.dismissNotifications(entriesWithRowsDismissedFromShade);
}
@@ -1462,9 +1454,7 @@
if (canChildBeDismissed(rowToRemove)) {
mNotificationEntryManager.performRemoveNotification(
rowToRemove.getEntry().getSbn(),
- getDismissedByUserStats(
- rowToRemove.getEntry(),
- mNotificationEntryManager.getActiveNotificationsCount()),
+ getDismissedByUserStats(rowToRemove.getEntry()),
NotificationListenerService.REASON_CANCEL_ALL);
} else {
rowToRemove.resetTranslation();
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/CollapsedStatusBarFragment.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/CollapsedStatusBarFragment.java
index 2098a1d..2b62e0b 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/CollapsedStatusBarFragment.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/CollapsedStatusBarFragment.java
@@ -26,6 +26,7 @@
import static com.android.systemui.statusbar.events.SystemStatusAnimationSchedulerKt.SHOWING_PERSISTENT_DOT;
import android.animation.ValueAnimator;
+import android.annotation.NonNull;
import android.annotation.Nullable;
import android.app.Fragment;
import android.os.Bundle;
@@ -46,8 +47,9 @@
import com.android.systemui.statusbar.OperatorNameView;
import com.android.systemui.statusbar.OperatorNameViewController;
import com.android.systemui.statusbar.StatusBarState;
+import com.android.systemui.statusbar.connectivity.IconState;
import com.android.systemui.statusbar.connectivity.NetworkController;
-import com.android.systemui.statusbar.connectivity.NetworkController.SignalCallback;
+import com.android.systemui.statusbar.connectivity.SignalCallback;
import com.android.systemui.statusbar.events.SystemStatusAnimationCallback;
import com.android.systemui.statusbar.events.SystemStatusAnimationScheduler;
import com.android.systemui.statusbar.phone.StatusBarIconController.DarkIconManager;
@@ -104,12 +106,13 @@
private final NotificationIconAreaController mNotificationIconAreaController;
private final PanelExpansionStateManager mPanelExpansionStateManager;
private final StatusBarIconController mStatusBarIconController;
+ private final StatusBarHideIconsForBouncerManager mStatusBarHideIconsForBouncerManager;
private List<String> mBlockedIcons = new ArrayList<>();
private SignalCallback mSignalCallback = new SignalCallback() {
@Override
- public void setIsAirplaneMode(NetworkController.IconState icon) {
+ public void setIsAirplaneMode(@NonNull IconState icon) {
mCommandQueue.recomputeDisableFlags(getContext().getDisplayId(), true /* animate */);
}
};
@@ -131,6 +134,7 @@
PanelExpansionStateManager panelExpansionStateManager,
FeatureFlags featureFlags,
StatusBarIconController statusBarIconController,
+ StatusBarHideIconsForBouncerManager statusBarHideIconsForBouncerManager,
KeyguardStateController keyguardStateController,
NetworkController networkController,
StatusBarStateController statusBarStateController,
@@ -146,6 +150,7 @@
mPanelExpansionStateManager = panelExpansionStateManager;
mFeatureFlags = featureFlags;
mStatusBarIconController = statusBarIconController;
+ mStatusBarHideIconsForBouncerManager = statusBarHideIconsForBouncerManager;
mKeyguardStateController = keyguardStateController;
mNetworkController = networkController;
mStatusBarStateController = statusBarStateController;
@@ -372,10 +377,7 @@
StatusBar::hideStatusBarIconsWhenExpanded).orElse(false)) {
return true;
}
- if (statusBarOptional.map(StatusBar::hideStatusBarIconsForBouncer).orElse(false)) {
- return true;
- }
- return false;
+ return mStatusBarHideIconsForBouncerManager.getShouldHideStatusBarIconsForBouncer();
}
private void hideSystemIconArea(boolean animate) {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelViewController.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelViewController.java
index f90bfe4..e26e69f 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelViewController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelViewController.java
@@ -3938,45 +3938,6 @@
private long mLastTouchDownTime = -1L;
@Override
- public boolean onTouchForwardedFromStatusBar(MotionEvent event) {
- // TODO(b/202981994): Move the touch debugging in this method to a central location.
- // (Right now, it's split between StatusBar and here.)
-
- // If panels aren't enabled, ignore the gesture and don't pass it down to the
- // panel view.
- if (!mCommandQueue.panelsEnabled()) {
- if (event.getAction() == MotionEvent.ACTION_DOWN) {
- Log.v(
- TAG,
- String.format(
- "onTouchForwardedFromStatusBar: "
- + "panel disabled, ignoring touch at (%d,%d)",
- (int) event.getX(),
- (int) event.getY()
- )
- );
- }
- return false;
- }
-
- // If the view that would receive the touch is disabled, just have status bar eat
- // the gesture.
- if (event.getAction() == MotionEvent.ACTION_DOWN && !mView.isEnabled()) {
- Log.v(TAG,
- String.format(
- "onTouchForwardedFromStatusBar: "
- + "panel view disabled, eating touch at (%d,%d)",
- (int) event.getX(),
- (int) event.getY()
- )
- );
- return true;
- }
-
- return mView.dispatchTouchEvent(event);
- }
-
- @Override
public boolean onInterceptTouchEvent(MotionEvent event) {
if (mBlockTouches || mQs.disallowPanelTouches()) {
return false;
@@ -4087,6 +4048,55 @@
};
}
+ private final PhoneStatusBarView.TouchEventHandler mStatusBarViewTouchEventHandler =
+ new PhoneStatusBarView.TouchEventHandler() {
+ @Override
+ public void onInterceptTouchEvent(MotionEvent event) {
+ mStatusBar.onTouchEvent(event);
+ }
+
+ @Override
+ public boolean handleTouchEvent(MotionEvent event) {
+ mStatusBar.onTouchEvent(event);
+
+ // TODO(b/202981994): Move the touch debugging in this method to a central
+ // location. (Right now, it's split between StatusBar and here.)
+
+ // If panels aren't enabled, ignore the gesture and don't pass it down to the
+ // panel view.
+ if (!mCommandQueue.panelsEnabled()) {
+ if (event.getAction() == MotionEvent.ACTION_DOWN) {
+ Log.v(
+ TAG,
+ String.format(
+ "onTouchForwardedFromStatusBar: "
+ + "panel disabled, ignoring touch at (%d,%d)",
+ (int) event.getX(),
+ (int) event.getY()
+ )
+ );
+ }
+ return false;
+ }
+
+ // If the view that would receive the touch is disabled, just have status bar
+ // eat the gesture.
+ if (event.getAction() == MotionEvent.ACTION_DOWN && !mView.isEnabled()) {
+ Log.v(TAG,
+ String.format(
+ "onTouchForwardedFromStatusBar: "
+ + "panel view disabled, eating touch at (%d,%d)",
+ (int) event.getX(),
+ (int) event.getY()
+ )
+ );
+ return true;
+ }
+
+ return mView.dispatchTouchEvent(event);
+ }
+ };
+
@Override
protected PanelViewController.OnConfigurationChangedListener
createOnConfigurationChangedListener() {
@@ -4880,6 +4890,6 @@
/** Returns the handler that the status bar should forward touches to. */
public PhoneStatusBarView.TouchEventHandler getStatusBarTouchEventHandler() {
- return getTouchHandler()::onTouchForwardedFromStatusBar;
+ return mStatusBarViewTouchEventHandler;
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/PanelViewController.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/PanelViewController.java
index 38cf787..2823d98 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/PanelViewController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/PanelViewController.java
@@ -292,10 +292,6 @@
: mTouchSlop;
}
- protected TouchHandler getTouchHandler() {
- return mTouchHandler;
- }
-
private void addMovement(MotionEvent event) {
// Add movement to velocity tracker using raw screen X and Y coordinates instead
// of window coordinates because the window frame may be moving at the same time.
@@ -1161,17 +1157,7 @@
return new OnConfigurationChangedListener();
}
- public abstract class TouchHandler implements View.OnTouchListener {
- /**
- * Method called when a touch has occurred on {@link PhoneStatusBarView}.
- *
- * Touches that occur on the status bar view may have ramifications for the notification
- * panel (e.g. a touch that pulls down the shade could start on the status bar), so we need
- * to notify the panel controller when these touches occur.
- *
- * Returns true if the event was handled and false otherwise.
- */
- public abstract boolean onTouchForwardedFromStatusBar(MotionEvent event);
+ public class TouchHandler implements View.OnTouchListener {
public boolean onInterceptTouchEvent(MotionEvent event) {
if (mInstantExpanding || !mNotificationsDragEnabled || mTouchDisabled || (mMotionAborted
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBarView.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBarView.java
index 0acf2ac..f67d181 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBarView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBarView.java
@@ -47,9 +47,6 @@
private static final String TAG = "PhoneStatusBarView";
private final StatusBarContentInsetsProvider mContentInsetsProvider;
- StatusBar mBar;
-
- private ScrimController mScrimController;
private DarkReceiver mBattery;
private DarkReceiver mClock;
private int mRotationOrientation = -1;
@@ -73,18 +70,10 @@
mContentInsetsProvider = Dependency.get(StatusBarContentInsetsProvider.class);
}
- public void setBar(StatusBar bar) {
- mBar = bar;
- }
-
void setTouchEventHandler(TouchEventHandler handler) {
mTouchEventHandler = handler;
}
- public void setScrimController(ScrimController scrimController) {
- mScrimController = scrimController;
- }
-
@Override
public void onFinishInflate() {
mBattery = findViewById(R.id.battery);
@@ -171,7 +160,6 @@
@Override
public boolean onTouchEvent(MotionEvent event) {
- mBar.onTouchEvent(event);
if (mTouchEventHandler == null) {
Log.w(
TAG,
@@ -188,7 +176,7 @@
@Override
public boolean onInterceptTouchEvent(MotionEvent event) {
- mBar.onTouchEvent(event);
+ mTouchEventHandler.onInterceptTouchEvent(event);
return super.onInterceptTouchEvent(event);
}
@@ -269,13 +257,26 @@
}
/**
- * A handler repsonsible for all touch event handling on the status bar.
+ * A handler responsible for all touch event handling on the status bar.
*
- * The handler will be notified each time {@link this#onTouchEvent} is called, and the return
- * value from the handler will be returned from {@link this#onTouchEvent}.
+ * Touches that occur on the status bar view may have ramifications for the notification
+ * panel (e.g. a touch that pulls down the shade could start on the status bar), so this
+ * interface provides a way to notify the panel controller when these touches occur.
+ *
+ * The handler will be notified each time {@link PhoneStatusBarView#onTouchEvent} and
+ * {@link PhoneStatusBarView#onInterceptTouchEvent} are called.
**/
public interface TouchEventHandler {
- /** Called each time {@link this#onTouchEvent} is called. */
+ /** Called each time {@link PhoneStatusBarView#onInterceptTouchEvent} is called. */
+ void onInterceptTouchEvent(MotionEvent event);
+
+ /**
+ * Called each time {@link PhoneStatusBarView#onTouchEvent} is called.
+ *
+ * Should return true if the touch was handled by this handler and false otherwise. The
+ * return value from the handler will be returned from
+ * {@link PhoneStatusBarView#onTouchEvent}.
+ */
boolean handleTouchEvent(MotionEvent event);
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBar.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBar.java
index 28c6b99..afa333a 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBar.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBar.java
@@ -339,6 +339,12 @@
void setWindowState(int state) {
mStatusBarWindowState = state;
mStatusBarWindowHidden = state == WINDOW_STATE_HIDDEN;
+ mStatusBarHideIconsForBouncerManager.setStatusBarWindowHidden(mStatusBarWindowHidden);
+ if (getStatusBarView() != null) {
+ // Should #updateHideIconsForBouncer always be called, regardless of whether we have a
+ // status bar view? If so, we can make #updateHideIconsForBouncer private.
+ mStatusBarHideIconsForBouncerManager.updateHideIconsForBouncer(/* animate= */ false);
+ }
}
void acquireGestureWakeLock(long time) {
@@ -358,14 +364,6 @@
return mStatusBarMode;
}
- boolean getWereIconsJustHidden() {
- return mWereIconsJustHidden;
- }
-
- void setWereIconsJustHidden(boolean justHidden) {
- mWereIconsJustHidden = justHidden;
- }
-
void resendMessage(int msg) {
mMessageRouter.cancelMessages(msg);
mMessageRouter.sendMessage(msg);
@@ -508,6 +506,7 @@
private final SystemStatusAnimationScheduler mAnimationScheduler;
private final StatusBarLocationPublisher mStatusBarLocationPublisher;
private final StatusBarIconController mStatusBarIconController;
+ private final StatusBarHideIconsForBouncerManager mStatusBarHideIconsForBouncerManager;
// expanded notifications
// the sliding/resizing panel within the notification window
@@ -640,10 +639,7 @@
private int mLastLoggedStateFingerprint;
private boolean mTopHidesStatusBar;
private boolean mStatusBarWindowHidden;
- private boolean mHideIconsForBouncer;
private boolean mIsOccluded;
- private boolean mWereIconsJustHidden;
- private boolean mBouncerWasShowingWhenHidden;
private boolean mIsLaunchingActivityOverLockscreen;
private final UserSwitcherController mUserSwitcherController;
@@ -776,6 +772,7 @@
SystemStatusAnimationScheduler animationScheduler,
StatusBarLocationPublisher locationPublisher,
StatusBarIconController statusBarIconController,
+ StatusBarHideIconsForBouncerManager statusBarHideIconsForBouncerManager,
LockscreenShadeTransitionController lockscreenShadeTransitionController,
FeatureFlags featureFlags,
KeyguardUnlockAnimationController keyguardUnlockAnimationController,
@@ -872,6 +869,7 @@
mAnimationScheduler = animationScheduler;
mStatusBarLocationPublisher = locationPublisher;
mStatusBarIconController = statusBarIconController;
+ mStatusBarHideIconsForBouncerManager = statusBarHideIconsForBouncerManager;
mFeatureFlags = featureFlags;
mKeyguardUnlockAnimationController = keyguardUnlockAnimationController;
mMainHandler = mainHandler;
@@ -937,6 +935,7 @@
mDisplay = mContext.getDisplay();
mDisplayId = mDisplay.getDisplayId();
updateDisplaySize();
+ mStatusBarHideIconsForBouncerManager.setDisplayId(mDisplayId);
// start old BaseStatusBar.start().
mWindowManagerService = WindowManagerGlobal.getWindowManagerService();
@@ -1140,8 +1139,6 @@
PhoneStatusBarView oldStatusBarView = mStatusBarView;
mStatusBarView = (PhoneStatusBarView) statusBarFragment.getView();
- mStatusBarView.setBar(this);
- mStatusBarView.setScrimController(mScrimController);
mPhoneStatusBarViewController = mPhoneStatusBarViewControllerFactory
.create(mStatusBarView, mNotificationPanelViewController
@@ -1197,6 +1194,7 @@
mPanelExpansionStateManager,
mFeatureFlags,
mStatusBarIconController,
+ mStatusBarHideIconsForBouncerManager,
mKeyguardStateController,
mNetworkController,
mStatusBarStateController,
@@ -1872,7 +1870,7 @@
mNotificationLogger.onPanelExpandedChanged(isExpanded);
}
mPanelExpanded = isExpanded;
- updateHideIconsForBouncer(false /* animate */);
+ mStatusBarHideIconsForBouncerManager.setPanelExpandedAndTriggerUpdate(isExpanded);
mNotificationShadeWindowController.setPanelExpanded(isExpanded);
mStatusBarStateController.setPanelExpanded(isExpanded);
if (isExpanded && mStatusBarStateController.getState() != StatusBarState.KEYGUARD) {
@@ -1916,46 +1914,8 @@
public void setOccluded(boolean occluded) {
mIsOccluded = occluded;
+ mStatusBarHideIconsForBouncerManager.setIsOccludedAndTriggerUpdate(occluded);
mScrimController.setKeyguardOccluded(occluded);
- updateHideIconsForBouncer(false /* animate */);
- }
-
- public boolean hideStatusBarIconsForBouncer() {
- return mHideIconsForBouncer || mWereIconsJustHidden;
- }
-
- /**
- * Decides if the status bar (clock + notifications + signal cluster) should be visible
- * or not when showing the bouncer.
- *
- * We want to hide it when:
- * • User swipes up on the keyguard
- * • Locked activity that doesn't show a status bar requests the bouncer
- *
- * @param animate should the change of the icons be animated.
- */
- void updateHideIconsForBouncer(boolean animate) {
- boolean hideBecauseApp = mTopHidesStatusBar && mIsOccluded
- && (mStatusBarWindowHidden || mBouncerShowing);
- boolean hideBecauseKeyguard = !mPanelExpanded && !mIsOccluded && mBouncerShowing;
- boolean shouldHideIconsForBouncer = hideBecauseApp || hideBecauseKeyguard;
- if (mHideIconsForBouncer != shouldHideIconsForBouncer) {
- mHideIconsForBouncer = shouldHideIconsForBouncer;
- if (!shouldHideIconsForBouncer && mBouncerWasShowingWhenHidden) {
- // We're delaying the showing, since most of the time the fullscreen app will
- // hide the icons again and we don't want them to fade in and out immediately again.
- mWereIconsJustHidden = true;
- mMainExecutor.executeDelayed(() -> {
- mWereIconsJustHidden = false;
- mCommandQueue.recomputeDisableFlags(mDisplayId, true);
- }, 500);
- } else {
- mCommandQueue.recomputeDisableFlags(mDisplayId, animate);
- }
- }
- if (shouldHideIconsForBouncer) {
- mBouncerWasShowingWhenHidden = mBouncerShowing;
- }
}
public boolean headsUpShouldBeVisible() {
@@ -3500,7 +3460,7 @@
mKeyguardBypassController.setBouncerShowing(bouncerShowing);
mPulseExpansionHandler.setBouncerShowing(bouncerShowing);
setBouncerShowingForStatusBarComponents(bouncerShowing);
- updateHideIconsForBouncer(true /* animate */);
+ mStatusBarHideIconsForBouncerManager.setBouncerShowingAndTriggerUpdate(bouncerShowing);
mCommandQueue.recomputeDisableFlags(mDisplayId, true /* animate */);
updateScrimController();
if (!mBouncerShowing) {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarCommandQueueCallbacks.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarCommandQueueCallbacks.java
index bb1daa2..a77a097 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarCommandQueueCallbacks.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarCommandQueueCallbacks.java
@@ -95,6 +95,7 @@
private final SysuiStatusBarStateController mStatusBarStateController;
private final NotificationShadeWindowView mNotificationShadeWindowView;
private final NotificationStackScrollLayoutController mNotificationStackScrollLayoutController;
+ private final StatusBarHideIconsForBouncerManager mStatusBarHideIconsForBouncerManager;
private final PowerManager mPowerManager;
private final VibratorHelper mVibratorHelper;
private final Optional<Vibrator> mVibratorOptional;
@@ -132,6 +133,7 @@
SysuiStatusBarStateController statusBarStateController,
NotificationShadeWindowView notificationShadeWindowView,
NotificationStackScrollLayoutController notificationStackScrollLayoutController,
+ StatusBarHideIconsForBouncerManager statusBarHideIconsForBouncerManager,
PowerManager powerManager,
VibratorHelper vibratorHelper,
Optional<Vibrator> vibratorOptional,
@@ -158,6 +160,7 @@
mStatusBarStateController = statusBarStateController;
mNotificationShadeWindowView = notificationShadeWindowView;
mNotificationStackScrollLayoutController = notificationStackScrollLayoutController;
+ mStatusBarHideIconsForBouncerManager = statusBarHideIconsForBouncerManager;
mPowerManager = powerManager;
mVibratorHelper = vibratorHelper;
mVibratorOptional = vibratorOptional;
@@ -509,14 +512,8 @@
@Override
public void setTopAppHidesStatusBar(boolean topAppHidesStatusBar) {
- mStatusBar.setTopHidesStatusBar(topAppHidesStatusBar);
- if (!topAppHidesStatusBar && mStatusBar.getWereIconsJustHidden()) {
- // Immediately update the icon hidden state, since that should only apply if we're
- // staying fullscreen.
- mStatusBar.setWereIconsJustHidden(false);
- mCommandQueue.recomputeDisableFlags(mDisplayId, true);
- }
- mStatusBar.updateHideIconsForBouncer(true /* animate */);
+ mStatusBarHideIconsForBouncerManager
+ .setTopAppHidesStatusBarAndTriggerUpdate(topAppHidesStatusBar);
}
@Override
@@ -534,13 +531,11 @@
if (StatusBar.DEBUG_WINDOW_STATE) {
Log.d(StatusBar.TAG, "Status bar " + windowStateToString(state));
}
- if (mStatusBar.getStatusBarView() != null) {
- if (!showing && mStatusBarStateController.getState() == StatusBarState.SHADE) {
+ if (mStatusBar.getStatusBarView() != null
+ && !showing
+ && mStatusBarStateController.getState() == StatusBarState.SHADE) {
mNotificationPanelViewController.collapsePanel(
false /* animate */, false /* delayed */, 1.0f /* speedUpFactor */);
- }
-
- mStatusBar.updateHideIconsForBouncer(false /* animate */);
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarHideIconsForBouncerManager.kt b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarHideIconsForBouncerManager.kt
new file mode 100644
index 0000000..d2181d0
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarHideIconsForBouncerManager.kt
@@ -0,0 +1,133 @@
+package com.android.systemui.statusbar.phone
+
+import com.android.systemui.Dumpable
+import com.android.systemui.dagger.SysUISingleton
+import com.android.systemui.dagger.qualifiers.Main
+import com.android.systemui.dump.DumpManager
+import com.android.systemui.statusbar.CommandQueue
+import com.android.systemui.util.concurrency.DelayableExecutor
+import java.io.FileDescriptor
+import java.io.PrintWriter
+import javax.inject.Inject
+
+/**
+ * A class that manages if the status bar (clock + notifications + signal cluster) should be visible
+ * or not when showing the bouncer.
+ *
+ * We want to hide it when:
+ * • User swipes up on the keyguard
+ * • Locked activity that doesn't show a status bar requests the bouncer.
+ *
+ * [getShouldHideStatusBarIconsForBouncer] is the main exported method for this class. The other
+ * methods set state variables that are used in the calculation or manually trigger an update.
+ */
+@SysUISingleton
+class StatusBarHideIconsForBouncerManager @Inject constructor(
+ private val commandQueue: CommandQueue,
+ @Main private val mainExecutor: DelayableExecutor,
+ dumpManager: DumpManager
+) : Dumpable {
+ // State variables set by external classes.
+ private var panelExpanded: Boolean = false
+ private var isOccluded: Boolean = false
+ private var bouncerShowing: Boolean = false
+ private var topAppHidesStatusBar: Boolean = false
+ private var statusBarWindowHidden: Boolean = false
+ private var displayId: Int = 0
+
+ // State variables calculated internally.
+ private var hideIconsForBouncer: Boolean = false
+ private var bouncerWasShowingWhenHidden: Boolean = false
+ private var wereIconsJustHidden: Boolean = false
+
+ init {
+ dumpManager.registerDumpable(this)
+ }
+
+ /** Returns true if the status bar icons should be hidden in the bouncer. */
+ fun getShouldHideStatusBarIconsForBouncer(): Boolean {
+ return hideIconsForBouncer || wereIconsJustHidden
+ }
+
+ fun setStatusBarWindowHidden(statusBarWindowHidden: Boolean) {
+ this.statusBarWindowHidden = statusBarWindowHidden
+ }
+
+ fun setDisplayId(displayId: Int) {
+ this.displayId = displayId
+ }
+
+ fun setPanelExpandedAndTriggerUpdate(panelExpanded: Boolean) {
+ this.panelExpanded = panelExpanded
+ updateHideIconsForBouncer(animate = false)
+ }
+
+ fun setIsOccludedAndTriggerUpdate(isOccluded: Boolean) {
+ this.isOccluded = isOccluded
+ updateHideIconsForBouncer(animate = false)
+ }
+
+ fun setBouncerShowingAndTriggerUpdate(bouncerShowing: Boolean) {
+ this.bouncerShowing = bouncerShowing
+ updateHideIconsForBouncer(animate = true)
+ }
+
+ fun setTopAppHidesStatusBarAndTriggerUpdate(topAppHidesStatusBar: Boolean) {
+ this.topAppHidesStatusBar = topAppHidesStatusBar
+ if (!topAppHidesStatusBar && wereIconsJustHidden) {
+ // Immediately update the icon hidden state, since that should only apply if we're
+ // staying fullscreen.
+ wereIconsJustHidden = false
+ commandQueue.recomputeDisableFlags(displayId, /* animate= */ true)
+ }
+ updateHideIconsForBouncer(animate = true)
+ }
+
+ /**
+ * Updates whether the status bar icons should be hidden in the bouncer. May trigger
+ * [commandQueue.recomputeDisableFlags] if the icon visibility status changes.
+ */
+ fun updateHideIconsForBouncer(animate: Boolean) {
+ val hideBecauseApp =
+ topAppHidesStatusBar &&
+ isOccluded &&
+ (statusBarWindowHidden || bouncerShowing)
+ val hideBecauseKeyguard = !panelExpanded && !isOccluded && bouncerShowing
+ val shouldHideIconsForBouncer = hideBecauseApp || hideBecauseKeyguard
+ if (hideIconsForBouncer != shouldHideIconsForBouncer) {
+ hideIconsForBouncer = shouldHideIconsForBouncer
+ if (!shouldHideIconsForBouncer && bouncerWasShowingWhenHidden) {
+ // We're delaying the showing, since most of the time the fullscreen app will
+ // hide the icons again and we don't want them to fade in and out immediately again.
+ wereIconsJustHidden = true
+ mainExecutor.executeDelayed(
+ {
+ wereIconsJustHidden = false
+ commandQueue.recomputeDisableFlags(displayId, true)
+ },
+ 500
+ )
+ } else {
+ commandQueue.recomputeDisableFlags(displayId, animate)
+ }
+ }
+ if (shouldHideIconsForBouncer) {
+ bouncerWasShowingWhenHidden = bouncerShowing
+ }
+ }
+
+ override fun dump(fd: FileDescriptor, pw: PrintWriter, args: Array<out String>) {
+ pw.println("---- State variables set externally ----")
+ pw.println("panelExpanded=$panelExpanded")
+ pw.println("isOccluded=$isOccluded")
+ pw.println("bouncerShowing=$bouncerShowing")
+ pw.println("topAppHideStatusBar=$topAppHidesStatusBar")
+ pw.println("statusBarWindowHidden=$statusBarWindowHidden")
+ pw.println("displayId=$displayId")
+
+ pw.println("---- State variables calculated internally ----")
+ pw.println("hideIconsForBouncer=$hideIconsForBouncer")
+ pw.println("bouncerWasShowingWhenHidden=$bouncerWasShowingWhenHidden")
+ pw.println("wereIconsJustHidden=$wereIconsJustHidden")
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarNotificationActivityStarter.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarNotificationActivityStarter.java
index dba3b24..c2e790f 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarNotificationActivityStarter.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarNotificationActivityStarter.java
@@ -68,8 +68,8 @@
import com.android.systemui.statusbar.notification.collection.NotificationEntry;
import com.android.systemui.statusbar.notification.collection.notifcollection.NotifCollectionListener;
import com.android.systemui.statusbar.notification.collection.render.GroupMembershipManager;
+import com.android.systemui.statusbar.notification.collection.render.NotificationVisibilityProvider;
import com.android.systemui.statusbar.notification.interruption.NotificationInterruptStateProvider;
-import com.android.systemui.statusbar.notification.logging.NotificationLogger;
import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow;
import com.android.systemui.statusbar.notification.row.ExpandableNotificationRowDragController;
import com.android.systemui.statusbar.notification.row.OnUserInteractionCallback;
@@ -97,6 +97,7 @@
private final NotificationEntryManager mEntryManager;
private final NotifPipeline mNotifPipeline;
+ private final NotificationVisibilityProvider mVisibilityProvider;
private final HeadsUpManagerPhone mHeadsUpManager;
private final ActivityStarter mActivityStarter;
private final NotificationClickNotifier mClickNotifier;
@@ -136,6 +137,7 @@
Executor uiBgExecutor,
NotificationEntryManager entryManager,
NotifPipeline notifPipeline,
+ NotificationVisibilityProvider visibilityProvider,
HeadsUpManagerPhone headsUpManager,
ActivityStarter activityStarter,
NotificationClickNotifier clickNotifier,
@@ -171,6 +173,7 @@
mUiBgExecutor = uiBgExecutor;
mEntryManager = entryManager;
mNotifPipeline = notifPipeline;
+ mVisibilityProvider = visibilityProvider;
mHeadsUpManager = headsUpManager;
mActivityStarter = activityStarter;
mClickNotifier = clickNotifier;
@@ -366,10 +369,7 @@
mAssistManagerLazy.get().hideAssist();
}
- NotificationVisibility.NotificationLocation location =
- NotificationLogger.getNotificationLocation(entry);
- final NotificationVisibility nv = NotificationVisibility.obtain(entry.getKey(),
- entry.getRanking().getRank(), getVisibleNotificationsCount(), true, location);
+ final NotificationVisibility nv = mVisibilityProvider.obtain(entry, true);
// retrieve the group summary to remove with this entry before we tell NMS the
// notification was clicked to avoid a race condition
@@ -414,10 +414,7 @@
public void onDragSuccess(NotificationEntry entry) {
// this method is not responsible for intent sending.
// will focus follow operation only after drag-and-drop that notification.
- NotificationVisibility.NotificationLocation location =
- NotificationLogger.getNotificationLocation(entry);
- final NotificationVisibility nv = NotificationVisibility.obtain(entry.getKey(),
- entry.getRanking().getRank(), getVisibleNotificationsCount(), true, location);
+ final NotificationVisibility nv = mVisibilityProvider.obtain(entry, true);
// retrieve the group summary to remove with this entry before we tell NMS the
// notification was clicked to avoid a race condition
@@ -681,6 +678,7 @@
private final Executor mUiBgExecutor;
private final NotificationEntryManager mEntryManager;
private final NotifPipeline mNotifPipeline;
+ private final NotificationVisibilityProvider mVisibilityProvider;
private final HeadsUpManagerPhone mHeadsUpManager;
private final ActivityStarter mActivityStarter;
private final NotificationClickNotifier mClickNotifier;
@@ -719,6 +717,7 @@
@UiBackground Executor uiBgExecutor,
NotificationEntryManager entryManager,
NotifPipeline notifPipeline,
+ NotificationVisibilityProvider visibilityProvider,
HeadsUpManagerPhone headsUpManager,
ActivityStarter activityStarter,
NotificationClickNotifier clickNotifier,
@@ -749,6 +748,7 @@
mUiBgExecutor = uiBgExecutor;
mEntryManager = entryManager;
mNotifPipeline = notifPipeline;
+ mVisibilityProvider = visibilityProvider;
mHeadsUpManager = headsUpManager;
mActivityStarter = activityStarter;
mClickNotifier = clickNotifier;
@@ -813,6 +813,7 @@
mUiBgExecutor,
mEntryManager,
mNotifPipeline,
+ mVisibilityProvider,
mHeadsUpManager,
mActivityStarter,
mClickNotifier,
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarSignalPolicy.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarSignalPolicy.java
index fbd9ef7..9c69f51 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarSignalPolicy.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarSignalPolicy.java
@@ -16,6 +16,7 @@
package com.android.systemui.statusbar.phone;
+import android.annotation.NonNull;
import android.content.Context;
import android.os.Handler;
import android.telephony.SubscriptionInfo;
@@ -26,11 +27,11 @@
import com.android.systemui.R;
import com.android.systemui.dagger.SysUISingleton;
import com.android.systemui.flags.FeatureFlags;
+import com.android.systemui.statusbar.connectivity.IconState;
+import com.android.systemui.statusbar.connectivity.MobileDataIndicators;
import com.android.systemui.statusbar.connectivity.NetworkController;
-import com.android.systemui.statusbar.connectivity.NetworkController.IconState;
-import com.android.systemui.statusbar.connectivity.NetworkController.MobileDataIndicators;
-import com.android.systemui.statusbar.connectivity.NetworkController.WifiIndicators;
-import com.android.systemui.statusbar.connectivity.NetworkControllerImpl;
+import com.android.systemui.statusbar.connectivity.SignalCallback;
+import com.android.systemui.statusbar.connectivity.WifiIndicators;
import com.android.systemui.statusbar.policy.SecurityController;
import com.android.systemui.tuner.TunerService;
import com.android.systemui.tuner.TunerService.Tunable;
@@ -44,7 +45,7 @@
/** Controls the signal policies for icons shown in the StatusBar. **/
@SysUISingleton
-public class StatusBarSignalPolicy implements NetworkControllerImpl.SignalCallback,
+public class StatusBarSignalPolicy implements SignalCallback,
SecurityController.SecurityControllerCallback, Tunable {
private static final String TAG = "StatusBarSignalPolicy";
private static final boolean DEBUG = Log.isLoggable(TAG, Log.DEBUG);
@@ -169,7 +170,7 @@
}
@Override
- public void setWifiIndicators(WifiIndicators indicators) {
+ public void setWifiIndicators(@NonNull WifiIndicators indicators) {
if (DEBUG) {
Log.d(TAG, "setWifiIndicators: " + indicators);
}
@@ -219,7 +220,7 @@
}
@Override
- public void setCallIndicator(IconState statusIcon, int subId) {
+ public void setCallIndicator(@NonNull IconState statusIcon, int subId) {
if (DEBUG) {
Log.d(TAG, "setCallIndicator: "
+ "statusIcon = " + statusIcon + ","
@@ -247,7 +248,7 @@
}
@Override
- public void setMobileDataIndicators(MobileDataIndicators indicators) {
+ public void setMobileDataIndicators(@NonNull MobileDataIndicators indicators) {
if (DEBUG) {
Log.d(TAG, "setMobileDataIndicators: " + indicators);
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/dagger/StatusBarPhoneModule.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/dagger/StatusBarPhoneModule.java
index 5a3cb6f..3259f6b 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/dagger/StatusBarPhoneModule.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/dagger/StatusBarPhoneModule.java
@@ -92,6 +92,7 @@
import com.android.systemui.statusbar.phone.ScrimController;
import com.android.systemui.statusbar.phone.ShadeController;
import com.android.systemui.statusbar.phone.StatusBar;
+import com.android.systemui.statusbar.phone.StatusBarHideIconsForBouncerManager;
import com.android.systemui.statusbar.phone.StatusBarIconController;
import com.android.systemui.statusbar.phone.StatusBarKeyguardViewManager;
import com.android.systemui.statusbar.phone.StatusBarLocationPublisher;
@@ -224,6 +225,7 @@
SystemStatusAnimationScheduler animationScheduler,
StatusBarLocationPublisher locationPublisher,
StatusBarIconController statusBarIconController,
+ StatusBarHideIconsForBouncerManager statusBarHideIconsForBouncerManager,
LockscreenShadeTransitionController transitionController,
FeatureFlags featureFlags,
KeyguardUnlockAnimationController keyguardUnlockAnimationController,
@@ -322,6 +324,7 @@
animationScheduler,
locationPublisher,
statusBarIconController,
+ statusBarHideIconsForBouncerManager,
transitionController,
featureFlags,
keyguardUnlockAnimationController,
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/dagger/StatusBarPolicyModule.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/dagger/StatusBarPolicyModule.java
index 9290879..b6a96a7 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/dagger/StatusBarPolicyModule.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/dagger/StatusBarPolicyModule.java
@@ -23,6 +23,7 @@
import com.android.systemui.dagger.SysUISingleton;
import com.android.systemui.dagger.qualifiers.Main;
import com.android.systemui.settings.UserTracker;
+import com.android.systemui.statusbar.connectivity.AccessPointController;
import com.android.systemui.statusbar.connectivity.AccessPointControllerImpl;
import com.android.systemui.statusbar.connectivity.NetworkController;
import com.android.systemui.statusbar.connectivity.NetworkControllerImpl;
@@ -135,7 +136,7 @@
/** */
@Binds
- NetworkController.AccessPointController provideAccessPointController(
+ AccessPointController provideAccessPointController(
AccessPointControllerImpl accessPointControllerImpl);
/** */
diff --git a/packages/SystemUI/src/com/android/systemui/unfold/SysUIUnfoldModule.kt b/packages/SystemUI/src/com/android/systemui/unfold/SysUIUnfoldModule.kt
index cc2c208..65106f1 100644
--- a/packages/SystemUI/src/com/android/systemui/unfold/SysUIUnfoldModule.kt
+++ b/packages/SystemUI/src/com/android/systemui/unfold/SysUIUnfoldModule.kt
@@ -41,7 +41,9 @@
* no objects will get constructed if these parameters are empty.
*/
@Module(subcomponents = [SysUIUnfoldComponent::class])
-object SysUIUnfoldModule {
+class SysUIUnfoldModule {
+ constructor() {}
+
@Provides
@SysUISingleton
fun provideSysUIUnfoldComponent(
diff --git a/packages/SystemUI/src/com/android/systemui/wmshell/BubblesManager.java b/packages/SystemUI/src/com/android/systemui/wmshell/BubblesManager.java
index 291c64d..4aad9b6 100644
--- a/packages/SystemUI/src/com/android/systemui/wmshell/BubblesManager.java
+++ b/packages/SystemUI/src/com/android/systemui/wmshell/BubblesManager.java
@@ -76,8 +76,8 @@
import com.android.systemui.statusbar.notification.collection.legacy.NotificationGroupManagerLegacy;
import com.android.systemui.statusbar.notification.collection.notifcollection.DismissedByUserStats;
import com.android.systemui.statusbar.notification.collection.notifcollection.NotifCollectionListener;
+import com.android.systemui.statusbar.notification.collection.render.NotificationVisibilityProvider;
import com.android.systemui.statusbar.notification.interruption.NotificationInterruptStateProvider;
-import com.android.systemui.statusbar.notification.logging.NotificationLogger;
import com.android.systemui.statusbar.phone.ShadeController;
import com.android.systemui.statusbar.policy.ConfigurationController;
import com.android.systemui.statusbar.policy.ZenModeController;
@@ -109,6 +109,7 @@
private final ShadeController mShadeController;
private final IStatusBarService mBarService;
private final INotificationManager mNotificationManager;
+ private final NotificationVisibilityProvider mVisibilityProvider;
private final NotificationInterruptStateProvider mNotificationInterruptStateProvider;
private final NotificationGroupManagerLegacy mNotificationGroupManager;
private final NotificationEntryManager mNotificationEntryManager;
@@ -132,6 +133,7 @@
ConfigurationController configurationController,
@Nullable IStatusBarService statusBarService,
INotificationManager notificationManager,
+ NotificationVisibilityProvider visibilityProvider,
NotificationInterruptStateProvider interruptionStateProvider,
ZenModeController zenModeController,
NotificationLockscreenUserManager notifUserManager,
@@ -146,6 +148,7 @@
return new BubblesManager(context, bubblesOptional.get(),
notificationShadeWindowController, statusBarStateController, shadeController,
configurationController, statusBarService, notificationManager,
+ visibilityProvider,
interruptionStateProvider, zenModeController, notifUserManager,
groupManager, entryManager, notifPipeline, sysUiState, featureFlags,
dumpManager, sysuiMainExecutor);
@@ -163,6 +166,7 @@
ConfigurationController configurationController,
@Nullable IStatusBarService statusBarService,
INotificationManager notificationManager,
+ NotificationVisibilityProvider visibilityProvider,
NotificationInterruptStateProvider interruptionStateProvider,
ZenModeController zenModeController,
NotificationLockscreenUserManager notifUserManager,
@@ -178,6 +182,7 @@
mNotificationShadeWindowController = notificationShadeWindowController;
mShadeController = shadeController;
mNotificationManager = notificationManager;
+ mVisibilityProvider = visibilityProvider;
mNotificationInterruptStateProvider = interruptionStateProvider;
mNotificationGroupManager = groupManager;
mNotificationEntryManager = entryManager;
@@ -598,12 +603,7 @@
return new DismissedByUserStats(
DISMISSAL_BUBBLE,
DISMISS_SENTIMENT_NEUTRAL,
- NotificationVisibility.obtain(
- entry.getKey(),
- entry.getRanking().getRank(),
- mNotificationEntryManager.getActiveNotificationsCount(),
- isVisible,
- NotificationLogger.getNotificationLocation(entry)));
+ mVisibilityProvider.obtain(entry, isVisible));
}
/**
diff --git a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardUpdateMonitorTest.java b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardUpdateMonitorTest.java
index e53b450..1ee6f70 100644
--- a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardUpdateMonitorTest.java
+++ b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardUpdateMonitorTest.java
@@ -87,7 +87,6 @@
import com.android.systemui.biometrics.AuthController;
import com.android.systemui.broadcast.BroadcastDispatcher;
import com.android.systemui.dump.DumpManager;
-import com.android.systemui.flags.FeatureFlags;
import com.android.systemui.plugins.statusbar.StatusBarStateController;
import com.android.systemui.statusbar.StatusBarState;
import com.android.systemui.statusbar.phone.KeyguardBypassController;
@@ -170,8 +169,6 @@
@Mock
private TelephonyListenerManager mTelephonyListenerManager;
@Mock
- private FeatureFlags mFeatureFlags;
- @Mock
private InteractionJankMonitor mInteractionJankMonitor;
@Mock
private LatencyTracker mLatencyTracker;
@@ -179,6 +176,7 @@
private ArgumentCaptor<StatusBarStateController.StateListener> mStatusBarStateListenerCaptor;
// Direct executor
private Executor mBackgroundExecutor = Runnable::run;
+ private Executor mMainExecutor = Runnable::run;
private TestableLooper mTestableLooper;
private TestableKeyguardUpdateMonitor mKeyguardUpdateMonitor;
private TestableContext mSpiedContext;
@@ -882,6 +880,25 @@
}
@Test
+ public void testRegisterAuthControllerCallback() {
+ assertThat(mKeyguardUpdateMonitor.isUdfpsEnrolled()).isFalse();
+
+ // verify AuthController.Callback is added:
+ ArgumentCaptor<AuthController.Callback> captor = ArgumentCaptor.forClass(
+ AuthController.Callback.class);
+ verify(mAuthController).addCallback(captor.capture());
+ AuthController.Callback callback = captor.getValue();
+
+ // WHEN udfps is now enrolled
+ when(mAuthController.isUdfpsEnrolled(anyInt())).thenReturn(true);
+ callback.onEnrollmentsChanged();
+
+ // THEN isUdfspEnrolled is TRUE
+ assertThat(mKeyguardUpdateMonitor.isUdfpsEnrolled()).isTrue();
+ }
+
+
+ @Test
public void testStartUdfpsServiceBeginsOnKeyguard() {
// GIVEN
// - status bar state is on the keyguard
@@ -1060,9 +1077,9 @@
super(context,
TestableLooper.get(KeyguardUpdateMonitorTest.this).getLooper(),
mBroadcastDispatcher, mDumpManager,
- mRingerModeTracker, mBackgroundExecutor,
+ mRingerModeTracker, mBackgroundExecutor, mMainExecutor,
mStatusBarStateController, mLockPatternUtils,
- mAuthController, mTelephonyListenerManager, mFeatureFlags,
+ mAuthController, mTelephonyListenerManager,
mInteractionJankMonitor, mLatencyTracker);
setStrongAuthTracker(KeyguardUpdateMonitorTest.this.mStrongAuthTracker);
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/animation/DialogLaunchAnimatorTest.kt b/packages/SystemUI/tests/src/com/android/systemui/animation/DialogLaunchAnimatorTest.kt
index 5427806..209df6b 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/animation/DialogLaunchAnimatorTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/animation/DialogLaunchAnimatorTest.kt
@@ -60,9 +60,20 @@
assertEquals(0, dialog.findViewById<ViewGroup>(android.R.id.content).childCount)
assertEquals(1, hostDialogContent.childCount)
+ // The original dialog content is added to another view that is the same size as the
+ // original dialog window.
val hostDialogRoot = hostDialogContent.getChildAt(0) as ViewGroup
assertEquals(1, hostDialogRoot.childCount)
- assertEquals(dialog.contentView, hostDialogRoot.getChildAt(0))
+
+ val dialogContentParent = hostDialogRoot.getChildAt(0) as ViewGroup
+ assertEquals(1, dialogContentParent.childCount)
+ assertEquals(TestDialog.DIALOG_WIDTH, dialogContentParent.layoutParams.width)
+ assertEquals(TestDialog.DIALOG_HEIGHT, dialogContentParent.layoutParams.height)
+
+ val dialogContent = dialogContentParent.getChildAt(0)
+ assertEquals(dialog.contentView, dialogContent)
+ assertEquals(ViewGroup.LayoutParams.MATCH_PARENT, dialogContent.layoutParams.width)
+ assertEquals(ViewGroup.LayoutParams.MATCH_PARENT, dialogContent.layoutParams.height)
// Hiding/showing/dismissing the dialog should hide/show/dismiss the host dialog given that
// it's a ListenableDialog.
@@ -126,6 +137,11 @@
}
private class TestDialog(context: Context) : Dialog(context), ListenableDialog {
+ companion object {
+ const val DIALOG_WIDTH = 100
+ const val DIALOG_HEIGHT = 200
+ }
+
private val listeners = hashSetOf<DialogListener>()
val contentView = View(context)
var onStartCalled = false
@@ -138,6 +154,7 @@
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
+ window.setLayout(DIALOG_WIDTH, DIALOG_HEIGHT)
setContentView(contentView)
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/flags/FeatureFlagReaderTest.java b/packages/SystemUI/tests/src/com/android/systemui/flags/FeatureFlagReaderTest.java
deleted file mode 100644
index 52d08dc..0000000
--- a/packages/SystemUI/tests/src/com/android/systemui/flags/FeatureFlagReaderTest.java
+++ /dev/null
@@ -1,180 +0,0 @@
-/*
- * 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.flags;
-
-import static com.google.common.truth.Truth.assertThat;
-
-import static org.junit.Assert.assertFalse;
-import static org.junit.Assert.assertTrue;
-import static org.mockito.ArgumentMatchers.anyBoolean;
-import static org.mockito.ArgumentMatchers.anyString;
-import static org.mockito.ArgumentMatchers.eq;
-import static org.mockito.Mockito.mock;
-import static org.mockito.Mockito.times;
-import static org.mockito.Mockito.verify;
-import static org.mockito.Mockito.when;
-
-import android.content.res.Resources;
-
-import androidx.annotation.BoolRes;
-import androidx.test.filters.SmallTest;
-
-import com.android.systemui.R;
-import com.android.systemui.SysuiTestCase;
-import com.android.systemui.dump.DumpManager;
-import com.android.systemui.shared.plugins.PluginManager;
-import com.android.systemui.util.wrapper.BuildInfo;
-
-import org.junit.Before;
-import org.junit.Test;
-import org.mockito.Mock;
-import org.mockito.MockitoAnnotations;
-
-import java.io.FileDescriptor;
-import java.io.PrintWriter;
-import java.io.StringWriter;
-
-@SmallTest
-public class FeatureFlagReaderTest extends SysuiTestCase {
- @Mock private Resources mResources;
- @Mock private BuildInfo mBuildInfo;
- @Mock private DumpManager mDumpManager;
- @Mock private PluginManager mPluginManager;
- @Mock private SystemPropertiesHelper mSystemPropertiesHelper;
- @Mock private FlagReader mFlagReader;
-
- private FeatureFlagReader mReader;
-
- @Before
- public void setUp() {
- MockitoAnnotations.initMocks(this);
-
- when(mSystemPropertiesHelper.getBoolean(anyString(), anyBoolean()))
- .thenAnswer(invocation -> invocation.getArgument(1));
-
- defineFlag(FLAG_RESID_0, false);
- defineFlag(FLAG_RESID_1, true);
-
- initialize(true, true);
- }
-
- private void initialize(boolean isDebuggable, boolean isOverrideable) {
- when(mBuildInfo.isDebuggable()).thenReturn(isDebuggable);
- when(mResources.getBoolean(R.bool.are_flags_overrideable)).thenReturn(isOverrideable);
- mReader = new FeatureFlagReader(
- mResources, mBuildInfo, mDumpManager, mSystemPropertiesHelper, mFlagReader);
- }
-
- @Test
- public void testCantOverrideIfNotDebuggable() {
- // GIVEN that the build is not debuggable
- initialize(false, true);
-
- // GIVEN that a flag has been overridden to true
- overrideFlag(FLAG_RESID_0, true);
-
- // THEN the flag is still false
- assertFalse(mReader.isEnabled(FLAG_RESID_0));
- }
-
- @Test
- public void testCantOverrideIfNotOverrideable() {
- // GIVEN that flags are not overrideable
- initialize(true, false);
-
- // GIVEN that a flag has been overridden to true
- overrideFlag(FLAG_RESID_0, true);
-
- // THEN the flag is still false
- assertFalse(mReader.isEnabled(FLAG_RESID_0));
- }
-
- @Test
- public void testReadFlags() {
- assertFalse(mReader.isEnabled(FLAG_RESID_0));
- assertTrue(mReader.isEnabled(FLAG_RESID_1));
- }
-
- @Test
- public void testOverrideFlags() {
- // GIVEN that flags are overridden
- overrideFlag(FLAG_RESID_0, true);
- overrideFlag(FLAG_RESID_1, false);
-
- // THEN the reader returns the overridden values
- assertTrue(mReader.isEnabled(FLAG_RESID_0));
- assertFalse(mReader.isEnabled(FLAG_RESID_1));
- }
-
- @Test
- public void testThatFlagReadsAreCached() {
- // GIVEN that a flag is overridden
- overrideFlag(FLAG_RESID_0, true);
-
- // WHEN the flag is queried many times
- mReader.isEnabled(FLAG_RESID_0);
- mReader.isEnabled(FLAG_RESID_0);
- mReader.isEnabled(FLAG_RESID_0);
- mReader.isEnabled(FLAG_RESID_0);
-
- // THEN the underlying resource and override are only queried once
- verify(mResources, times(1)).getBoolean(FLAG_RESID_0);
- verify(mSystemPropertiesHelper, times(1))
- .getBoolean(fakeStorageKey(FLAG_RESID_0), false);
- }
-
- @Test
- public void testDump() {
- // GIVEN that the flag 0 (by override) and 1 (by default) are both true
- overrideFlag(FLAG_RESID_0, true);
-
- // WHEN the flags have been accessed
- assertTrue(mReader.isEnabled(FLAG_RESID_0));
- assertTrue(mReader.isEnabled(FLAG_RESID_1));
-
- // THEN the dump contains the flags and their correct values
- StringWriter sw = new StringWriter();
- PrintWriter pw = new PrintWriter(sw);
- mReader.dump(mock(FileDescriptor.class), pw, new String[0]);
- pw.flush();
- String dump = sw.toString();
- assertThat(dump).contains(" flag_testname_" + FLAG_RESID_0 + ": true\n");
- assertThat(dump).contains(" flag_testname_" + FLAG_RESID_1 + ": true\n");
- assertThat(dump).contains("AreFlagsOverrideable: true\n");
- }
-
- private void defineFlag(int resId, boolean value) {
- when(mResources.getBoolean(resId)).thenReturn(value);
- when(mResources.getResourceEntryName(resId)).thenReturn(fakeResourceEntryName(resId));
- }
-
- private void overrideFlag(int resId, boolean value) {
- when(mSystemPropertiesHelper.getBoolean(eq(fakeStorageKey(resId)), anyBoolean()))
- .thenReturn(value);
- }
-
- private String fakeResourceEntryName(@BoolRes int resId) {
- return "flag_testname_" + resId;
- }
-
- private String fakeStorageKey(@BoolRes int resId) {
- return "persist.systemui.flag_testname_" + resId;
- }
-
- private static final int FLAG_RESID_0 = 47;
- private static final int FLAG_RESID_1 = 48;
-}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/flags/FeatureFlagsTest.java b/packages/SystemUI/tests/src/com/android/systemui/flags/FeatureFlagsTest.java
index a850f70..30e9b51 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/flags/FeatureFlagsTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/flags/FeatureFlagsTest.java
@@ -18,7 +18,12 @@
import static com.google.common.truth.Truth.assertThat;
+import static org.mockito.ArgumentMatchers.anyBoolean;
+import static org.mockito.ArgumentMatchers.anyInt;
import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.when;
+
+import android.content.res.Resources;
import androidx.test.filters.SmallTest;
@@ -29,11 +34,13 @@
import org.mockito.ArgumentCaptor;
import org.mockito.Mock;
import org.mockito.MockitoAnnotations;
+import org.mockito.stubbing.Answer;
@SmallTest
public class FeatureFlagsTest extends SysuiTestCase {
- @Mock FeatureFlagReader mFeatureFlagReader;
+ @Mock Resources mResources;
+ @Mock FlagReader mFeatureFlagReader;
private FeatureFlags mFeatureFlags;
@@ -41,7 +48,10 @@
public void setup() {
MockitoAnnotations.initMocks(this);
- mFeatureFlags = new FeatureFlags(mFeatureFlagReader, getContext());
+ when(mFeatureFlagReader.isEnabled(anyInt(), anyBoolean())).thenAnswer(
+ (Answer<Boolean>) invocation -> invocation.getArgument(1));
+
+ mFeatureFlags = new FeatureFlags(mResources, mFeatureFlagReader, getContext());
}
@Test
@@ -103,6 +113,26 @@
pluginListener.onFlagChanged(flag.getId());
// Assert that the change was not triggered
assertThat(changedFlag[0]).isNull();
+ }
+ @Test
+ public void testBooleanDefault() {
+ BooleanFlag flag = new BooleanFlag(1, true);
+
+ mFeatureFlags.addFlag(flag);
+
+ assertThat(mFeatureFlags.isEnabled(flag)).isTrue();
+ }
+
+ @Test
+ public void testBooleanResourceOverlay() {
+ int resourceId = 12;
+ BooleanFlag flag = new BooleanFlag(1, false, resourceId);
+ when(mResources.getBoolean(resourceId)).thenReturn(true);
+ when(mResources.getResourceEntryName(resourceId)).thenReturn("flag");
+
+ mFeatureFlags.addFlag(flag);
+
+ assertThat(mFeatureFlags.isEnabled(flag)).isTrue();
}
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/people/widget/LaunchConversationActivityTest.java b/packages/SystemUI/tests/src/com/android/systemui/people/widget/LaunchConversationActivityTest.java
index d8ba164..99f21ad 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/people/widget/LaunchConversationActivityTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/people/widget/LaunchConversationActivityTest.java
@@ -21,7 +21,9 @@
import static com.google.common.truth.Truth.assertThat;
import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.ArgumentMatchers.anyBoolean;
import static org.mockito.ArgumentMatchers.anyInt;
+import static org.mockito.ArgumentMatchers.anyString;
import static org.mockito.ArgumentMatchers.eq;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.never;
@@ -43,8 +45,9 @@
import com.android.internal.statusbar.NotificationVisibility;
import com.android.systemui.SysuiTestCase;
import com.android.systemui.statusbar.CommandQueue;
-import com.android.systemui.statusbar.notification.NotificationEntryManager;
import com.android.systemui.statusbar.notification.collection.NotificationEntry;
+import com.android.systemui.statusbar.notification.collection.notifcollection.CommonNotifCollection;
+import com.android.systemui.statusbar.notification.collection.render.NotificationVisibilityProvider;
import com.android.systemui.wmshell.BubblesManager;
import com.android.wm.shell.bubbles.Bubble;
@@ -76,7 +79,9 @@
private LaunchConversationActivity mActivity;
@Mock
- private NotificationEntryManager mNotificationEntryManager;
+ private NotificationVisibilityProvider mVisibilityProvider;
+ @Mock
+ private CommonNotifCollection mNotifCollection;
@Mock
private IStatusBarService mIStatusBarService;
@Mock
@@ -104,8 +109,13 @@
@Before
public void setUp() throws Exception {
MockitoAnnotations.initMocks(this);
- mActivity = new LaunchConversationActivity(mNotificationEntryManager,
- Optional.of(mBubblesManager), mUserManager, mCommandQueue);
+ mActivity = new LaunchConversationActivity(
+ mVisibilityProvider,
+ mNotifCollection,
+ Optional.of(mBubblesManager),
+ mUserManager,
+ mCommandQueue
+ );
verify(mCommandQueue, times(1)).addCallback(mCallbacksCaptor.capture());
mActivity.setIsForTesting(true, mIStatusBarService);
@@ -114,19 +124,26 @@
mIntent.putExtra(PeopleSpaceWidgetProvider.EXTRA_PACKAGE_NAME, PACKAGE_NAME);
mIntent.putExtra(PeopleSpaceWidgetProvider.EXTRA_USER_HANDLE, USER_HANDLE);
- when(mNotificationEntryManager.getActiveNotificationsCount()).thenReturn(NOTIF_COUNT);
- when(mNotificationEntryManager.getPendingOrActiveNotif(NOTIF_KEY)).thenReturn(mNotifEntry);
- when(mNotificationEntryManager.getPendingOrActiveNotif(NOTIF_KEY_NO_ENTRY))
- .thenReturn(null);
- when(mNotificationEntryManager.getPendingOrActiveNotif(NOTIF_KEY_NO_RANKING))
- .thenReturn(mNotifEntryNoRanking);
- when(mNotificationEntryManager.getPendingOrActiveNotif(NOTIF_KEY_CAN_BUBBLE))
- .thenReturn(mNotifEntryCanBubble);
+ when(mNotifCollection.getEntry(NOTIF_KEY)).thenReturn(mNotifEntry);
+ when(mNotifCollection.getEntry(NOTIF_KEY_NO_ENTRY)).thenReturn(null);
+ when(mNotifCollection.getEntry(NOTIF_KEY_NO_RANKING)).thenReturn(mNotifEntryNoRanking);
+ when(mNotifCollection.getEntry(NOTIF_KEY_CAN_BUBBLE)).thenReturn(mNotifEntryCanBubble);
+ when(mVisibilityProvider.obtain(anyString(), anyBoolean())).thenAnswer(
+ invocation-> {
+ String key = invocation.getArgument(0);
+ boolean visible = invocation.getArgument(1);
+ return NotificationVisibility.obtain(key, NOTIF_RANK, NOTIF_COUNT, visible);
+ });
+ when(mVisibilityProvider.obtain(any(NotificationEntry.class), anyBoolean())).thenAnswer(
+ invocation-> {
+ String key = invocation.<NotificationEntry>getArgument(0).getKey();
+ boolean visible = invocation.getArgument(1);
+ return NotificationVisibility.obtain(key, NOTIF_RANK, NOTIF_COUNT, visible);
+ });
when(mNotifEntry.getRanking()).thenReturn(mRanking);
when(mNotifEntryCanBubble.getRanking()).thenReturn(mRanking);
when(mNotifEntryCanBubble.canBubble()).thenReturn(true);
when(mNotifEntryNoRanking.getRanking()).thenReturn(null);
- when(mRanking.getRank()).thenReturn(NOTIF_RANK);
when(mUserManager.isQuietModeEnabled(any(UserHandle.class))).thenReturn(false);
}
@@ -173,8 +190,12 @@
assertThat(mActivity.isFinishing()).isTrue();
mCallbacksCaptor.getValue().appTransitionFinished(DEFAULT_DISPLAY);
+ // Ensure callback removed
+ verify(mCommandQueue).removeCallback(any());
+ // Clear the notification for bubbles.
verify(mIStatusBarService, times(1)).onNotificationClear(any(),
anyInt(), any(), anyInt(), anyInt(), mNotificationVisibilityCaptor.capture());
+ // Do not select the bubble.
verify(mBubblesManager, never()).expandStackAndSelectBubble(any(Bubble.class));
verify(mBubblesManager, never()).expandStackAndSelectBubble(any(NotificationEntry.class));
@@ -194,6 +215,8 @@
assertThat(mActivity.isFinishing()).isTrue();
mCallbacksCaptor.getValue().appTransitionFinished(DEFAULT_DISPLAY);
+ // Ensure callback removed
+ verify(mCommandQueue).removeCallback(any());
// Don't clear the notification for bubbles.
verify(mIStatusBarService, never()).onNotificationClear(any(),
anyInt(), any(), anyInt(), anyInt(), any());
@@ -211,10 +234,14 @@
mActivity.onCreate(new Bundle());
assertThat(mActivity.isFinishing()).isTrue();
- mCommandQueue.appTransitionFinished(DEFAULT_DISPLAY);
+ mCallbacksCaptor.getValue().appTransitionFinished(DEFAULT_DISPLAY);
+ // Ensure callback removed
+ verify(mCommandQueue).removeCallback(any());
+ // Don't clear the notification for bubbles.
verify(mIStatusBarService, never()).onNotificationClear(any(),
anyInt(), any(), anyInt(), anyInt(), any());
+ // Do not select the bubble.
verify(mBubblesManager, never()).expandStackAndSelectBubble(any(Bubble.class));
verify(mBubblesManager, never()).expandStackAndSelectBubble(any(NotificationEntry.class));
}
@@ -233,6 +260,9 @@
assertThat(mActivity.isFinishing()).isTrue();
mCallbacksCaptor.getValue().appTransitionFinished(DEFAULT_DISPLAY);
+ // Ensure callback removed
+ verify(mCommandQueue).removeCallback(any());
+ // Select the bubble.
verify(mBubblesManager, times(1)).expandStackAndSelectBubble(eq(bubble));
}
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/carrier/QSCarrierGroupControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/qs/carrier/QSCarrierGroupControllerTest.java
index 8ae7100..bd794d6 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/qs/carrier/QSCarrierGroupControllerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/qs/carrier/QSCarrierGroupControllerTest.java
@@ -39,8 +39,10 @@
import com.android.keyguard.CarrierTextManager;
import com.android.systemui.flags.FeatureFlags;
import com.android.systemui.plugins.ActivityStarter;
+import com.android.systemui.statusbar.connectivity.IconState;
+import com.android.systemui.statusbar.connectivity.MobileDataIndicators;
import com.android.systemui.statusbar.connectivity.NetworkController;
-import com.android.systemui.statusbar.connectivity.NetworkController.MobileDataIndicators;
+import com.android.systemui.statusbar.connectivity.SignalCallback;
import com.android.systemui.util.CarrierConfigTracker;
import com.android.systemui.utils.leaks.LeakCheckedTest;
import com.android.systemui.utils.os.FakeHandler;
@@ -57,7 +59,7 @@
public class QSCarrierGroupControllerTest extends LeakCheckedTest {
private QSCarrierGroupController mQSCarrierGroupController;
- private NetworkController.SignalCallback mSignalCallback;
+ private SignalCallback mSignalCallback;
private CarrierTextManager.CarrierTextCallback mCallback;
@Mock
private QSCarrierGroup mQSCarrierGroup;
@@ -94,7 +96,7 @@
when(mNetworkController.hasVoiceCallingFeature()).thenReturn(true);
doAnswer(invocation -> mSignalCallback = invocation.getArgument(0))
.when(mNetworkController)
- .addCallback(any(NetworkController.SignalCallback.class));
+ .addCallback(any(SignalCallback.class));
when(mCarrierTextControllerBuilder.setShowAirplaneMode(anyBoolean()))
.thenReturn(mCarrierTextControllerBuilder);
@@ -230,8 +232,8 @@
mSlotIndexResolver.overrideInvalid = true;
MobileDataIndicators indicators = new MobileDataIndicators(
- mock(NetworkController.IconState.class),
- mock(NetworkController.IconState.class),
+ mock(IconState.class),
+ mock(IconState.class),
0, 0, true, true, "", "", "", 0, true, true);
mSignalCallback.setMobileDataIndicators(indicators);
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/CastTileTest.java b/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/CastTileTest.java
index f0bd0657..5a49337 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/CastTileTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/CastTileTest.java
@@ -43,8 +43,10 @@
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.connectivity.IconState;
import com.android.systemui.statusbar.connectivity.NetworkController;
-import com.android.systemui.statusbar.connectivity.NetworkController.WifiIndicators;
+import com.android.systemui.statusbar.connectivity.SignalCallback;
+import com.android.systemui.statusbar.connectivity.WifiIndicators;
import com.android.systemui.statusbar.policy.CastController;
import com.android.systemui.statusbar.policy.CastController.CastDevice;
import com.android.systemui.statusbar.policy.HotspotController;
@@ -77,7 +79,7 @@
@Mock
private QSTileHost mHost;
@Mock
- NetworkController.SignalCallback mSignalCallback;
+ SignalCallback mSignalCallback;
@Mock
private MetricsLogger mMetricsLogger;
@Mock
@@ -122,8 +124,8 @@
mTestableLooper.processAllMessages();
mCastTile.handleSetListening(true);
- ArgumentCaptor<NetworkController.SignalCallback> signalCallbackArgumentCaptor =
- ArgumentCaptor.forClass(NetworkController.SignalCallback.class);
+ ArgumentCaptor<SignalCallback> signalCallbackArgumentCaptor =
+ ArgumentCaptor.forClass(SignalCallback.class);
verify(mNetworkController).observe(any(LifecycleOwner.class),
signalCallbackArgumentCaptor.capture());
mSignalCallback = signalCallbackArgumentCaptor.getValue();
@@ -139,10 +141,9 @@
// All these tests for enabled/disabled wifi have hotspot not enabled
@Test
public void testStateUnavailable_wifiDisabled() {
- NetworkController.IconState qsIcon =
- new NetworkController.IconState(false, 0, "");
+ IconState qsIcon = new IconState(false, 0, "");
WifiIndicators indicators = new WifiIndicators(
- false, mock(NetworkController.IconState.class),
+ false, mock(IconState.class),
qsIcon, false,false, "",
false, "");
mSignalCallback.setWifiIndicators(indicators);
@@ -153,10 +154,9 @@
@Test
public void testStateUnavailable_wifiNotConnected() {
- NetworkController.IconState qsIcon =
- new NetworkController.IconState(false, 0, "");
+ IconState qsIcon = new IconState(false, 0, "");
WifiIndicators indicators = new WifiIndicators(
- true, mock(NetworkController.IconState.class),
+ true, mock(IconState.class),
qsIcon, false,false, "",
false, "");
mSignalCallback.setWifiIndicators(indicators);
@@ -166,10 +166,9 @@
}
private void enableWifiAndProcessMessages() {
- NetworkController.IconState qsIcon =
- new NetworkController.IconState(true, 0, "");
+ IconState qsIcon = new IconState(true, 0, "");
WifiIndicators indicators = new WifiIndicators(
- true, mock(NetworkController.IconState.class),
+ true, mock(IconState.class),
qsIcon, false,false, "",
false, "");
mSignalCallback.setWifiIndicators(indicators);
diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/ScreenRecordTileTest.java b/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/ScreenRecordTileTest.java
index 964ce01..e4c5299 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/ScreenRecordTileTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/ScreenRecordTileTest.java
@@ -17,9 +17,11 @@
package com.android.systemui.qs.tiles;
import static junit.framework.Assert.assertEquals;
+import static junit.framework.Assert.assertNotNull;
import static junit.framework.Assert.assertTrue;
import static org.junit.Assert.assertFalse;
+import static org.mockito.ArgumentMatchers.any;
import static org.mockito.Mockito.times;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
@@ -34,6 +36,7 @@
import com.android.internal.logging.MetricsLogger;
import com.android.systemui.R;
import com.android.systemui.SysuiTestCase;
+import com.android.systemui.animation.DialogLaunchAnimator;
import com.android.systemui.classifier.FalsingManagerFake;
import com.android.systemui.plugins.ActivityStarter;
import com.android.systemui.plugins.statusbar.StatusBarStateController;
@@ -41,10 +44,12 @@
import com.android.systemui.qs.logging.QSLogger;
import com.android.systemui.screenrecord.RecordingController;
import com.android.systemui.statusbar.phone.KeyguardDismissUtil;
+import com.android.systemui.statusbar.policy.KeyguardStateController;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
+import org.mockito.ArgumentCaptor;
import org.mockito.Mock;
import org.mockito.MockitoAnnotations;
@@ -67,6 +72,10 @@
private ActivityStarter mActivityStarter;
@Mock
private QSLogger mQSLogger;
+ @Mock
+ private KeyguardStateController mKeyguardStateController;
+ @Mock
+ private DialogLaunchAnimator mDialogLaunchAnimator;
private TestableLooper mTestableLooper;
private ScreenRecordTile mTile;
@@ -89,7 +98,9 @@
mActivityStarter,
mQSLogger,
mController,
- mKeyguardDismissUtil
+ mKeyguardDismissUtil,
+ mKeyguardStateController,
+ mDialogLaunchAnimator
);
mTile.initialize();
@@ -112,7 +123,15 @@
mTile.handleClick(null /* view */);
mTestableLooper.processAllMessages();
- verify(mController, times(1)).getPromptIntent();
+
+ ArgumentCaptor<Runnable> onStartRecordingClicked = ArgumentCaptor.forClass(Runnable.class);
+ verify(mController).createScreenRecordDialog(any(), onStartRecordingClicked.capture());
+
+ // When starting the recording, we collapse the shade and disable the dialog animation.
+ assertNotNull(onStartRecordingClicked.getValue());
+ onStartRecordingClicked.getValue().run();
+ verify(mDialogLaunchAnimator).disableAllCurrentDialogsExitAnimations();
+ verify(mHost).collapsePanels();
}
// Test that the tile is active and labeled correctly when the controller is starting
diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/dialog/InternetDialogControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/dialog/InternetDialogControllerTest.java
index eb03b5f..ca8903b 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/dialog/InternetDialogControllerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/dialog/InternetDialogControllerTest.java
@@ -49,8 +49,7 @@
import com.android.systemui.broadcast.BroadcastDispatcher;
import com.android.systemui.dagger.qualifiers.Main;
import com.android.systemui.plugins.ActivityStarter;
-import com.android.systemui.statusbar.connectivity.NetworkController;
-import com.android.systemui.statusbar.connectivity.NetworkController.AccessPointController;
+import com.android.systemui.statusbar.connectivity.AccessPointController;
import com.android.systemui.statusbar.policy.KeyguardStateController;
import com.android.systemui.statusbar.policy.LocationController;
import com.android.systemui.toast.SystemUIToast;
@@ -104,7 +103,7 @@
@Mock
private KeyguardStateController mKeyguardStateController;
@Mock
- private NetworkController.AccessPointController mAccessPointController;
+ private AccessPointController mAccessPointController;
@Mock
private WifiEntry mConnectedEntry;
@Mock
diff --git a/packages/SystemUI/tests/src/com/android/systemui/screenrecord/RecordingControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/screenrecord/RecordingControllerTest.java
index b7cc651dc..013e58e 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/screenrecord/RecordingControllerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/screenrecord/RecordingControllerTest.java
@@ -32,6 +32,7 @@
import com.android.systemui.SysuiTestCase;
import com.android.systemui.broadcast.BroadcastDispatcher;
+import com.android.systemui.settings.UserContextProvider;
import org.junit.Before;
import org.junit.Test;
@@ -52,6 +53,8 @@
private RecordingController.RecordingStateChangeCallback mCallback;
@Mock
private BroadcastDispatcher mBroadcastDispatcher;
+ @Mock
+ private UserContextProvider mUserContextProvider;
private RecordingController mController;
@@ -60,7 +63,7 @@
@Before
public void setUp() {
MockitoAnnotations.initMocks(this);
- mController = new RecordingController(mBroadcastDispatcher);
+ mController = new RecordingController(mBroadcastDispatcher, mUserContextProvider);
mController.addCallback(mCallback);
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/NotificationLockscreenUserManagerTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/NotificationLockscreenUserManagerTest.java
index 23cca72..48f8206 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/NotificationLockscreenUserManagerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/NotificationLockscreenUserManagerTest.java
@@ -62,6 +62,8 @@
import com.android.systemui.statusbar.notification.NotificationEntryManager;
import com.android.systemui.statusbar.notification.collection.NotificationEntry;
import com.android.systemui.statusbar.notification.collection.NotificationEntryBuilder;
+import com.android.systemui.statusbar.notification.collection.notifcollection.CommonNotifCollection;
+import com.android.systemui.statusbar.notification.collection.render.NotificationVisibilityProvider;
import com.android.systemui.statusbar.policy.DeviceProvisionedController;
import com.android.systemui.statusbar.policy.KeyguardStateController;
@@ -84,8 +86,12 @@
// Dependency mocks:
@Mock
+ private NotificationVisibilityProvider mVisibilityProvider;
+ @Mock
private NotificationEntryManager mEntryManager;
@Mock
+ private CommonNotifCollection mNotifCollection;
+ @Mock
private DevicePolicyManager mDevicePolicyManager;
@Mock
private NotificationClickNotifier mClickNotifier;
@@ -419,6 +425,8 @@
mBroadcastDispatcher,
mDevicePolicyManager,
mUserManager,
+ (() -> mVisibilityProvider),
+ (() -> mNotifCollection),
mClickNotifier,
NotificationLockscreenUserManagerTest.this.mKeyguardManager,
mStatusBarStateController,
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/NotificationRemoteInputManagerTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/NotificationRemoteInputManagerTest.java
index 4ed7224..8e4b98f 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/NotificationRemoteInputManagerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/NotificationRemoteInputManagerTest.java
@@ -47,6 +47,7 @@
import com.android.systemui.statusbar.notification.NotificationEntryManager;
import com.android.systemui.statusbar.notification.collection.NotificationEntry;
import com.android.systemui.statusbar.notification.collection.NotificationEntryBuilder;
+import com.android.systemui.statusbar.notification.collection.render.NotificationVisibilityProvider;
import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow;
import com.android.systemui.statusbar.phone.StatusBar;
import com.android.systemui.statusbar.policy.RemoteInputUriController;
@@ -70,7 +71,7 @@
private static final String TEST_PACKAGE_NAME = "test";
private static final int TEST_UID = 0;
- @Mock private NotificationPresenter mPresenter;
+ @Mock private NotificationVisibilityProvider mVisibilityProvider;
@Mock private RemoteInputController.Delegate mDelegate;
@Mock private NotificationRemoteInputManager.Callback mCallback;
@Mock private RemoteInputController mController;
@@ -101,6 +102,7 @@
mock(FeatureFlags.class),
mLockscreenUserManager,
mSmartReplyController,
+ mVisibilityProvider,
mEntryManager,
mock(RemoteInputNotificationRebuilder.class),
() -> Optional.of(mock(StatusBar.class)),
@@ -191,6 +193,7 @@
FeatureFlags featureFlags,
NotificationLockscreenUserManager lockscreenUserManager,
SmartReplyController smartReplyController,
+ NotificationVisibilityProvider visibilityProvider,
NotificationEntryManager notificationEntryManager,
RemoteInputNotificationRebuilder rebuilder,
Lazy<Optional<StatusBar>> statusBarOptionalLazy,
@@ -205,6 +208,7 @@
featureFlags,
lockscreenUserManager,
smartReplyController,
+ visibilityProvider,
notificationEntryManager,
rebuilder,
statusBarOptionalLazy,
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/SmartReplyControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/SmartReplyControllerTest.java
index 99c965a..8b28fd5 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/SmartReplyControllerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/SmartReplyControllerTest.java
@@ -44,6 +44,7 @@
import com.android.systemui.statusbar.notification.NotificationEntryManager;
import com.android.systemui.statusbar.notification.collection.NotificationEntry;
import com.android.systemui.statusbar.notification.collection.NotificationEntryBuilder;
+import com.android.systemui.statusbar.notification.collection.render.NotificationVisibilityProvider;
import com.android.systemui.statusbar.phone.StatusBar;
import com.android.systemui.statusbar.policy.RemoteInputUriController;
@@ -71,7 +72,7 @@
private SmartReplyController mSmartReplyController;
private NotificationRemoteInputManager mRemoteInputManager;
- @Mock private NotificationPresenter mPresenter;
+ @Mock private NotificationVisibilityProvider mVisibilityProvider;
@Mock private RemoteInputController.Delegate mDelegate;
@Mock private NotificationRemoteInputManager.Callback mCallback;
@Mock private StatusBarNotification mSbn;
@@ -89,7 +90,7 @@
mSmartReplyController = new SmartReplyController(
mock(DumpManager.class),
- mNotificationEntryManager,
+ mVisibilityProvider,
mIStatusBarService,
mClickNotifier);
mDependency.injectTestDependency(SmartReplyController.class,
@@ -97,7 +98,9 @@
mRemoteInputManager = new NotificationRemoteInputManager(mContext,
mock(FeatureFlags.class),
- mock(NotificationLockscreenUserManager.class), mSmartReplyController,
+ mock(NotificationLockscreenUserManager.class),
+ mSmartReplyController,
+ mVisibilityProvider,
mNotificationEntryManager,
new RemoteInputNotificationRebuilder(mContext),
() -> Optional.of(mock(StatusBar.class)),
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/connectivity/AccessPointControllerImplTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/connectivity/AccessPointControllerImplTest.kt
index 7896a26..c57b64d 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/connectivity/AccessPointControllerImplTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/connectivity/AccessPointControllerImplTest.kt
@@ -56,9 +56,9 @@
@Mock
private lateinit var wifiPickerTracker: WifiPickerTracker
@Mock
- private lateinit var callback: NetworkController.AccessPointController.AccessPointCallback
+ private lateinit var callback: AccessPointController.AccessPointCallback
@Mock
- private lateinit var otherCallback: NetworkController.AccessPointController.AccessPointCallback
+ private lateinit var otherCallback: AccessPointController.AccessPointCallback
@Mock
private lateinit var wifiEntryConnected: WifiEntry
@Mock
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/connectivity/CallbackHandlerTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/connectivity/CallbackHandlerTest.java
index 11a53c5..2d29c80 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/connectivity/CallbackHandlerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/connectivity/CallbackHandlerTest.java
@@ -29,10 +29,6 @@
import com.android.settingslib.mobile.TelephonyIcons;
import com.android.systemui.SysuiTestCase;
import com.android.systemui.statusbar.connectivity.NetworkController.EmergencyListener;
-import com.android.systemui.statusbar.connectivity.NetworkController.IconState;
-import com.android.systemui.statusbar.connectivity.NetworkController.MobileDataIndicators;
-import com.android.systemui.statusbar.connectivity.NetworkController.SignalCallback;
-import com.android.systemui.statusbar.connectivity.NetworkController.WifiIndicators;
import com.android.systemui.tests.R;
import org.junit.Before;
diff --git a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/MobileStateTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/connectivity/MobileStateTest.java
similarity index 90%
rename from packages/SettingsLib/tests/robotests/src/com/android/settingslib/MobileStateTest.java
rename to packages/SystemUI/tests/src/com/android/systemui/statusbar/connectivity/MobileStateTest.java
index 92a32bc..7ddfde3 100644
--- a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/MobileStateTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/connectivity/MobileStateTest.java
@@ -14,22 +14,26 @@
* limitations under the License.
*/
-package com.android.settingslib;
+package com.android.systemui.statusbar.connectivity;
import static junit.framework.Assert.assertFalse;
import static junit.framework.Assert.assertTrue;
+import android.test.suitebuilder.annotation.SmallTest;
+import android.testing.AndroidTestingRunner;
+
import com.android.settingslib.mobile.TelephonyIcons;
+import com.android.systemui.SysuiTestCase;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
-import org.robolectric.RobolectricTestRunner;
-@RunWith(RobolectricTestRunner.class)
-public class MobileStateTest {
+@SmallTest
+@RunWith(AndroidTestingRunner.class)
+public class MobileStateTest extends SysuiTestCase {
- private SignalIcon.MobileState mState = new SignalIcon.MobileState();
+ private final MobileState mState = new MobileState();
@Before
public void setUp() {
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/connectivity/NetworkControllerBaseTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/connectivity/NetworkControllerBaseTest.java
index b23d07a..47a11fc 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/connectivity/NetworkControllerBaseTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/connectivity/NetworkControllerBaseTest.java
@@ -72,9 +72,6 @@
import com.android.systemui.demomode.DemoModeController;
import com.android.systemui.dump.DumpManager;
import com.android.systemui.flags.FeatureFlags;
-import com.android.systemui.statusbar.connectivity.NetworkController.IconState;
-import com.android.systemui.statusbar.connectivity.NetworkController.MobileDataIndicators;
-import com.android.systemui.statusbar.connectivity.NetworkController.SignalCallback;
import com.android.systemui.statusbar.policy.DeviceProvisionedController;
import com.android.systemui.statusbar.policy.DeviceProvisionedController.DeviceProvisionedListener;
import com.android.systemui.telephony.TelephonyListenerManager;
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/connectivity/NetworkControllerEthernetTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/connectivity/NetworkControllerEthernetTest.java
index 675d755..f6f939a 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/connectivity/NetworkControllerEthernetTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/connectivity/NetworkControllerEthernetTest.java
@@ -23,8 +23,6 @@
import android.testing.AndroidTestingRunner;
import android.testing.TestableLooper.RunWithLooper;
-import com.android.systemui.statusbar.connectivity.NetworkController.IconState;
-
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.ArgumentCaptor;
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/connectivity/NetworkControllerWifiTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/connectivity/NetworkControllerWifiTest.java
index ffeaf20..a39971d 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/connectivity/NetworkControllerWifiTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/connectivity/NetworkControllerWifiTest.java
@@ -36,7 +36,6 @@
import android.testing.TestableLooper.RunWithLooper;
import com.android.settingslib.mobile.TelephonyIcons;
-import com.android.systemui.statusbar.connectivity.NetworkController.WifiIndicators;
import org.junit.Before;
import org.junit.Test;
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/NotifPipelineTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/NotifPipelineTest.kt
new file mode 100644
index 0000000..cf7174e
--- /dev/null
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/NotifPipelineTest.kt
@@ -0,0 +1,81 @@
+/*
+ * Copyright (C) 2019 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.statusbar.notification.collection
+
+import android.testing.AndroidTestingRunner
+import androidx.test.filters.SmallTest
+import com.android.systemui.SysuiTestCase
+import com.google.common.truth.Truth.assertThat
+import org.junit.Before
+import org.junit.Test
+import org.junit.runner.RunWith
+import org.mockito.Mock
+import org.mockito.MockitoAnnotations
+import org.mockito.Mockito.`when` as whenever
+
+@SmallTest
+@RunWith(AndroidTestingRunner::class)
+class NotifPipelineTest : SysuiTestCase() {
+
+ @Mock private lateinit var notifCollection: NotifCollection
+ @Mock private lateinit var shadeListBuilder: ShadeListBuilder
+ private lateinit var notifPipeline: NotifPipeline
+
+ @Before
+ fun setup() {
+ MockitoAnnotations.initMocks(this)
+ notifPipeline = NotifPipeline(notifCollection, shadeListBuilder)
+ whenever(shadeListBuilder.shadeList).thenReturn(listOf(
+ NotificationEntryBuilder().setPkg("foo").setId(1).build(),
+ NotificationEntryBuilder().setPkg("foo").setId(2).build(),
+ group(
+ NotificationEntryBuilder().setPkg("bar").setId(1).build(),
+ NotificationEntryBuilder().setPkg("bar").setId(2).build(),
+ NotificationEntryBuilder().setPkg("bar").setId(3).build(),
+ NotificationEntryBuilder().setPkg("bar").setId(4).build()
+ ),
+ NotificationEntryBuilder().setPkg("baz").setId(1).build()
+ ))
+ }
+
+ private fun group(summary: NotificationEntry, vararg children: NotificationEntry): GroupEntry {
+ return GroupEntry(summary.key, summary.creationTime).also { group ->
+ group.summary = summary
+ for (it in children) {
+ group.addChild(it)
+ }
+ }
+ }
+
+ @Test
+ fun testGetShadeListCount() {
+ assertThat(notifPipeline.getShadeListCount()).isEqualTo(7)
+ }
+
+ @Test
+ fun testGetFlatShadeList() {
+ assertThat(notifPipeline.getFlatShadeList().map { it.key }).containsExactly(
+ "0|foo|1|null|0",
+ "0|foo|2|null|0",
+ "0|bar|1|null|0",
+ "0|bar|2|null|0",
+ "0|bar|3|null|0",
+ "0|bar|4|null|0",
+ "0|baz|1|null|0"
+ ).inOrder()
+ }
+}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/coordinator/ConversationCoordinatorTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/coordinator/ConversationCoordinatorTest.kt
index 711f0ba..a46b440 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/coordinator/ConversationCoordinatorTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/coordinator/ConversationCoordinatorTest.kt
@@ -29,12 +29,12 @@
import com.android.systemui.statusbar.notification.collection.render.NodeController
import com.android.systemui.statusbar.notification.people.PeopleNotificationIdentifier
import com.android.systemui.statusbar.notification.people.PeopleNotificationIdentifier.Companion.TYPE_PERSON
+import com.android.systemui.util.mockito.withArgCaptor
import org.junit.Assert.assertFalse
import org.junit.Assert.assertTrue
import org.junit.Before
import org.junit.Test
import org.junit.runner.RunWith
-import org.mockito.ArgumentCaptor
import org.mockito.Mock
import org.mockito.Mockito.verify
import org.mockito.MockitoAnnotations
@@ -65,9 +65,9 @@
coordinator.attach(pipeline)
// capture arguments:
- val notifPromoterCaptor = ArgumentCaptor.forClass(NotifPromoter::class.java)
- verify(pipeline).addPromoter(notifPromoterCaptor.capture())
- promoter = notifPromoterCaptor.value
+ promoter = withArgCaptor {
+ verify(pipeline).addPromoter(capture())
+ }
peopleSectioner = coordinator.sectioner
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/coordinator/GutsCoordinatorTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/coordinator/GutsCoordinatorTest.kt
index 0cba0703..0f6bd77 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/coordinator/GutsCoordinatorTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/coordinator/GutsCoordinatorTest.kt
@@ -28,7 +28,7 @@
import com.android.systemui.statusbar.notification.collection.render.NotifGutsViewListener
import com.android.systemui.statusbar.notification.collection.render.NotifGutsViewManager
import com.android.systemui.statusbar.notification.row.NotificationGuts
-import com.android.systemui.util.mockito.argumentCaptor
+import com.android.systemui.util.mockito.withArgCaptor
import com.google.common.truth.Truth.assertThat
import org.junit.Before
import org.junit.Test
@@ -60,13 +60,11 @@
initMocks(this)
coordinator = GutsCoordinator(notifGutsViewManager, logger, dumpManager)
coordinator.attach(pipeline)
- notifLifetimeExtender = argumentCaptor<NotifLifetimeExtender>().let {
- verify(pipeline).addNotificationLifetimeExtender(it.capture())
- it.value!!
+ notifLifetimeExtender = withArgCaptor {
+ verify(pipeline).addNotificationLifetimeExtender(capture())
}
- notifGutsViewListener = argumentCaptor<NotifGutsViewListener>().let {
- verify(notifGutsViewManager).setGutsListener(it.capture())
- it.value!!
+ notifGutsViewListener = withArgCaptor {
+ verify(notifGutsViewManager).setGutsListener(capture())
}
notifLifetimeExtender.setCallback(lifetimeExtenderCallback)
entry1 = NotificationEntryBuilder().setId(1).build()
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/coordinator/ShadeEventCoordinatorTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/coordinator/ShadeEventCoordinatorTest.kt
index 5915cd7..452af21 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/coordinator/ShadeEventCoordinatorTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/coordinator/ShadeEventCoordinatorTest.kt
@@ -26,7 +26,7 @@
import com.android.systemui.statusbar.notification.collection.NotificationEntryBuilder
import com.android.systemui.statusbar.notification.collection.listbuilder.OnBeforeRenderListListener
import com.android.systemui.statusbar.notification.collection.notifcollection.NotifCollectionListener
-import com.android.systemui.util.mockito.argumentCaptor
+import com.android.systemui.util.mockito.withArgCaptor
import org.junit.Before
import org.junit.Test
import org.junit.runner.RunWith
@@ -56,13 +56,11 @@
initMocks(this)
coordinator = ShadeEventCoordinator(logger)
coordinator.attach(pipeline)
- notifCollectionListener = argumentCaptor<NotifCollectionListener>().let {
- verify(pipeline).addCollectionListener(it.capture())
- it.value!!
+ notifCollectionListener = withArgCaptor {
+ verify(pipeline).addCollectionListener(capture())
}
- onBeforeRenderListListener = argumentCaptor<OnBeforeRenderListListener>().let {
- verify(pipeline).addOnBeforeRenderListListener(it.capture())
- it.value!!
+ onBeforeRenderListListener = withArgCaptor {
+ verify(pipeline).addOnBeforeRenderListListener(capture())
}
coordinator.setNotifRemovedByUserCallback(notifRemovedByUserCallback)
coordinator.setShadeEmptiedCallback(shadeEmptiedCallback)
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/coordinator/SmartspaceDedupingCoordinatorTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/coordinator/SmartspaceDedupingCoordinatorTest.kt
index a8db8d7..fdff6e9 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/coordinator/SmartspaceDedupingCoordinatorTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/coordinator/SmartspaceDedupingCoordinatorTest.kt
@@ -35,15 +35,13 @@
import com.android.systemui.statusbar.notification.collection.listbuilder.pluggable.Pluggable
import com.android.systemui.statusbar.notification.collection.notifcollection.NotifCollectionListener
import com.android.systemui.util.concurrency.FakeExecutor
-import com.android.systemui.util.mockito.capture
+import com.android.systemui.util.mockito.withArgCaptor
import com.android.systemui.util.time.FakeSystemClock
import org.junit.Assert.assertEquals
import org.junit.Assert.assertFalse
import org.junit.Assert.assertTrue
import org.junit.Before
import org.junit.Test
-import org.mockito.ArgumentCaptor
-import org.mockito.Captor
import org.mockito.Mock
import org.mockito.Mockito.`when`
import org.mockito.Mockito.anyString
@@ -69,15 +67,6 @@
@Mock
private lateinit var pluggableListener: Pluggable.PluggableListener<NotifFilter>
- @Captor
- private lateinit var filterCaptor: ArgumentCaptor<NotifFilter>
- @Captor
- private lateinit var collectionListenerCaptor: ArgumentCaptor<NotifCollectionListener>
- @Captor
- private lateinit var stateListenerCaptor: ArgumentCaptor<StatusBarStateController.StateListener>
- @Captor
- private lateinit var smartspaceListenerCaptor: ArgumentCaptor<SmartspaceTargetListener>
-
private lateinit var filter: NotifFilter
private lateinit var collectionListener: NotifCollectionListener
private lateinit var statusBarListener: StatusBarStateController.StateListener
@@ -118,18 +107,22 @@
// Attach the deduper and capture the listeners/filters that it registers
deduper.attach(notifPipeline)
- verify(notifPipeline).addPreGroupFilter(filterCaptor.capture())
- filter = filterCaptor.value
+ filter = withArgCaptor {
+ verify(notifPipeline).addPreGroupFilter(capture())
+ }
filter.setInvalidationListener(pluggableListener)
- verify(notifPipeline).addCollectionListener(capture(collectionListenerCaptor))
- collectionListener = collectionListenerCaptor.value
+ collectionListener = withArgCaptor {
+ verify(notifPipeline).addCollectionListener(capture())
+ }
- verify(statusBarStateController).addCallback(capture(stateListenerCaptor))
- statusBarListener = stateListenerCaptor.value
+ statusBarListener = withArgCaptor {
+ verify(statusBarStateController).addCallback(capture())
+ }
- verify(smartspaceController).addListener(capture(smartspaceListenerCaptor))
- newTargetListener = smartspaceListenerCaptor.value
+ newTargetListener = withArgCaptor {
+ verify(smartspaceController).addListener(capture())
+ }
// Initialize some test data
entry1HasRecentlyAlerted = NotificationEntryBuilder()
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/logging/NotificationLoggerLegacyTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/logging/NotificationLoggerLegacyTest.java
new file mode 100644
index 0000000..4cf530e
--- /dev/null
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/logging/NotificationLoggerLegacyTest.java
@@ -0,0 +1,283 @@
+/*
+ * Copyright (C) 2017 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.statusbar.notification.logging;
+
+import static org.junit.Assert.assertArrayEquals;
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertTrue;
+import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.Mockito.doAnswer;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.never;
+import static org.mockito.Mockito.times;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.when;
+
+import android.app.Notification;
+import android.os.Handler;
+import android.os.Looper;
+import android.os.UserHandle;
+import android.testing.AndroidTestingRunner;
+import android.testing.TestableLooper;
+
+import androidx.test.filters.SmallTest;
+
+import com.android.internal.logging.InstanceId;
+import com.android.internal.statusbar.IStatusBarService;
+import com.android.internal.statusbar.NotificationVisibility;
+import com.android.systemui.SysuiTestCase;
+import com.android.systemui.flags.FeatureFlags;
+import com.android.systemui.statusbar.NotificationListener;
+import com.android.systemui.statusbar.StatusBarState;
+import com.android.systemui.statusbar.StatusBarStateControllerImpl;
+import com.android.systemui.statusbar.notification.NotificationEntryManager;
+import com.android.systemui.statusbar.notification.collection.NotifPipeline;
+import com.android.systemui.statusbar.notification.collection.NotificationEntry;
+import com.android.systemui.statusbar.notification.collection.NotificationEntryBuilder;
+import com.android.systemui.statusbar.notification.collection.render.NotificationVisibilityProvider;
+import com.android.systemui.statusbar.notification.logging.nano.Notifications;
+import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow;
+import com.android.systemui.statusbar.notification.stack.NotificationListContainer;
+import com.android.systemui.util.concurrency.FakeExecutor;
+import com.android.systemui.util.time.FakeSystemClock;
+
+import com.google.android.collect.Lists;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.Mock;
+import org.mockito.Mockito;
+import org.mockito.MockitoAnnotations;
+
+import java.util.concurrent.ConcurrentLinkedQueue;
+import java.util.concurrent.Executor;
+
+@SmallTest
+@RunWith(AndroidTestingRunner.class)
+@TestableLooper.RunWithLooper
+public class NotificationLoggerLegacyTest extends SysuiTestCase {
+ private static final String TEST_PACKAGE_NAME = "test";
+ private static final int TEST_UID = 0;
+
+ @Mock private NotificationListContainer mListContainer;
+ @Mock private IStatusBarService mBarService;
+ @Mock private ExpandableNotificationRow mRow;
+ @Mock private NotificationLogger.ExpansionStateLogger mExpansionStateLogger;
+
+ // Dependency mocks:
+ @Mock private FeatureFlags mFeatureFlags;
+ @Mock private NotificationVisibilityProvider mVisibilityProvider;
+ @Mock private NotificationEntryManager mEntryManager;
+ @Mock private NotifPipeline mNotifPipeline;
+ @Mock private NotificationListener mListener;
+
+ private NotificationEntry mEntry;
+ private TestableNotificationLogger mLogger;
+ private ConcurrentLinkedQueue<AssertionError> mErrorQueue = new ConcurrentLinkedQueue<>();
+ private FakeExecutor mUiBgExecutor = new FakeExecutor(new FakeSystemClock());
+ private NotificationPanelLoggerFake mNotificationPanelLoggerFake =
+ new NotificationPanelLoggerFake();
+
+ @Before
+ public void setUp() {
+ MockitoAnnotations.initMocks(this);
+
+ mEntry = new NotificationEntryBuilder()
+ .setPkg(TEST_PACKAGE_NAME)
+ .setOpPkg(TEST_PACKAGE_NAME)
+ .setUid(TEST_UID)
+ .setNotification(new Notification())
+ .setUser(UserHandle.CURRENT)
+ .setInstanceId(InstanceId.fakeInstanceId(1))
+ .build();
+ mEntry.setRow(mRow);
+
+ mLogger = new TestableNotificationLogger(
+ mListener,
+ mUiBgExecutor,
+ mFeatureFlags,
+ mVisibilityProvider,
+ mEntryManager,
+ mNotifPipeline,
+ mock(StatusBarStateControllerImpl.class),
+ mBarService,
+ mExpansionStateLogger
+ );
+ mLogger.setUpWithContainer(mListContainer);
+ verify(mEntryManager).addNotificationEntryListener(any());
+ verify(mNotifPipeline, never()).addCollectionListener(any());
+ }
+
+ @Test
+ public void testOnChildLocationsChangedReportsVisibilityChanged() throws Exception {
+ NotificationVisibility[] newlyVisibleKeys = {
+ NotificationVisibility.obtain(mEntry.getKey(), 0, 1, true)
+ };
+ NotificationVisibility[] noLongerVisibleKeys = {};
+ doAnswer(invocation -> {
+ try {
+ assertArrayEquals(newlyVisibleKeys,
+ (NotificationVisibility[]) invocation.getArguments()[0]);
+ assertArrayEquals(noLongerVisibleKeys,
+ (NotificationVisibility[]) invocation.getArguments()[1]);
+ } catch (AssertionError error) {
+ mErrorQueue.offer(error);
+ }
+ return null;
+ }
+ ).when(mBarService).onNotificationVisibilityChanged(any(NotificationVisibility[].class),
+ any(NotificationVisibility[].class));
+
+ when(mListContainer.isInVisibleLocation(any())).thenReturn(true);
+ when(mEntryManager.getVisibleNotifications()).thenReturn(Lists.newArrayList(mEntry));
+ mLogger.getChildLocationsChangedListenerForTest().onChildLocationsChanged();
+ TestableLooper.get(this).processAllMessages();
+ mUiBgExecutor.runAllReady();
+
+ if (!mErrorQueue.isEmpty()) {
+ throw mErrorQueue.poll();
+ }
+
+ // |mEntry| won't change visibility, so it shouldn't be reported again:
+ Mockito.reset(mBarService);
+ mLogger.getChildLocationsChangedListenerForTest().onChildLocationsChanged();
+ TestableLooper.get(this).processAllMessages();
+ mUiBgExecutor.runAllReady();
+
+ verify(mBarService, never()).onNotificationVisibilityChanged(any(), any());
+ }
+
+ @Test
+ public void testStoppingNotificationLoggingReportsCurrentNotifications()
+ throws Exception {
+ when(mListContainer.isInVisibleLocation(any())).thenReturn(true);
+ when(mEntryManager.getVisibleNotifications()).thenReturn(Lists.newArrayList(mEntry));
+ mLogger.getChildLocationsChangedListenerForTest().onChildLocationsChanged();
+ TestableLooper.get(this).processAllMessages();
+ mUiBgExecutor.runAllReady();
+ Mockito.reset(mBarService);
+
+ setStateAsleep();
+ mLogger.onDozingChanged(false); // Wake to lockscreen
+ mLogger.onDozingChanged(true); // And go back to sleep, turning off logging
+ mUiBgExecutor.runAllReady();
+ // The visibility objects are recycled by NotificationLogger, so we can't use specific
+ // matchers here.
+ verify(mBarService, times(1)).onNotificationVisibilityChanged(any(), any());
+ }
+
+ private void setStateAsleep() {
+ mLogger.onPanelExpandedChanged(true);
+ mLogger.onDozingChanged(true);
+ mLogger.onStateChanged(StatusBarState.KEYGUARD);
+ }
+
+ private void setStateAwake() {
+ mLogger.onPanelExpandedChanged(false);
+ mLogger.onDozingChanged(false);
+ mLogger.onStateChanged(StatusBarState.SHADE);
+ }
+
+ @Test
+ public void testLogPanelShownOnWake() {
+ when(mEntryManager.getVisibleNotifications()).thenReturn(Lists.newArrayList(mEntry));
+ setStateAsleep();
+ mLogger.onDozingChanged(false); // Wake to lockscreen
+ assertEquals(1, mNotificationPanelLoggerFake.getCalls().size());
+ assertTrue(mNotificationPanelLoggerFake.get(0).isLockscreen);
+ assertEquals(1, mNotificationPanelLoggerFake.get(0).list.notifications.length);
+ Notifications.Notification n = mNotificationPanelLoggerFake.get(0).list.notifications[0];
+ assertEquals(TEST_PACKAGE_NAME, n.packageName);
+ assertEquals(TEST_UID, n.uid);
+ assertEquals(1, n.instanceId);
+ assertFalse(n.isGroupSummary);
+ assertEquals(Notifications.Notification.SECTION_ALERTING, n.section);
+ }
+
+ @Test
+ public void testLogPanelShownOnShadePull() {
+ when(mEntryManager.getVisibleNotifications()).thenReturn(Lists.newArrayList(mEntry));
+ setStateAwake();
+ // Now expand panel
+ mLogger.onPanelExpandedChanged(true);
+ assertEquals(1, mNotificationPanelLoggerFake.getCalls().size());
+ assertFalse(mNotificationPanelLoggerFake.get(0).isLockscreen);
+ assertEquals(1, mNotificationPanelLoggerFake.get(0).list.notifications.length);
+ Notifications.Notification n = mNotificationPanelLoggerFake.get(0).list.notifications[0];
+ assertEquals(TEST_PACKAGE_NAME, n.packageName);
+ assertEquals(TEST_UID, n.uid);
+ assertEquals(1, n.instanceId);
+ assertFalse(n.isGroupSummary);
+ assertEquals(Notifications.Notification.SECTION_ALERTING, n.section);
+ }
+
+
+ @Test
+ public void testLogPanelShownHandlesNullInstanceIds() {
+ // Construct a NotificationEntry like mEntry, but with a null instance id.
+ NotificationEntry entry = new NotificationEntryBuilder()
+ .setPkg(TEST_PACKAGE_NAME)
+ .setOpPkg(TEST_PACKAGE_NAME)
+ .setUid(TEST_UID)
+ .setNotification(new Notification())
+ .setUser(UserHandle.CURRENT)
+ .build();
+ entry.setRow(mRow);
+
+ when(mEntryManager.getVisibleNotifications()).thenReturn(Lists.newArrayList(entry));
+ setStateAsleep();
+ mLogger.onDozingChanged(false); // Wake to lockscreen
+ assertEquals(1, mNotificationPanelLoggerFake.getCalls().size());
+ assertEquals(1, mNotificationPanelLoggerFake.get(0).list.notifications.length);
+ Notifications.Notification n = mNotificationPanelLoggerFake.get(0).list.notifications[0];
+ assertEquals(0, n.instanceId);
+ }
+
+ private class TestableNotificationLogger extends NotificationLogger {
+
+ TestableNotificationLogger(NotificationListener notificationListener,
+ Executor uiBgExecutor,
+ FeatureFlags featureFlags,
+ NotificationVisibilityProvider visibilityProvider,
+ NotificationEntryManager entryManager,
+ NotifPipeline notifPipeline,
+ StatusBarStateControllerImpl statusBarStateController,
+ IStatusBarService barService,
+ ExpansionStateLogger expansionStateLogger) {
+ super(
+ notificationListener,
+ uiBgExecutor,
+ featureFlags,
+ visibilityProvider,
+ entryManager,
+ notifPipeline,
+ statusBarStateController,
+ expansionStateLogger,
+ mNotificationPanelLoggerFake
+ );
+ mBarService = barService;
+ // Make this on the current thread so we can wait for it during tests.
+ mHandler = Handler.createAsync(Looper.myLooper());
+ }
+
+ OnChildLocationsChangedListener getChildLocationsChangedListenerForTest() {
+ return mNotificationLocationsChangedListener;
+ }
+ }
+}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/logging/NotificationLoggerTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/logging/NotificationLoggerTest.java
index c979dc6..ba198ef 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/logging/NotificationLoggerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/logging/NotificationLoggerTest.java
@@ -41,13 +41,15 @@
import com.android.internal.statusbar.IStatusBarService;
import com.android.internal.statusbar.NotificationVisibility;
import com.android.systemui.SysuiTestCase;
+import com.android.systemui.flags.FeatureFlags;
import com.android.systemui.statusbar.NotificationListener;
import com.android.systemui.statusbar.StatusBarState;
import com.android.systemui.statusbar.StatusBarStateControllerImpl;
-import com.android.systemui.statusbar.notification.NotificationEntryListener;
import com.android.systemui.statusbar.notification.NotificationEntryManager;
+import com.android.systemui.statusbar.notification.collection.NotifPipeline;
import com.android.systemui.statusbar.notification.collection.NotificationEntry;
import com.android.systemui.statusbar.notification.collection.NotificationEntryBuilder;
+import com.android.systemui.statusbar.notification.collection.render.NotificationVisibilityProvider;
import com.android.systemui.statusbar.notification.logging.nano.Notifications;
import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow;
import com.android.systemui.statusbar.notification.stack.NotificationListContainer;
@@ -59,8 +61,6 @@
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
-import org.mockito.ArgumentCaptor;
-import org.mockito.Captor;
import org.mockito.Mock;
import org.mockito.Mockito;
import org.mockito.MockitoAnnotations;
@@ -81,9 +81,11 @@
@Mock private NotificationLogger.ExpansionStateLogger mExpansionStateLogger;
// Dependency mocks:
+ @Mock private FeatureFlags mFeatureFlags;
+ @Mock private NotificationVisibilityProvider mVisibilityProvider;
@Mock private NotificationEntryManager mEntryManager;
+ @Mock private NotifPipeline mNotifPipeline;
@Mock private NotificationListener mListener;
- @Captor private ArgumentCaptor<NotificationEntryListener> mEntryListenerCaptor;
private NotificationEntry mEntry;
private TestableNotificationLogger mLogger;
@@ -95,8 +97,7 @@
@Before
public void setUp() {
MockitoAnnotations.initMocks(this);
- mDependency.injectTestDependency(NotificationEntryManager.class, mEntryManager);
- mDependency.injectTestDependency(NotificationListener.class, mListener);
+ when(mFeatureFlags.isNewNotifPipelineRenderingEnabled()).thenReturn(true);
mEntry = new NotificationEntryBuilder()
.setPkg(TEST_PACKAGE_NAME)
@@ -108,11 +109,20 @@
.build();
mEntry.setRow(mRow);
- mLogger = new TestableNotificationLogger(mListener, mUiBgExecutor,
- mEntryManager, mock(StatusBarStateControllerImpl.class), mBarService,
- mExpansionStateLogger);
+ mLogger = new TestableNotificationLogger(
+ mListener,
+ mUiBgExecutor,
+ mFeatureFlags,
+ mVisibilityProvider,
+ mEntryManager,
+ mNotifPipeline,
+ mock(StatusBarStateControllerImpl.class),
+ mBarService,
+ mExpansionStateLogger
+ );
mLogger.setUpWithContainer(mListContainer);
- verify(mEntryManager).addNotificationEntryListener(mEntryListenerCaptor.capture());
+ verify(mEntryManager, never()).addNotificationEntryListener(any());
+ verify(mNotifPipeline).addCollectionListener(any());
}
@Test
@@ -136,12 +146,12 @@
any(NotificationVisibility[].class));
when(mListContainer.isInVisibleLocation(any())).thenReturn(true);
- when(mEntryManager.getVisibleNotifications()).thenReturn(Lists.newArrayList(mEntry));
+ when(mNotifPipeline.getFlatShadeList()).thenReturn(Lists.newArrayList(mEntry));
mLogger.getChildLocationsChangedListenerForTest().onChildLocationsChanged();
TestableLooper.get(this).processAllMessages();
mUiBgExecutor.runAllReady();
- if(!mErrorQueue.isEmpty()) {
+ if (!mErrorQueue.isEmpty()) {
throw mErrorQueue.poll();
}
@@ -158,7 +168,7 @@
public void testStoppingNotificationLoggingReportsCurrentNotifications()
throws Exception {
when(mListContainer.isInVisibleLocation(any())).thenReturn(true);
- when(mEntryManager.getVisibleNotifications()).thenReturn(Lists.newArrayList(mEntry));
+ when(mNotifPipeline.getFlatShadeList()).thenReturn(Lists.newArrayList(mEntry));
mLogger.getChildLocationsChangedListenerForTest().onChildLocationsChanged();
TestableLooper.get(this).processAllMessages();
mUiBgExecutor.runAllReady();
@@ -187,7 +197,7 @@
@Test
public void testLogPanelShownOnWake() {
- when(mEntryManager.getVisibleNotifications()).thenReturn(Lists.newArrayList(mEntry));
+ when(mNotifPipeline.getFlatShadeList()).thenReturn(Lists.newArrayList(mEntry));
setStateAsleep();
mLogger.onDozingChanged(false); // Wake to lockscreen
assertEquals(1, mNotificationPanelLoggerFake.getCalls().size());
@@ -203,7 +213,7 @@
@Test
public void testLogPanelShownOnShadePull() {
- when(mEntryManager.getVisibleNotifications()).thenReturn(Lists.newArrayList(mEntry));
+ when(mNotifPipeline.getFlatShadeList()).thenReturn(Lists.newArrayList(mEntry));
setStateAwake();
// Now expand panel
mLogger.onPanelExpandedChanged(true);
@@ -231,7 +241,7 @@
.build();
entry.setRow(mRow);
- when(mEntryManager.getVisibleNotifications()).thenReturn(Lists.newArrayList(entry));
+ when(mNotifPipeline.getFlatShadeList()).thenReturn(Lists.newArrayList(entry));
setStateAsleep();
mLogger.onDozingChanged(false); // Wake to lockscreen
assertEquals(1, mNotificationPanelLoggerFake.getCalls().size());
@@ -244,19 +254,30 @@
TestableNotificationLogger(NotificationListener notificationListener,
Executor uiBgExecutor,
+ FeatureFlags featureFlags,
+ NotificationVisibilityProvider visibilityProvider,
NotificationEntryManager entryManager,
+ NotifPipeline notifPipeline,
StatusBarStateControllerImpl statusBarStateController,
IStatusBarService barService,
ExpansionStateLogger expansionStateLogger) {
- super(notificationListener, uiBgExecutor, entryManager, statusBarStateController,
- expansionStateLogger, mNotificationPanelLoggerFake);
+ super(
+ notificationListener,
+ uiBgExecutor,
+ featureFlags,
+ visibilityProvider,
+ entryManager,
+ notifPipeline,
+ statusBarStateController,
+ expansionStateLogger,
+ mNotificationPanelLoggerFake
+ );
mBarService = barService;
// Make this on the current thread so we can wait for it during tests.
mHandler = Handler.createAsync(Looper.myLooper());
}
- OnChildLocationsChangedListener
- getChildLocationsChangedListenerForTest() {
+ OnChildLocationsChangedListener getChildLocationsChangedListenerForTest() {
return mNotificationLocationsChangedListener;
}
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayoutControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayoutControllerTest.java
index baed694..f26bb75 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayoutControllerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayoutControllerTest.java
@@ -64,6 +64,7 @@
import com.android.systemui.statusbar.notification.collection.NotifPipeline;
import com.android.systemui.statusbar.notification.collection.legacy.NotificationGroupManagerLegacy;
import com.android.systemui.statusbar.notification.collection.legacy.VisualStabilityManager;
+import com.android.systemui.statusbar.notification.collection.render.NotificationVisibilityProvider;
import com.android.systemui.statusbar.notification.collection.render.SectionHeaderController;
import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow;
import com.android.systemui.statusbar.notification.row.ForegroundServiceDungeonView;
@@ -97,6 +98,7 @@
public class NotificationStackScrollLayoutControllerTest extends SysuiTestCase {
@Mock private NotificationGutsManager mNotificationGutsManager;
+ @Mock private NotificationVisibilityProvider mVisibilityProvider;
@Mock private HeadsUpManagerPhone mHeadsUpManager;
@Mock private NotificationRoundnessManager mNotificationRoundnessManager;
@Mock private TunerService mTunerService;
@@ -151,6 +153,7 @@
mController = new NotificationStackScrollLayoutController(
true,
mNotificationGutsManager,
+ mVisibilityProvider,
mHeadsUpManager,
mNotificationRoundnessManager,
mTunerService,
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/CollapsedStatusBarFragmentTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/CollapsedStatusBarFragmentTest.java
index e4c4c63..75a8624 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/CollapsedStatusBarFragmentTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/CollapsedStatusBarFragmentTest.java
@@ -38,6 +38,7 @@
import com.android.systemui.R;
import com.android.systemui.SysuiBaseFragmentTest;
+import com.android.systemui.dump.DumpManager;
import com.android.systemui.flags.FeatureFlags;
import com.android.systemui.log.LogBuffer;
import com.android.systemui.log.LogcatEchoTracker;
@@ -50,6 +51,8 @@
import com.android.systemui.statusbar.phone.ongoingcall.OngoingCallController;
import com.android.systemui.statusbar.phone.panelstate.PanelExpansionStateManager;
import com.android.systemui.statusbar.policy.KeyguardStateController;
+import com.android.systemui.util.concurrency.FakeExecutor;
+import com.android.systemui.util.time.FakeSystemClock;
import org.junit.Before;
import org.junit.Ignore;
@@ -265,6 +268,8 @@
new PanelExpansionStateManager(),
mock(FeatureFlags.class),
mStatusBarIconController,
+ new StatusBarHideIconsForBouncerManager(
+ mCommandQueue, new FakeExecutor(new FakeSystemClock()), new DumpManager()),
mKeyguardStateController,
mNetworkController,
mStatusBarStateController,
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/NotificationPanelViewControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/NotificationPanelViewControllerTest.java
index fbab075..f2be05b 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/NotificationPanelViewControllerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/NotificationPanelViewControllerTest.java
@@ -586,46 +586,50 @@
}
@Test
- public void onTouchForwardedFromStatusBar_panelsNotEnabled_returnsFalseAndNoViewEvent() {
+ public void handleTouchEventFromStatusBar_panelsNotEnabled_returnsFalseAndNoViewEvent() {
when(mCommandQueue.panelsEnabled()).thenReturn(false);
- boolean returnVal = mTouchHandler.onTouchForwardedFromStatusBar(
- MotionEvent.obtain(0L, 0L, MotionEvent.ACTION_DOWN, 0f, 0f, 0));
+ boolean returnVal = mNotificationPanelViewController
+ .getStatusBarTouchEventHandler()
+ .handleTouchEvent(
+ MotionEvent.obtain(0L, 0L, MotionEvent.ACTION_DOWN, 0f, 0f, 0));
assertThat(returnVal).isFalse();
verify(mView, never()).dispatchTouchEvent(any());
}
@Test
- public void onTouchForwardedFromStatusBar_viewNotEnabled_returnsTrueAndNoViewEvent() {
+ public void handleTouchEventFromStatusBar_viewNotEnabled_returnsTrueAndNoViewEvent() {
when(mCommandQueue.panelsEnabled()).thenReturn(true);
when(mView.isEnabled()).thenReturn(false);
- boolean returnVal = mTouchHandler.onTouchForwardedFromStatusBar(
- MotionEvent.obtain(0L, 0L, MotionEvent.ACTION_DOWN, 0f, 0f, 0));
+ boolean returnVal = mNotificationPanelViewController
+ .getStatusBarTouchEventHandler()
+ .handleTouchEvent(
+ MotionEvent.obtain(0L, 0L, MotionEvent.ACTION_DOWN, 0f, 0f, 0));
assertThat(returnVal).isTrue();
verify(mView, never()).dispatchTouchEvent(any());
}
@Test
- public void onTouchForwardedFromStatusBar_viewNotEnabledButIsMoveEvent_viewReceivesEvent() {
+ public void handleTouchEventFromStatusBar_viewNotEnabledButIsMoveEvent_viewReceivesEvent() {
when(mCommandQueue.panelsEnabled()).thenReturn(true);
when(mView.isEnabled()).thenReturn(false);
MotionEvent event = MotionEvent.obtain(0L, 0L, MotionEvent.ACTION_MOVE, 0f, 0f, 0);
- mTouchHandler.onTouchForwardedFromStatusBar(event);
+ mNotificationPanelViewController.getStatusBarTouchEventHandler().handleTouchEvent(event);
verify(mView).dispatchTouchEvent(event);
}
@Test
- public void onTouchForwardedFromStatusBar_panelAndViewEnabled_viewReceivesEvent() {
+ public void handleTouchEventFromStatusBar_panelAndViewEnabled_viewReceivesEvent() {
when(mCommandQueue.panelsEnabled()).thenReturn(true);
when(mView.isEnabled()).thenReturn(true);
MotionEvent event = MotionEvent.obtain(0L, 0L, MotionEvent.ACTION_DOWN, 0f, 0f, 0);
- mTouchHandler.onTouchForwardedFromStatusBar(event);
+ mNotificationPanelViewController.getStatusBarTouchEventHandler().handleTouchEvent(event);
verify(mView).dispatchTouchEvent(event);
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/PhoneStatusBarViewControllerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/PhoneStatusBarViewControllerTest.kt
index eea8eb9..dc32007 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/PhoneStatusBarViewControllerTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/PhoneStatusBarViewControllerTest.kt
@@ -52,8 +52,6 @@
@Mock
private lateinit var panelView: ViewGroup
@Mock
- private lateinit var scrimController: ScrimController
- @Mock
private lateinit var moveFromCenterAnimation: StatusBarMoveFromCenterAnimationController
@Mock
private lateinit var sysuiUnfoldComponent: SysUIUnfoldComponent
@@ -76,8 +74,6 @@
val parent = FrameLayout(mContext) // add parent to keep layout params
view = LayoutInflater.from(mContext)
.inflate(R.layout.status_bar, parent, false) as PhoneStatusBarView
- view.setScrimController(scrimController)
- view.setBar(mock(StatusBar::class.java))
}
controller = createController(view)
@@ -85,10 +81,13 @@
@Test
fun constructor_setsTouchHandlerOnView() {
+ val interceptEvent = MotionEvent.obtain(0L, 0L, MotionEvent.ACTION_MOVE, 10f, 10f, 0)
val event = MotionEvent.obtain(0L, 0L, MotionEvent.ACTION_DOWN, 0f, 0f, 0)
+ view.onInterceptTouchEvent(interceptEvent)
view.onTouchEvent(event)
+ assertThat(touchEventHandler.lastInterceptEvent).isEqualTo(interceptEvent)
assertThat(touchEventHandler.lastEvent).isEqualTo(event)
}
@@ -128,6 +127,11 @@
private class TestTouchEventHandler : PhoneStatusBarView.TouchEventHandler {
var lastEvent: MotionEvent? = null
+ var lastInterceptEvent: MotionEvent? = null
+
+ override fun onInterceptTouchEvent(event: MotionEvent?) {
+ lastInterceptEvent = event
+ }
override fun handleTouchEvent(event: MotionEvent?): Boolean {
lastEvent = event
return false
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/PhoneStatusBarViewTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/PhoneStatusBarViewTest.kt
index e8ad5fd3..8d686ae 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/PhoneStatusBarViewTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/PhoneStatusBarViewTest.kt
@@ -34,10 +34,6 @@
private lateinit var panelViewController: PanelViewController
@Mock
private lateinit var panelView: ViewGroup
- @Mock
- private lateinit var scrimController: ScrimController
- @Mock
- private lateinit var statusBar: StatusBar
private lateinit var view: PhoneStatusBarView
@@ -49,8 +45,6 @@
`when`(panelViewController.view).thenReturn(panelView)
view = PhoneStatusBarView(mContext, null)
- view.setScrimController(scrimController)
- view.setBar(statusBar)
}
@Test
@@ -65,12 +59,23 @@
}
@Test
+ fun onInterceptTouchEvent_listenerNotified() {
+ val handler = TestTouchEventHandler()
+ view.setTouchEventHandler(handler)
+
+ val event = MotionEvent.obtain(0L, 0L, MotionEvent.ACTION_DOWN, 0f, 0f, 0)
+ view.onInterceptTouchEvent(event)
+
+ assertThat(handler.lastInterceptEvent).isEqualTo(event)
+ }
+
+ @Test
fun onTouchEvent_listenerReturnsTrue_viewReturnsTrue() {
val handler = TestTouchEventHandler()
view.setTouchEventHandler(handler)
val event = MotionEvent.obtain(0L, 0L, MotionEvent.ACTION_DOWN, 0f, 0f, 0)
- handler.returnValue = true
+ handler.handleTouchReturnValue = true
assertThat(view.onTouchEvent(event)).isTrue()
}
@@ -81,7 +86,7 @@
view.setTouchEventHandler(handler)
val event = MotionEvent.obtain(0L, 0L, MotionEvent.ACTION_DOWN, 0f, 0f, 0)
- handler.returnValue = false
+ handler.handleTouchReturnValue = false
assertThat(view.onTouchEvent(event)).isFalse()
}
@@ -93,11 +98,17 @@
}
private class TestTouchEventHandler : PhoneStatusBarView.TouchEventHandler {
+ var lastInterceptEvent: MotionEvent? = null
var lastEvent: MotionEvent? = null
- var returnValue: Boolean = false
+ var handleTouchReturnValue: Boolean = false
+
+ override fun onInterceptTouchEvent(event: MotionEvent?) {
+ lastInterceptEvent = event
+ }
+
override fun handleTouchEvent(event: MotionEvent?): Boolean {
lastEvent = event
- return returnValue
+ return handleTouchReturnValue
}
}
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarCommandQueueCallbacksTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarCommandQueueCallbacksTest.java
index 8555306..0131293 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarCommandQueueCallbacksTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarCommandQueueCallbacksTest.java
@@ -36,6 +36,7 @@
import com.android.keyguard.KeyguardUpdateMonitor;
import com.android.systemui.SysuiTestCase;
import com.android.systemui.assist.AssistManager;
+import com.android.systemui.dump.DumpManager;
import com.android.systemui.keyguard.WakefulnessLifecycle;
import com.android.systemui.statusbar.CommandQueue;
import com.android.systemui.statusbar.DisableFlagsLogger;
@@ -45,6 +46,8 @@
import com.android.systemui.statusbar.policy.DeviceProvisionedController;
import com.android.systemui.statusbar.policy.KeyguardStateController;
import com.android.systemui.statusbar.policy.RemoteInputQuickSettingsDisabler;
+import com.android.systemui.util.concurrency.FakeExecutor;
+import com.android.systemui.util.time.FakeSystemClock;
import com.android.wm.shell.legacysplitscreen.LegacySplitScreen;
import org.junit.Before;
@@ -109,6 +112,8 @@
mStatusBarStateController,
mNotificationShadeWindowView,
mNotificationStackScrollLayoutController,
+ new StatusBarHideIconsForBouncerManager(
+ mCommandQueue, new FakeExecutor(new FakeSystemClock()), new DumpManager()),
mPowerManager,
mVibratorHelper,
Optional.of(mVibrator),
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarNotificationActivityStarterTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarNotificationActivityStarterTest.java
index 72a3d66..7791fd0 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarNotificationActivityStarterTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarNotificationActivityStarterTest.java
@@ -22,6 +22,7 @@
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.anyBoolean;
import static org.mockito.ArgumentMatchers.anyInt;
+import static org.mockito.ArgumentMatchers.anyString;
import static org.mockito.ArgumentMatchers.eq;
import static org.mockito.Mockito.atLeastOnce;
import static org.mockito.Mockito.doAnswer;
@@ -68,6 +69,7 @@
import com.android.systemui.statusbar.notification.collection.NotifPipeline;
import com.android.systemui.statusbar.notification.collection.NotificationEntry;
import com.android.systemui.statusbar.notification.collection.legacy.NotificationGroupManagerLegacy;
+import com.android.systemui.statusbar.notification.collection.render.NotificationVisibilityProvider;
import com.android.systemui.statusbar.notification.interruption.NotificationInterruptStateProvider;
import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow;
import com.android.systemui.statusbar.notification.row.NotificationTestHelper;
@@ -123,6 +125,8 @@
private FeatureFlags mFeatureFlags;
@Mock
private NotifPipeline mNotifPipeline;
+ @Mock
+ private NotificationVisibilityProvider mVisibilityProvider;
@Mock
private ActivityIntentHelper mActivityIntentHelper;
@@ -179,6 +183,11 @@
when(mFeatureFlags.isNewNotifPipelineRenderingEnabled()).thenReturn(false);
when(mOnUserInteractionCallback.getGroupSummaryToDismiss(mNotificationRow.getEntry()))
.thenReturn(null);
+ when(mVisibilityProvider.obtain(anyString(), anyBoolean())).thenAnswer(
+ invocation-> NotificationVisibility.obtain(invocation.getArgument(0), 0, 1, false));
+ when(mVisibilityProvider.obtain(any(NotificationEntry.class), anyBoolean())).thenAnswer(
+ invocation-> NotificationVisibility.obtain(
+ invocation.<NotificationEntry>getArgument(0).getKey(), 0, 1, false));
HeadsUpManagerPhone headsUpManager = mock(HeadsUpManagerPhone.class);
NotificationLaunchAnimatorControllerProvider notificationAnimationProvider =
@@ -195,6 +204,7 @@
mUiBgExecutor,
mEntryManager,
mNotifPipeline,
+ mVisibilityProvider,
headsUpManager,
mActivityStarter,
mClickNotifier,
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarTest.java
index 7a93d03..371b91f 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarTest.java
@@ -117,10 +117,12 @@
import com.android.systemui.statusbar.notification.NotificationEntryManager;
import com.android.systemui.statusbar.notification.NotificationFilter;
import com.android.systemui.statusbar.notification.NotificationWakeUpCoordinator;
+import com.android.systemui.statusbar.notification.collection.NotifPipeline;
import com.android.systemui.statusbar.notification.collection.NotificationEntry;
import com.android.systemui.statusbar.notification.collection.NotificationEntryBuilder;
import com.android.systemui.statusbar.notification.collection.legacy.VisualStabilityManager;
import com.android.systemui.statusbar.notification.collection.render.NotifShadeEventSource;
+import com.android.systemui.statusbar.notification.collection.render.NotificationVisibilityProvider;
import com.android.systemui.statusbar.notification.init.NotificationsController;
import com.android.systemui.statusbar.notification.interruption.BypassHeadsUpNotifier;
import com.android.systemui.statusbar.notification.interruption.NotificationInterruptStateProviderImpl;
@@ -264,6 +266,7 @@
@Mock private StatusBarIconController mIconController;
@Mock private LockscreenShadeTransitionController mLockscreenTransitionController;
@Mock private FeatureFlags mFeatureFlags;
+ @Mock private NotificationVisibilityProvider mVisibilityProvider;
@Mock private WallpaperManager mWallpaperManager;
@Mock private IWallpaperManager mIWallpaperManager;
@Mock private KeyguardUnlockAnimationController mKeyguardUnlockAnimationController;
@@ -279,6 +282,7 @@
private FakeExecutor mMainExecutor = new FakeExecutor(mFakeSystemClock);
private FakeExecutor mUiBgExecutor = new FakeExecutor(mFakeSystemClock);
private InitController mInitController = new InitController();
+ private final DumpManager mDumpManager = new DumpManager();
@Before
public void setup() throws Exception {
@@ -301,9 +305,17 @@
mContext.addMockSystemService(FingerprintManager.class, mock(FingerprintManager.class));
mMetricsLogger = new FakeMetricsLogger();
- NotificationLogger notificationLogger = new NotificationLogger(mNotificationListener,
- mUiBgExecutor, mock(NotificationEntryManager.class), mStatusBarStateController,
- mExpansionStateLogger, new NotificationPanelLoggerFake());
+ NotificationLogger notificationLogger = new NotificationLogger(
+ mNotificationListener,
+ mUiBgExecutor,
+ mFeatureFlags,
+ mVisibilityProvider,
+ mock(NotificationEntryManager.class),
+ mock(NotifPipeline.class),
+ mStatusBarStateController,
+ mExpansionStateLogger,
+ new NotificationPanelLoggerFake()
+ );
notificationLogger.setVisibilityReporter(mock(Runnable.class));
when(mCommandQueue.asBinder()).thenReturn(new Binder());
@@ -332,7 +344,7 @@
}).when(mStatusBarKeyguardViewManager).addAfterKeyguardGoneRunnable(any());
WakefulnessLifecycle wakefulnessLifecycle =
- new WakefulnessLifecycle(mContext, mIWallpaperManager, mock(DumpManager.class));
+ new WakefulnessLifecycle(mContext, mIWallpaperManager, mDumpManager);
wakefulnessLifecycle.dispatchStartedWakingUp(PowerManager.WAKE_REASON_UNKNOWN);
wakefulnessLifecycle.dispatchFinishedWakingUp();
@@ -390,7 +402,7 @@
mNetworkController,
mBatteryController,
mColorExtractor,
- new ScreenLifecycle(mock(DumpManager.class)),
+ new ScreenLifecycle(mDumpManager),
wakefulnessLifecycle,
mStatusBarStateController,
Optional.of(mBubblesManager),
@@ -440,6 +452,7 @@
mAnimationScheduler,
mLocationPublisher,
mIconController,
+ new StatusBarHideIconsForBouncerManager(mCommandQueue, mMainExecutor, mDumpManager),
mLockscreenTransitionController,
mFeatureFlags,
mKeyguardUnlockAnimationController,
@@ -450,7 +463,7 @@
mUnlockedScreenOffAnimationController,
Optional.of(mStartingSurface),
mTunerService,
- mock(DumpManager.class),
+ mDumpManager,
mActivityLaunchAnimator);
when(mKeyguardViewMediator.registerStatusBar(
any(StatusBar.class),
diff --git a/packages/SystemUI/tests/src/com/android/systemui/util/mockito/KotlinMockitoHelpers.kt b/packages/SystemUI/tests/src/com/android/systemui/util/mockito/KotlinMockitoHelpers.kt
index 483dc9f..eb54fe0 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/util/mockito/KotlinMockitoHelpers.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/util/mockito/KotlinMockitoHelpers.kt
@@ -67,6 +67,27 @@
inline fun <reified T : Any> mock(): T = Mockito.mock(T::class.java)
/**
+ * A kotlin implemented wrapper of [ArgumentCaptor] which prevents the following exception when
+ * kotlin tests are mocking kotlin objects and the methods take non-null parameters:
+ *
+ * java.lang.NullPointerException: capture() must not be null
+ */
+class KotlinArgumentCaptor<T> constructor(clazz: Class<T>) {
+ private val wrapped: ArgumentCaptor<T> = ArgumentCaptor.forClass(clazz)
+ fun capture(): T = wrapped.capture()
+ val value: T
+ get() = wrapped.value
+}
+
+/**
+ * Helper function for creating an argumentCaptor in kotlin.
+ *
+ * Generic T is nullable because implicitly bounded by Any?.
+ */
+inline fun <reified T : Any> kotlinArgumentCaptor(): KotlinArgumentCaptor<T> =
+ KotlinArgumentCaptor(T::class.java)
+
+/**
* Helper function for creating and using a single-use ArgumentCaptor in kotlin.
*
* val captor = argumentCaptor<Foo>()
@@ -76,6 +97,8 @@
* becomes:
*
* val captured = withArgCaptor<Foo> { verify(...).someMethod(capture()) }
+ *
+ * NOTE: this uses the KotlinArgumentCaptor to avoid the NullPointerException.
*/
-inline fun <reified T : Any> withArgCaptor(block: ArgumentCaptor<T>.() -> Unit): T =
- argumentCaptor<T>().apply { block() }.value
\ No newline at end of file
+inline fun <reified T : Any> withArgCaptor(block: KotlinArgumentCaptor<T>.() -> Unit): T =
+ kotlinArgumentCaptor<T>().apply { block() }.value
diff --git a/packages/SystemUI/tests/src/com/android/systemui/utils/leaks/FakeNetworkController.java b/packages/SystemUI/tests/src/com/android/systemui/utils/leaks/FakeNetworkController.java
index 8ea9da6..33ef9cf 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/utils/leaks/FakeNetworkController.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/utils/leaks/FakeNetworkController.java
@@ -18,8 +18,9 @@
import android.testing.LeakCheck;
import com.android.settingslib.net.DataUsageController;
+import com.android.systemui.statusbar.connectivity.AccessPointController;
import com.android.systemui.statusbar.connectivity.NetworkController;
-import com.android.systemui.statusbar.connectivity.NetworkController.SignalCallback;
+import com.android.systemui.statusbar.connectivity.SignalCallback;
import com.android.systemui.statusbar.policy.DataSaverController;
public class FakeNetworkController extends BaseLeakChecker<SignalCallback>
diff --git a/packages/SystemUI/tests/src/com/android/systemui/wmshell/BubblesTest.java b/packages/SystemUI/tests/src/com/android/systemui/wmshell/BubblesTest.java
index 15a92dc..c900ad5 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/wmshell/BubblesTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/wmshell/BubblesTest.java
@@ -92,6 +92,7 @@
import com.android.systemui.statusbar.notification.collection.NotificationEntry;
import com.android.systemui.statusbar.notification.collection.NotificationEntryBuilder;
import com.android.systemui.statusbar.notification.collection.legacy.NotificationGroupManagerLegacy;
+import com.android.systemui.statusbar.notification.collection.render.NotificationVisibilityProvider;
import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow;
import com.android.systemui.statusbar.notification.row.NotificationTestHelper;
import com.android.systemui.statusbar.phone.DozeParameters;
@@ -228,6 +229,8 @@
@Mock
private IStatusBarService mStatusBarService;
@Mock
+ private NotificationVisibilityProvider mVisibilityProvider;
+ @Mock
private LauncherApps mLauncherApps;
@Mock
private WindowManagerShellWrapper mWindowManagerShellWrapper;
@@ -354,6 +357,7 @@
mConfigurationController,
mStatusBarService,
mock(INotificationManager.class),
+ mVisibilityProvider,
interruptionStateProvider,
mZenModeController,
mLockscreenUserManager,
diff --git a/packages/SystemUI/tests/src/com/android/systemui/wmshell/NewNotifPipelineBubblesTest.java b/packages/SystemUI/tests/src/com/android/systemui/wmshell/NewNotifPipelineBubblesTest.java
index 43b181e..5ab2113 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/wmshell/NewNotifPipelineBubblesTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/wmshell/NewNotifPipelineBubblesTest.java
@@ -78,6 +78,7 @@
import com.android.systemui.statusbar.notification.collection.NotificationEntry;
import com.android.systemui.statusbar.notification.collection.legacy.NotificationGroupManagerLegacy;
import com.android.systemui.statusbar.notification.collection.notifcollection.NotifCollectionListener;
+import com.android.systemui.statusbar.notification.collection.render.NotificationVisibilityProvider;
import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow;
import com.android.systemui.statusbar.notification.row.NotificationTestHelper;
import com.android.systemui.statusbar.phone.DozeParameters;
@@ -204,6 +205,8 @@
@Mock
private IStatusBarService mStatusBarService;
@Mock
+ private NotificationVisibilityProvider mVisibilityProvider;
+ @Mock
private LauncherApps mLauncherApps;
@Mock
private WindowManagerShellWrapper mWindowManagerShellWrapper;
@@ -319,6 +322,7 @@
mConfigurationController,
mStatusBarService,
mock(INotificationManager.class),
+ mVisibilityProvider,
interruptionStateProvider,
mZenModeController,
mLockscreenUserManager,
diff --git a/services/core/java/com/android/server/BatteryService.java b/services/core/java/com/android/server/BatteryService.java
index 0146aa8..728efa5 100644
--- a/services/core/java/com/android/server/BatteryService.java
+++ b/services/core/java/com/android/server/BatteryService.java
@@ -17,6 +17,7 @@
package com.android.server;
import static com.android.internal.logging.nano.MetricsProto.MetricsEvent;
+import static com.android.server.health.Utils.copy;
import android.annotation.Nullable;
import android.app.ActivityManager;
@@ -26,13 +27,7 @@
import android.content.Intent;
import android.database.ContentObserver;
import android.hardware.health.V1_0.HealthInfo;
-import android.hardware.health.V2_0.IHealth;
-import android.hardware.health.V2_0.Result;
import android.hardware.health.V2_1.BatteryCapacityLevel;
-import android.hardware.health.V2_1.Constants;
-import android.hardware.health.V2_1.IHealthInfoCallback;
-import android.hidl.manager.V1_0.IServiceManager;
-import android.hidl.manager.V1_0.IServiceNotification;
import android.metrics.LogMaker;
import android.os.BatteryManager;
import android.os.BatteryManagerInternal;
@@ -44,7 +39,6 @@
import android.os.DropBoxManager;
import android.os.FileUtils;
import android.os.Handler;
-import android.os.HandlerThread;
import android.os.IBatteryPropertiesRegistrar;
import android.os.IBinder;
import android.os.OsProtoEnums;
@@ -62,15 +56,14 @@
import android.service.battery.BatteryServiceDumpProto;
import android.sysprop.PowerProperties;
import android.util.EventLog;
-import android.util.MutableInt;
import android.util.Slog;
import android.util.proto.ProtoOutputStream;
-import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.app.IBatteryStats;
import com.android.internal.logging.MetricsLogger;
import com.android.internal.util.DumpUtils;
import com.android.server.am.BatteryStatsService;
+import com.android.server.health.HealthServiceWrapper;
import com.android.server.lights.LightsManager;
import com.android.server.lights.LogicalLight;
@@ -82,8 +75,6 @@
import java.util.ArrayDeque;
import java.util.ArrayList;
import java.util.NoSuchElementException;
-import java.util.Objects;
-import java.util.concurrent.atomic.AtomicReference;
/**
* <p>BatteryService monitors the charging status, and charge level of the device
@@ -191,7 +182,6 @@
private ActivityManagerInternal mActivityManagerInternal;
private HealthServiceWrapper mHealthServiceWrapper;
- private HealthHalCallback mHealthHalCallback;
private BatteryPropertiesRegistrar mBatteryPropertiesRegistrar;
private ArrayDeque<Bundle> mBatteryLevelsEventQueue;
private long mLastBatteryLevelChangedSentMs;
@@ -274,13 +264,9 @@
private void registerHealthCallback() {
traceBegin("HealthInitWrapper");
- mHealthServiceWrapper = new HealthServiceWrapper();
- mHealthHalCallback = new HealthHalCallback();
// IHealth is lazily retrieved.
try {
- mHealthServiceWrapper.init(mHealthHalCallback,
- new HealthServiceWrapper.IServiceManagerSupplier() {},
- new HealthServiceWrapper.IHealthSupplier() {});
+ mHealthServiceWrapper = HealthServiceWrapper.create(this::update);
} catch (RemoteException ex) {
Slog.e(TAG, "health: cannot register callback. (RemoteException)");
throw ex.rethrowFromSystemServer();
@@ -454,25 +440,6 @@
traceEnd();
}
- private static void copy(HealthInfo dst, HealthInfo src) {
- dst.chargerAcOnline = src.chargerAcOnline;
- dst.chargerUsbOnline = src.chargerUsbOnline;
- dst.chargerWirelessOnline = src.chargerWirelessOnline;
- dst.maxChargingCurrent = src.maxChargingCurrent;
- dst.maxChargingVoltage = src.maxChargingVoltage;
- dst.batteryStatus = src.batteryStatus;
- dst.batteryHealth = src.batteryHealth;
- dst.batteryPresent = src.batteryPresent;
- dst.batteryLevel = src.batteryLevel;
- dst.batteryVoltage = src.batteryVoltage;
- dst.batteryTemperature = src.batteryTemperature;
- dst.batteryCurrent = src.batteryCurrent;
- dst.batteryCycleCount = src.batteryCycleCount;
- dst.batteryFullCharge = src.batteryFullCharge;
- dst.batteryChargeCounter = src.batteryChargeCounter;
- dst.batteryTechnology = src.batteryTechnology;
- }
-
private static int plugType(HealthInfo healthInfo) {
if (healthInfo.chargerAcOnline) {
return BatteryManager.BATTERY_PLUGGED_AC;
@@ -1184,64 +1151,6 @@
}
}
- private final class HealthHalCallback extends IHealthInfoCallback.Stub
- implements HealthServiceWrapper.Callback {
- @Override public void healthInfoChanged(android.hardware.health.V2_0.HealthInfo props) {
- android.hardware.health.V2_1.HealthInfo propsLatest =
- new android.hardware.health.V2_1.HealthInfo();
- propsLatest.legacy = props;
-
- propsLatest.batteryCapacityLevel = BatteryCapacityLevel.UNSUPPORTED;
- propsLatest.batteryChargeTimeToFullNowSeconds =
- Constants.BATTERY_CHARGE_TIME_TO_FULL_NOW_SECONDS_UNSUPPORTED;
-
- BatteryService.this.update(propsLatest);
- }
-
- @Override public void healthInfoChanged_2_1(android.hardware.health.V2_1.HealthInfo props) {
- BatteryService.this.update(props);
- }
-
- // on new service registered
- @Override public void onRegistration(IHealth oldService, IHealth newService,
- String instance) {
- if (newService == null) return;
-
- traceBegin("HealthUnregisterCallback");
- try {
- if (oldService != null) {
- int r = oldService.unregisterCallback(this);
- if (r != Result.SUCCESS) {
- Slog.w(TAG, "health: cannot unregister previous callback: " +
- Result.toString(r));
- }
- }
- } catch (RemoteException ex) {
- Slog.w(TAG, "health: cannot unregister previous callback (transaction error): "
- + ex.getMessage());
- } finally {
- traceEnd();
- }
-
- traceBegin("HealthRegisterCallback");
- try {
- int r = newService.registerCallback(this);
- if (r != Result.SUCCESS) {
- Slog.w(TAG, "health: cannot register callback: " + Result.toString(r));
- return;
- }
- // registerCallback does NOT guarantee that update is called
- // immediately, so request a manual update here.
- newService.update();
- } catch (RemoteException ex) {
- Slog.e(TAG, "health: cannot register callback (transaction error): "
- + ex.getMessage());
- } finally {
- traceEnd();
- }
- }
- }
-
private final class BinderService extends Binder {
@Override protected void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
if (!DumpUtils.checkDumpPermission(mContext, TAG, pw)) return;
@@ -1265,71 +1174,11 @@
private final class BatteryPropertiesRegistrar extends IBatteryPropertiesRegistrar.Stub {
@Override
public int getProperty(int id, final BatteryProperty prop) throws RemoteException {
- traceBegin("HealthGetProperty");
- try {
- IHealth service = mHealthServiceWrapper.getLastService();
- if (service == null) throw new RemoteException("no health service");
- final MutableInt outResult = new MutableInt(Result.NOT_SUPPORTED);
- switch(id) {
- case BatteryManager.BATTERY_PROPERTY_CHARGE_COUNTER:
- service.getChargeCounter((int result, int value) -> {
- outResult.value = result;
- if (result == Result.SUCCESS) prop.setLong(value);
- });
- break;
- case BatteryManager.BATTERY_PROPERTY_CURRENT_NOW:
- service.getCurrentNow((int result, int value) -> {
- outResult.value = result;
- if (result == Result.SUCCESS) prop.setLong(value);
- });
- break;
- case BatteryManager.BATTERY_PROPERTY_CURRENT_AVERAGE:
- service.getCurrentAverage((int result, int value) -> {
- outResult.value = result;
- if (result == Result.SUCCESS) prop.setLong(value);
- });
- break;
- case BatteryManager.BATTERY_PROPERTY_CAPACITY:
- service.getCapacity((int result, int value) -> {
- outResult.value = result;
- if (result == Result.SUCCESS) prop.setLong(value);
- });
- break;
- case BatteryManager.BATTERY_PROPERTY_STATUS:
- service.getChargeStatus((int result, int value) -> {
- outResult.value = result;
- if (result == Result.SUCCESS) prop.setLong(value);
- });
- break;
- case BatteryManager.BATTERY_PROPERTY_ENERGY_COUNTER:
- service.getEnergyCounter((int result, long value) -> {
- outResult.value = result;
- if (result == Result.SUCCESS) prop.setLong(value);
- });
- break;
- }
- return outResult.value;
- } finally {
- traceEnd();
- }
+ return mHealthServiceWrapper.getProperty(id, prop);
}
@Override
public void scheduleUpdate() throws RemoteException {
- mHealthServiceWrapper.getHandlerThread().getThreadHandler().post(() -> {
- traceBegin("HealthScheduleUpdate");
- try {
- IHealth service = mHealthServiceWrapper.getLastService();
- if (service == null) {
- Slog.e(TAG, "no health service");
- return;
- }
- service.update();
- } catch (RemoteException ex) {
- Slog.e(TAG, "Cannot call update on health HAL", ex);
- } finally {
- traceEnd();
- }
- });
+ mHealthServiceWrapper.scheduleUpdate();
}
}
@@ -1418,184 +1267,4 @@
BatteryService.this.suspendBatteryInput();
}
}
-
- /**
- * HealthServiceWrapper wraps the internal IHealth service and refreshes the service when
- * necessary.
- *
- * On new registration of IHealth service, {@link #onRegistration onRegistration} is called and
- * the internal service is refreshed.
- * On death of an existing IHealth service, the internal service is NOT cleared to avoid
- * race condition between death notification and new service notification. Hence,
- * a caller must check for transaction errors when calling into the service.
- *
- * @hide Should only be used internally.
- */
- public static final class HealthServiceWrapper {
- private static final String TAG = "HealthServiceWrapper";
- public static final String INSTANCE_VENDOR = "default";
-
- private final IServiceNotification mNotification = new Notification();
- private final HandlerThread mHandlerThread = new HandlerThread("HealthServiceHwbinder");
- // These variables are fixed after init.
- private Callback mCallback;
- private IHealthSupplier mHealthSupplier;
- private String mInstanceName;
-
- // Last IHealth service received.
- private final AtomicReference<IHealth> mLastService = new AtomicReference<>();
-
- /**
- * init should be called after constructor. For testing purposes, init is not called by
- * constructor.
- */
- public HealthServiceWrapper() {
- }
-
- public IHealth getLastService() {
- return mLastService.get();
- }
-
- /**
- * See {@link #init(Callback, IServiceManagerSupplier, IHealthSupplier)}
- */
- public void init() throws RemoteException, NoSuchElementException {
- init(/* callback= */null, new HealthServiceWrapper.IServiceManagerSupplier() {},
- new HealthServiceWrapper.IHealthSupplier() {});
- }
-
- /**
- * Start monitoring registration of new IHealth services. Only instance
- * {@link #INSTANCE_VENDOR} and in device / framework manifest are used. This function should
- * only be called once.
- *
- * mCallback.onRegistration() is called synchronously (aka in init thread) before
- * this method returns if callback is not null.
- *
- * @throws RemoteException transaction error when talking to IServiceManager
- * @throws NoSuchElementException if one of the following cases:
- * - No service manager;
- * - {@link #INSTANCE_VENDOR} is not in manifests (i.e. not
- * available on this device), or none of these instances are available to current
- * process.
- * @throws NullPointerException when supplier is null
- */
- void init(@Nullable Callback callback,
- IServiceManagerSupplier managerSupplier,
- IHealthSupplier healthSupplier)
- throws RemoteException, NoSuchElementException, NullPointerException {
- if (managerSupplier == null || healthSupplier == null) {
- throw new NullPointerException();
- }
- IServiceManager manager;
-
- mHealthSupplier = healthSupplier;
-
- // Initialize mLastService and call callback for the first time (in init thread)
- IHealth newService = null;
- traceBegin("HealthInitGetService_" + INSTANCE_VENDOR);
- try {
- newService = healthSupplier.get(INSTANCE_VENDOR);
- } catch (NoSuchElementException ex) {
- /* ignored, handled below */
- } finally {
- traceEnd();
- }
- if (newService != null) {
- mInstanceName = INSTANCE_VENDOR;
- mLastService.set(newService);
- }
-
- if (mInstanceName == null || newService == null) {
- throw new NoSuchElementException(String.format(
- "IHealth service instance %s isn't available. Perhaps no permission?",
- INSTANCE_VENDOR));
- }
-
- if (callback != null) {
- mCallback = callback;
- mCallback.onRegistration(null, newService, mInstanceName);
- }
-
- // Register for future service registrations
- traceBegin("HealthInitRegisterNotification");
- mHandlerThread.start();
- try {
- managerSupplier.get().registerForNotifications(
- IHealth.kInterfaceName, mInstanceName, mNotification);
- } finally {
- traceEnd();
- }
- Slog.i(TAG, "health: HealthServiceWrapper listening to instance " + mInstanceName);
- }
-
- @VisibleForTesting
- HandlerThread getHandlerThread() {
- return mHandlerThread;
- }
-
- interface Callback {
- /**
- * This function is invoked asynchronously when a new and related IServiceNotification
- * is received.
- * @param service the recently retrieved service from IServiceManager.
- * Can be a dead service before service notification of a new service is delivered.
- * Implementation must handle cases for {@link RemoteException}s when calling
- * into service.
- * @param instance instance name.
- */
- void onRegistration(IHealth oldService, IHealth newService, String instance);
- }
-
- /**
- * Supplier of services.
- * Must not return null; throw {@link NoSuchElementException} if a service is not available.
- */
- interface IServiceManagerSupplier {
- default IServiceManager get() throws NoSuchElementException, RemoteException {
- return IServiceManager.getService();
- }
- }
- /**
- * Supplier of services.
- * Must not return null; throw {@link NoSuchElementException} if a service is not available.
- */
- interface IHealthSupplier {
- default IHealth get(String name) throws NoSuchElementException, RemoteException {
- return IHealth.getService(name, true /* retry */);
- }
- }
-
- private class Notification extends IServiceNotification.Stub {
- @Override
- public final void onRegistration(String interfaceName, String instanceName,
- boolean preexisting) {
- if (!IHealth.kInterfaceName.equals(interfaceName)) return;
- if (!mInstanceName.equals(instanceName)) return;
-
- // This runnable only runs on mHandlerThread and ordering is ensured, hence
- // no locking is needed inside the runnable.
- mHandlerThread.getThreadHandler().post(new Runnable() {
- @Override
- public void run() {
- try {
- IHealth newService = mHealthSupplier.get(mInstanceName);
- IHealth oldService = mLastService.getAndSet(newService);
-
- // preexisting may be inaccurate (race). Check for equality here.
- if (Objects.equals(newService, oldService)) return;
-
- Slog.i(TAG, "health: new instance registered " + mInstanceName);
- // #init() may be called with null callback. Skip null callbacks.
- if (mCallback == null) return;
- mCallback.onRegistration(oldService, newService, mInstanceName);
- } catch (NoSuchElementException | RemoteException ex) {
- Slog.e(TAG, "health: Cannot get instance '" + mInstanceName
- + "': " + ex.getMessage() + ". Perhaps no permission?");
- }
- }
- });
- }
- }
- }
}
diff --git a/services/core/java/com/android/server/am/ActiveServices.java b/services/core/java/com/android/server/am/ActiveServices.java
index b5623be..bf744cf 100644
--- a/services/core/java/com/android/server/am/ActiveServices.java
+++ b/services/core/java/com/android/server/am/ActiveServices.java
@@ -90,6 +90,7 @@
import android.app.ForegroundServiceDidNotStartInTimeException;
import android.app.ForegroundServiceStartNotAllowedException;
import android.app.IApplicationThread;
+import android.app.IForegroundServiceObserver;
import android.app.IServiceConnection;
import android.app.Notification;
import android.app.NotificationManager;
@@ -128,6 +129,7 @@
import android.os.PowerExemptionManager.ReasonCode;
import android.os.Process;
import android.os.RemoteCallback;
+import android.os.RemoteCallbackList;
import android.os.RemoteException;
import android.os.ServiceManager;
import android.os.SystemClock;
@@ -267,6 +269,12 @@
final SparseLongArray mFgsDeferralEligible = new SparseLongArray();
/**
+ * Foreground service observers: track what apps have FGSes
+ */
+ final RemoteCallbackList<IForegroundServiceObserver> mFgsObservers =
+ new RemoteCallbackList<>();
+
+ /**
* Map of services that are asked to be brought up (start/binding) but not ready to.
*/
private ArrayMap<ServiceRecord, ArrayList<Runnable>> mPendingBringups = new ArrayMap<>();
@@ -1382,6 +1390,10 @@
return false;
}
+ /**
+ * Put the named service into the foreground mode
+ */
+ @GuardedBy("mAm")
public void setServiceForegroundLocked(ComponentName className, IBinder token,
int id, Notification notification, int flags, int foregroundServiceType) {
final int userId = UserHandle.getCallingUserId();
@@ -1746,6 +1758,7 @@
/**
* @param id Notification ID. Zero === exit foreground state for the given service.
*/
+ @GuardedBy("mAm")
private void setServiceForegroundInnerLocked(final ServiceRecord r, int id,
Notification notification, int flags, int foregroundServiceType) {
if (id != 0) {
@@ -1981,6 +1994,7 @@
}
// Even if the service is already a FGS, we need to update the notification,
// so we need to call it again.
+ signalForegroundServiceObserversLocked(r);
r.postNotification();
if (r.app != null) {
updateServiceForegroundLocked(psr, true);
@@ -2060,6 +2074,7 @@
r.mFgsExitTime > r.mFgsEnterTime
? (int)(r.mFgsExitTime - r.mFgsEnterTime) : 0);
r.mFgsNotificationWasDeferred = false;
+ signalForegroundServiceObserversLocked(r);
resetFgsRestrictionLocked(r);
mAm.updateForegroundServiceUsageStats(r.name, r.userId, false);
if (r.app != null) {
@@ -4552,7 +4567,8 @@
}
cancelForegroundNotificationLocked(r);
- if (r.isForeground) {
+ final boolean exitingFg = r.isForeground;
+ if (exitingFg) {
decActiveForegroundAppLocked(smap, r);
synchronized (mAm.mProcessStats.mLock) {
ServiceState stracker = r.getTracker();
@@ -4578,6 +4594,11 @@
r.foregroundId = 0;
r.foregroundNoti = null;
resetFgsRestrictionLocked(r);
+ // Signal FGS observers *after* changing the isForeground state, and
+ // only if this was an actual state change.
+ if (exitingFg) {
+ signalForegroundServiceObserversLocked(r);
+ }
// Clear start entries.
r.clearDeliveredStartsLocked();
@@ -5133,6 +5154,54 @@
}
}
+ @GuardedBy("mAm")
+ private void signalForegroundServiceObserversLocked(ServiceRecord r) {
+ final int num = mFgsObservers.beginBroadcast();
+ for (int i = 0; i < num; i++) {
+ try {
+ mFgsObservers.getBroadcastItem(i).onForegroundStateChanged(r,
+ r.appInfo.packageName, r.userId, r.isForeground);
+ } catch (RemoteException e) {
+ // Will be unregistered automatically by RemoteCallbackList's dead-object
+ // tracking, so nothing we need to do here.
+ }
+ }
+ mFgsObservers.finishBroadcast();
+ }
+
+ @GuardedBy("mAm")
+ boolean registerForegroundServiceObserverLocked(final int callingUid,
+ IForegroundServiceObserver callback) {
+ // We always tell the newly-registered observer about any current FGSes. The
+ // most common case for this is a SysUI crash & relaunch; it needs to
+ // reconstruct its tracking of stoppable-FGS-hosting apps.
+ try {
+ final int mapSize = mServiceMap.size();
+ for (int mapIndex = 0; mapIndex < mapSize; mapIndex++) {
+ final ServiceMap smap = mServiceMap.valueAt(mapIndex);
+ if (smap != null) {
+ final int numServices = smap.mServicesByInstanceName.size();
+ for (int i = 0; i < numServices; i++) {
+ final ServiceRecord sr = smap.mServicesByInstanceName.valueAt(i);
+ if (sr.isForeground && callingUid == sr.appInfo.uid) {
+ callback.onForegroundStateChanged(sr, sr.appInfo.packageName,
+ sr.userId, true);
+ }
+ }
+ }
+ }
+ // Callback is fine, go ahead and record it
+ mFgsObservers.register(callback);
+ } catch (RemoteException e) {
+ // Whoops, something wrong with the callback. Don't register it, and
+ // report error back to the caller.
+ Slog.e(TAG_SERVICE, "Bad FGS observer from uid " + callingUid);
+ return false;
+ }
+
+ return true;
+ }
+
void forceStopPackageLocked(String packageName, int userId) {
ServiceMap smap = mServiceMap.get(userId);
if (smap != null && smap.mActiveForegroundApps.size() > 0) {
diff --git a/services/core/java/com/android/server/am/ActivityManagerService.java b/services/core/java/com/android/server/am/ActivityManagerService.java
index b1a1e0d..0eed190 100644
--- a/services/core/java/com/android/server/am/ActivityManagerService.java
+++ b/services/core/java/com/android/server/am/ActivityManagerService.java
@@ -21,6 +21,7 @@
import static android.Manifest.permission.FILTER_EVENTS;
import static android.Manifest.permission.INTERACT_ACROSS_USERS;
import static android.Manifest.permission.INTERACT_ACROSS_USERS_FULL;
+import static android.Manifest.permission.MANAGE_ACTIVITY_TASKS;
import static android.Manifest.permission.START_ACTIVITIES_FROM_BACKGROUND;
import static android.Manifest.permission.START_FOREGROUND_SERVICES_FROM_BACKGROUND;
import static android.app.ActivityManager.INSTR_FLAG_DISABLE_HIDDEN_API_CHECKS;
@@ -31,7 +32,7 @@
import static android.app.ActivityManager.PROCESS_STATE_IMPORTANT_FOREGROUND;
import static android.app.ActivityManager.PROCESS_STATE_NONEXISTENT;
import static android.app.ActivityManager.PROCESS_STATE_TOP;
-import static android.app.ActivityManager.StopBgUsersOnSwitch;
+import static android.app.ActivityManager.StopUserOnSwitch;
import static android.app.ActivityManagerInternal.ALLOW_FULL_ONLY;
import static android.app.ActivityManagerInternal.ALLOW_NON_FULL;
import static android.app.AppOpsManager.OP_NONE;
@@ -168,6 +169,7 @@
import android.app.IActivityController;
import android.app.IActivityManager;
import android.app.IApplicationThread;
+import android.app.IForegroundServiceObserver;
import android.app.IInstrumentationWatcher;
import android.app.INotificationManager;
import android.app.IProcessObserver;
@@ -442,6 +444,14 @@
private static final String SYSTEM_PROPERTY_DEVICE_PROVISIONED =
"persist.sys.device_provisioned";
+ /**
+ * Enabling this flag enforces the requirement for context registered receivers to use one of
+ * {@link Context#RECEIVER_EXPORTED} or {@link Context#RECEIVER_NOT_EXPORTED} for unprotected
+ * broadcasts
+ */
+ private static final boolean ENFORCE_DYNAMIC_RECEIVER_EXPLICIT_EXPORT =
+ SystemProperties.getBoolean("fw.enforce_dynamic_receiver_explicit_export", false);
+
static final String TAG = TAG_WITH_CLASS_NAME ? "ActivityManagerService" : TAG_AM;
static final String TAG_BACKUP = TAG + POSTFIX_BACKUP;
private static final String TAG_BROADCAST = TAG + POSTFIX_BROADCAST;
@@ -3754,12 +3764,12 @@
@Override
public void makeServicesNonForeground(final String packageName, int userId) {
- if (checkCallingPermission(android.Manifest.permission.MANAGE_ACTIVITY_TASKS)
+ if (checkCallingPermission(MANAGE_ACTIVITY_TASKS)
!= PackageManager.PERMISSION_GRANTED) {
String msg = "Permission Denial: makeServicesNonForeground() from pid="
+ Binder.getCallingPid()
+ ", uid=" + Binder.getCallingUid()
- + " requires " + android.Manifest.permission.MANAGE_ACTIVITY_TASKS;
+ + " requires " + MANAGE_ACTIVITY_TASKS;
Slog.w(TAG, msg);
throw new SecurityException(msg);
}
@@ -3776,6 +3786,27 @@
}
@Override
+ public boolean registerForegroundServiceObserver(IForegroundServiceObserver callback) {
+ final int callingUid = Binder.getCallingUid();
+ final int permActivityTasks = checkCallingPermission(MANAGE_ACTIVITY_TASKS);
+ final int permAcrossUsersFull = checkCallingPermission(INTERACT_ACROSS_USERS_FULL);
+ if (permActivityTasks != PackageManager.PERMISSION_GRANTED
+ || permAcrossUsersFull != PERMISSION_GRANTED) {
+ String msg = "Permission Denial: registerForegroundServiceObserver() from pid="
+ + Binder.getCallingPid()
+ + ", uid=" + callingUid
+ + " requires " + MANAGE_ACTIVITY_TASKS
+ + " and " + INTERACT_ACROSS_USERS_FULL;
+ Slog.w(TAG, msg);
+ throw new SecurityException(msg);
+ }
+
+ synchronized (this) {
+ return mServices.registerForegroundServiceObserverLocked(callingUid, callback);
+ }
+ }
+
+ @Override
public void forceStopPackage(final String packageName, int userId) {
if (checkCallingPermission(android.Manifest.permission.FORCE_STOP_PACKAGES)
!= PackageManager.PERMISSION_GRANTED) {
@@ -12623,28 +12654,43 @@
// an error so the consumer can know to explicitly set the value for their flag.
// If the caller is registering for a sticky broadcast with a null receiver, we won't
// require a flag
- if (!onlyProtectedBroadcasts && receiver != null && (
- CompatChanges.isChangeEnabled(
- DYNAMIC_RECEIVER_EXPLICIT_EXPORT_REQUIRED, callingUid)
- && (flags & (Context.RECEIVER_EXPORTED | Context.RECEIVER_NOT_EXPORTED))
- == 0)) {
- Slog.e(TAG,
- callerPackage + ": Targeting T+ (version " + Build.VERSION_CODES.TIRAMISU
- + " and above) requires that one of RECEIVER_EXPORTED or "
- + "RECEIVER_NOT_EXPORTED be specified when registering a receiver");
- } else if (((flags & Context.RECEIVER_EXPORTED) != 0) && (
+ final boolean explicitExportStateDefined =
+ (flags & (Context.RECEIVER_EXPORTED | Context.RECEIVER_NOT_EXPORTED)) != 0;
+ if (((flags & Context.RECEIVER_EXPORTED) != 0) && (
(flags & Context.RECEIVER_NOT_EXPORTED) != 0)) {
throw new IllegalArgumentException(
"Receiver can't specify both RECEIVER_EXPORTED and RECEIVER_NOT_EXPORTED"
+ "flag");
}
+ if (CompatChanges.isChangeEnabled(DYNAMIC_RECEIVER_EXPLICIT_EXPORT_REQUIRED,
+ callingUid)
+ && !explicitExportStateDefined) {
+ if (ENFORCE_DYNAMIC_RECEIVER_EXPLICIT_EXPORT) {
+ throw new SecurityException(
+ callerPackage + ": Targeting T+ (version "
+ + Build.VERSION_CODES.TIRAMISU
+ + " and above) requires that one of RECEIVER_EXPORTED or "
+ + "RECEIVER_NOT_EXPORTED be specified when registering a "
+ + "receiver");
+ } else {
+ Slog.wtf(TAG,
+ callerPackage + ": Targeting T+ (version "
+ + Build.VERSION_CODES.TIRAMISU
+ + " and above) requires that one of RECEIVER_EXPORTED or "
+ + "RECEIVER_NOT_EXPORTED be specified when registering a "
+ + "receiver");
+ // Assume default behavior-- flag check is not enforced
+ flags |= Context.RECEIVER_EXPORTED;
+ }
+ } else if (!CompatChanges.isChangeEnabled(DYNAMIC_RECEIVER_EXPLICIT_EXPORT_REQUIRED,
+ callingUid)) {
+ // Change is not enabled, thus not targeting T+. Assume exported.
+ flags |= Context.RECEIVER_EXPORTED;
+ }
}
// Dynamic receivers are exported by default for versions prior to T
- final boolean exported =
- ((flags & Context.RECEIVER_EXPORTED) != 0
- || (!CompatChanges.isChangeEnabled(
- DYNAMIC_RECEIVER_EXPLICIT_EXPORT_REQUIRED, callingUid)));
+ final boolean exported = (flags & Context.RECEIVER_EXPORTED) != 0;
ArrayList<Intent> allSticky = null;
if (stickyIntents != null) {
@@ -15333,8 +15379,8 @@
}
@Override
- public void setStopBackgroundUsersOnSwitch(@StopBgUsersOnSwitch int value) {
- mUserController.setStopBackgroundUsersOnSwitch(value);
+ public void setStopUserOnSwitch(@StopUserOnSwitch int value) {
+ mUserController.setStopUserOnSwitch(value);
}
@Override
@@ -16662,8 +16708,8 @@
}
@Override
- public void setStopBackgroundUsersOnSwitch(int value) {
- ActivityManagerService.this.setStopBackgroundUsersOnSwitch(value);
+ public void setStopUserOnSwitch(int value) {
+ ActivityManagerService.this.setStopUserOnSwitch(value);
}
}
diff --git a/services/core/java/com/android/server/am/ActivityManagerShellCommand.java b/services/core/java/com/android/server/am/ActivityManagerShellCommand.java
index 25adddd..b144c8d 100644
--- a/services/core/java/com/android/server/am/ActivityManagerShellCommand.java
+++ b/services/core/java/com/android/server/am/ActivityManagerShellCommand.java
@@ -333,7 +333,7 @@
case "get-isolated-pids":
return runGetIsolatedProcesses(pw);
case "set-stop-user-on-switch":
- return runSetStopBackgroundUsersOnSwitch(pw);
+ return runSetStopUserOnSwitch(pw);
default:
return handleDefaultCommands(cmd);
}
@@ -3184,25 +3184,24 @@
return 0;
}
- private int runSetStopBackgroundUsersOnSwitch(PrintWriter pw) throws RemoteException {
+ private int runSetStopUserOnSwitch(PrintWriter pw) throws RemoteException {
mInternal.enforceCallingPermission(android.Manifest.permission.INTERACT_ACROSS_USERS_FULL,
- "setStopBackgroundUsersOnSwitch()");
+ "setStopUserOnSwitch()");
String arg = getNextArg();
if (arg == null) {
- Slogf.i(TAG, "runSetStopBackgroundUsersOnSwitch(): resetting to default value");
- mInternal.setStopBackgroundUsersOnSwitch(
- ActivityManager.STOP_BG_USERS_ON_SWITCH_DEFAULT);
+ Slogf.i(TAG, "setStopUserOnSwitch(): resetting to default value");
+ mInternal.setStopUserOnSwitch(ActivityManager.STOP_USER_ON_SWITCH_DEFAULT);
pw.println("Reset to default value");
return 0;
}
boolean stop = Boolean.parseBoolean(arg);
int value = stop
- ? ActivityManager.STOP_BG_USERS_ON_SWITCH_TRUE
- : ActivityManager.STOP_BG_USERS_ON_SWITCH_FALSE;
+ ? ActivityManager.STOP_USER_ON_SWITCH_TRUE
+ : ActivityManager.STOP_USER_ON_SWITCH_FALSE;
- Slogf.i(TAG, "runSetStopBackgroundUsersOnSwitch(): setting to %d (%b)", value, stop);
- mInternal.setStopBackgroundUsersOnSwitch(value);
+ Slogf.i(TAG, "runSetStopUserOnSwitch(): setting to %d (%b)", value, stop);
+ mInternal.setStopUserOnSwitch(value);
pw.println("Set to " + stop);
return 0;
diff --git a/services/core/java/com/android/server/am/BroadcastQueue.java b/services/core/java/com/android/server/am/BroadcastQueue.java
index 8638c7d..fd6f099 100644
--- a/services/core/java/com/android/server/am/BroadcastQueue.java
+++ b/services/core/java/com/android/server/am/BroadcastQueue.java
@@ -792,7 +792,7 @@
+ " due to receiver " + filter.receiverList.app
+ " (uid " + filter.receiverList.uid + ")"
+ " not specifying RECEIVER_EXPORTED");
- // skip = true;
+ skip = true;
}
if (skip) {
diff --git a/services/core/java/com/android/server/am/UserController.java b/services/core/java/com/android/server/am/UserController.java
index 0c518a0..6dbdead 100644
--- a/services/core/java/com/android/server/am/UserController.java
+++ b/services/core/java/com/android/server/am/UserController.java
@@ -19,9 +19,9 @@
import static android.Manifest.permission.INTERACT_ACROSS_PROFILES;
import static android.Manifest.permission.INTERACT_ACROSS_USERS;
import static android.Manifest.permission.INTERACT_ACROSS_USERS_FULL;
-import static android.app.ActivityManager.STOP_BG_USERS_ON_SWITCH_DEFAULT;
-import static android.app.ActivityManager.STOP_BG_USERS_ON_SWITCH_TRUE;
-import static android.app.ActivityManager.StopBgUsersOnSwitch;
+import static android.app.ActivityManager.STOP_USER_ON_SWITCH_DEFAULT;
+import static android.app.ActivityManager.STOP_USER_ON_SWITCH_TRUE;
+import static android.app.ActivityManager.StopUserOnSwitch;
import static android.app.ActivityManager.USER_OP_ERROR_IS_SYSTEM;
import static android.app.ActivityManager.USER_OP_ERROR_RELATED_USERS_CANNOT_STOP;
import static android.app.ActivityManager.USER_OP_IS_CURRENT;
@@ -376,7 +376,7 @@
* user is switched.
*/
@GuardedBy("mLock")
- private @StopBgUsersOnSwitch int mStopBgUsersOnSwitch = STOP_BG_USERS_ON_SWITCH_DEFAULT;
+ private @StopUserOnSwitch int mStopUserOnSwitch = STOP_USER_ON_SWITCH_DEFAULT;
UserController(ActivityManagerService service) {
this(new Injector(service));
@@ -418,29 +418,27 @@
}
}
- void setStopBackgroundUsersOnSwitch(@StopBgUsersOnSwitch int value) {
+ void setStopUserOnSwitch(@StopUserOnSwitch int value) {
if (mInjector.checkCallingPermission(android.Manifest.permission.MANAGE_USERS)
== PackageManager.PERMISSION_DENIED && mInjector.checkCallingPermission(
android.Manifest.permission.INTERACT_ACROSS_USERS_FULL)
== PackageManager.PERMISSION_DENIED) {
throw new SecurityException(
"You either need MANAGE_USERS or INTERACT_ACROSS_USERS_FULL permission to "
- + "call setStopBackgroundUsersOnSwitch()");
+ + "call setStopUserOnSwitch()");
}
synchronized (mLock) {
- Slogf.i(TAG, "setStopBackgroundUsersOnSwitch(): %d -> %d",
- mStopBgUsersOnSwitch, value);
- mStopBgUsersOnSwitch = value;
+ Slogf.i(TAG, "setStopUserOnSwitch(): %d -> %d", mStopUserOnSwitch, value);
+ mStopUserOnSwitch = value;
}
}
- private boolean shouldStopBackgroundUsersOnSwitch() {
+ private boolean shouldStopUserOnSwitch() {
synchronized (mLock) {
- if (mStopBgUsersOnSwitch != STOP_BG_USERS_ON_SWITCH_DEFAULT) {
- final boolean value = mStopBgUsersOnSwitch == STOP_BG_USERS_ON_SWITCH_TRUE;
- Slogf.i(TAG, "isStopBackgroundUsersOnSwitch(): returning overridden value (%b)",
- value);
+ if (mStopUserOnSwitch != STOP_USER_ON_SWITCH_DEFAULT) {
+ final boolean value = mStopUserOnSwitch == STOP_USER_ON_SWITCH_TRUE;
+ Slogf.i(TAG, "shouldStopUserOnSwitch(): returning overridden value (%b)", value);
return value;
}
}
@@ -1846,7 +1844,7 @@
mUserSwitchObservers.finishBroadcast();
}
- private void stopBackgroundUsersOnSwitchIfEnforced(@UserIdInt int oldUserId) {
+ private void stopUserOnSwitchIfEnforced(@UserIdInt int oldUserId) {
// Never stop system user
if (oldUserId == UserHandle.USER_SYSTEM) {
return;
@@ -1854,18 +1852,17 @@
boolean hasRestriction =
hasUserRestriction(UserManager.DISALLOW_RUN_IN_BACKGROUND, oldUserId);
synchronized (mLock) {
- // If running in background is disabled or mStopBackgroundUsersOnSwitch mode,
- // stop the user.
- boolean disallowRunInBg = hasRestriction || shouldStopBackgroundUsersOnSwitch();
+ // If running in background is disabled or mStopUserOnSwitch mode, stop the user.
+ boolean disallowRunInBg = hasRestriction || shouldStopUserOnSwitch();
if (!disallowRunInBg) {
if (DEBUG_MU) {
- Slogf.i(TAG, "stopBackgroundUsersIfEnforced() NOT stopping %d and related "
- + "users", oldUserId);
+ Slogf.i(TAG, "stopUserOnSwitchIfEnforced() NOT stopping %d and related users",
+ oldUserId);
}
return;
}
if (DEBUG_MU) {
- Slogf.i(TAG, "stopBackgroundUsersIfEnforced() stopping %d and related users",
+ Slogf.i(TAG, "stopUserOnSwitchIfEnforced() stopping %d and related users",
oldUserId);
}
stopUsersLU(oldUserId, /* force= */ false, /* allowDelayedLocking= */ true,
@@ -1979,7 +1976,7 @@
mHandler.removeMessages(REPORT_USER_SWITCH_COMPLETE_MSG);
mHandler.sendMessage(mHandler.obtainMessage(REPORT_USER_SWITCH_COMPLETE_MSG, newUserId, 0));
stopGuestOrEphemeralUserIfBackground(oldUserId);
- stopBackgroundUsersOnSwitchIfEnforced(oldUserId);
+ stopUserOnSwitchIfEnforced(oldUserId);
t.traceEnd(); // end continueUserSwitch
}
@@ -2671,9 +2668,8 @@
pw.println(" mTargetUserId:" + mTargetUserId);
pw.println(" mLastActiveUsers:" + mLastActiveUsers);
pw.println(" mDelayUserDataLocking:" + mDelayUserDataLocking);
- pw.println(" shouldStopBackgroundUsersOnSwitch():"
- + shouldStopBackgroundUsersOnSwitch());
- pw.println(" mStopBgUsersOnSwitch:" + mStopBgUsersOnSwitch);
+ pw.println(" shouldStopUserOnSwitch():" + shouldStopUserOnSwitch());
+ pw.println(" mStopUserOnSwitch:" + mStopUserOnSwitch);
pw.println(" mMaxRunningUsers:" + mMaxRunningUsers);
pw.println(" mUserSwitchUiEnabled:" + mUserSwitchUiEnabled);
pw.println(" mInitialized:" + mInitialized);
diff --git a/services/core/java/com/android/server/camera/CameraServiceProxy.java b/services/core/java/com/android/server/camera/CameraServiceProxy.java
index 9d2cff9..5ce72c2 100644
--- a/services/core/java/com/android/server/camera/CameraServiceProxy.java
+++ b/services/core/java/com/android/server/camera/CameraServiceProxy.java
@@ -108,8 +108,9 @@
/**
* When enabled this change id forces the packages it is applied to override the default
- * camera rotate & crop behavior. The default behavior along with all possible override
- * combinations is discussed in the table below.
+ * camera rotate & crop behavior and always return CaptureRequest.SCALER_ROTATE_AND_CROP_NONE .
+ * The default behavior along with all possible override combinations is discussed in the table
+ * below.
*/
@ChangeId
@Overridable
@@ -121,9 +122,7 @@
* When enabled this change id forces the packages it is applied to ignore the current value of
* 'android:resizeableActivity' as well as target SDK equal to or below M and consider the
* activity as non-resizeable. In this case, the value of camera rotate & crop will only depend
- * on potential mismatches between the orientation of the camera and the fixed orientation of
- * the activity. You can check the table below for further details on the possible override
- * combinations.
+ * on the needed compensation considering the current display rotation.
*/
@ChangeId
@Overridable
@@ -132,67 +131,30 @@
public static final long OVERRIDE_CAMERA_RESIZABLE_AND_SDK_CHECK = 191513214L; // buganizer id
/**
- * This change id forces the packages it is applied to override the default camera rotate & crop
- * behavior. Enabling it will set the crop & rotate parameter to
- * {@link android.hardware.camera2.CaptureRequest#SCALER_ROTATE_AND_CROP_90} and disabling it
- * will reset the parameter to
- * {@link android.hardware.camera2.CaptureRequest#SCALER_ROTATE_AND_CROP_NONE} as long as camera
- * clients include {@link android.hardware.camera2.CaptureRequest#SCALER_ROTATE_AND_CROP_AUTO}
- * in their capture requests.
- *
- * This treatment only takes effect if OVERRIDE_CAMERA_ROTATE_AND_CROP_DEFAULTS is also enabled.
- * The table below includes further information about the possible override combinations.
- */
- @ChangeId
- @Overridable
- @Disabled
- @TestApi
- public static final long OVERRIDE_CAMERA_ROTATE_AND_CROP = 190069291L; //buganizer id
-
- /**
* Possible override combinations
*
- * |OVERRIDE | |OVERRIDE_
- * |CAMERA_ |OVERRIDE |CAMERA_
- * |ROTATE_ |CAMERA_ |RESIZEABLE_
- * |AND_CROP_ |ROTATE_ |AND_SDK_
- * |DEFAULTS |AND_CROP |CHECK
- * ______________________________________________
- * Default | | |
- * Behavior | D |D |D
- * ______________________________________________
- * Ignore | | |
- * SDK&Resize | D |D |E
- * ______________________________________________
- * Default | | |
- * Behavior | D |E |D
- * ______________________________________________
- * Ignore | | |
- * SDK&Resize | D |E |E
- * ______________________________________________
- * Rotate&Crop| | |
- * disabled | E |D |D
- * ______________________________________________
- * Rotate&Crop| | |
- * disabled | E |D |E
- * ______________________________________________
- * Rotate&Crop| | |
- * enabled | E |E |D
- * ______________________________________________
- * Rotate&Crop| | |
- * enabled | E |E |E
- * ______________________________________________
+ * |OVERRIDE |OVERRIDE_
+ * |CAMERA_ |CAMERA_
+ * |ROTATE_ |RESIZEABLE_
+ * |AND_CROP_ |AND_SDK_
+ * |DEFAULTS |CHECK
+ * _________________________________________________
+ * Default Behavior | D |D
+ * _________________________________________________
+ * Ignore SDK&Resize | D |E
+ * _________________________________________________
+ * SCALER_ROTATE_AND_CROP_NONE | E |D, E
+ * _________________________________________________
* Where:
- * E -> Override enabled
- * D -> Override disabled
- * Default behavior -> Rotate&crop will be enabled only in cases
- * where the fixed app orientation mismatches
- * with the orientation of the camera.
- * Additionally the app must either target M (or below)
- * or is declared as non-resizeable.
- * Ignore SDK&Resize -> Rotate&crop will be enabled only in cases
- * where the fixed app orientation mismatches
- * with the orientation of the camera.
+ * E -> Override enabled
+ * D -> Override disabled
+ * Default behavior -> Rotate&crop will be calculated depending on the required
+ * compensation necessary for the current display rotation.
+ * Additionally the app must either target M (or below)
+ * or is declared as non-resizeable.
+ * Ignore SDK&Resize -> The Rotate&crop value will depend on the required
+ * compensation for the current display rotation.
+ * SCALER_ROTATE_AND_CROP_NONE -> Always return CaptureRequest.SCALER_ROTATE_AND_CROP_NONE
*/
// Flags arguments to NFC adapter to enable/disable NFC
@@ -543,14 +505,8 @@
if ((taskInfo != null) && (CompatChanges.isChangeEnabled(
OVERRIDE_CAMERA_ROTATE_AND_CROP_DEFAULTS, packageName,
UserHandle.getUserHandleForUid(taskInfo.userId)))) {
- if (CompatChanges.isChangeEnabled(OVERRIDE_CAMERA_ROTATE_AND_CROP, packageName,
- UserHandle.getUserHandleForUid(taskInfo.userId))) {
- Slog.v(TAG, "OVERRIDE_CAMERA_ROTATE_AND_CROP enabled!");
+ Slog.v(TAG, "OVERRIDE_CAMERA_ROTATE_AND_CROP_DEFAULTS enabled!");
return CaptureRequest.SCALER_ROTATE_AND_CROP_NONE;
- } else {
- Slog.v(TAG, "OVERRIDE_CAMERA_ROTATE_AND_CROP disabled!");
- return CaptureRequest.SCALER_ROTATE_AND_CROP_NONE;
- }
}
boolean ignoreResizableAndSdkCheck = false;
if ((taskInfo != null) && (CompatChanges.isChangeEnabled(
diff --git a/services/core/java/com/android/server/compat/overrides/AppCompatOverridesParser.java b/services/core/java/com/android/server/compat/overrides/AppCompatOverridesParser.java
index 11dc1db..e8762a3 100644
--- a/services/core/java/com/android/server/compat/overrides/AppCompatOverridesParser.java
+++ b/services/core/java/com/android/server/compat/overrides/AppCompatOverridesParser.java
@@ -16,19 +16,24 @@
package com.android.server.compat.overrides;
+import static android.content.pm.PackageManager.CERT_INPUT_SHA256;
import static android.content.pm.PackageManager.MATCH_ANY_USER;
import static java.util.Collections.emptyMap;
import static java.util.Collections.emptySet;
+import android.annotation.Nullable;
import android.app.compat.PackageOverride;
import android.content.pm.ApplicationInfo;
import android.content.pm.PackageManager;
import android.util.ArrayMap;
import android.util.ArraySet;
import android.util.KeyValueListParser;
+import android.util.Pair;
import android.util.Slog;
+import libcore.util.HexEncoding;
+
import java.util.Arrays;
import java.util.Comparator;
import java.util.List;
@@ -178,7 +183,9 @@
* overrides, and returns a map from change ID to {@link PackageOverride} instances to add.
*
* <p>Each change override is in the following format:
- * '<change-id>:<min-version-code?>:<max-version-code?>:<enabled>'.
+ * '<signature?>~<change-id>:<min-version-code?>:<max-version-code?>:<enabled>'.
+ *
+ * <p>The signature is optional, and will only be enforced if included.
*
* <p>If there are multiple overrides that should be added with the same change ID, the one
* that best fits the given {@code versionCode} is added.
@@ -187,14 +194,27 @@
*
* <p>If a change override entry in {@code configStr} is invalid, it will be ignored.
*/
- static Map<Long, PackageOverride> parsePackageOverrides(String configStr, long versionCode,
+ Map<Long, PackageOverride> parsePackageOverrides(String configStr, String packageName,
+ long versionCode,
Set<Long> changeIdsToSkip) {
if (configStr.isEmpty()) {
return emptyMap();
}
PackageOverrideComparator comparator = new PackageOverrideComparator(versionCode);
Map<Long, PackageOverride> overridesToAdd = new ArrayMap<>();
- for (String overrideEntryString : configStr.split(",")) {
+
+ Pair<String, String> signatureAndConfig = extractSignatureFromConfig(configStr);
+ if (signatureAndConfig == null) {
+ return emptyMap();
+ }
+ final String signature = signatureAndConfig.first;
+ final String overridesConfig = signatureAndConfig.second;
+
+ if (!verifySignature(packageName, signature)) {
+ return emptyMap();
+ }
+
+ for (String overrideEntryString : overridesConfig.split(",")) {
List<String> changeIdAndVersions = Arrays.asList(overrideEntryString.split(":", 4));
if (changeIdAndVersions.size() != 4) {
Slog.w(TAG, "Invalid change override entry: " + overrideEntryString);
@@ -252,6 +272,51 @@
}
/**
+ * Extracts the signature from the config string if one exists.
+ *
+ * @param configStr String in the form of <signature?>~<overrideConfig>
+ */
+ @Nullable
+ private static Pair<String, String> extractSignatureFromConfig(String configStr) {
+ final List<String> signatureAndConfig = Arrays.asList(configStr.split("~"));
+
+ if (signatureAndConfig.size() == 1) {
+ // The config string doesn't contain a signature.
+ return Pair.create("", configStr);
+ }
+
+ if (signatureAndConfig.size() > 2) {
+ Slog.w(TAG, "Only one signature per config is supported. Config: " + configStr);
+ return null;
+ }
+
+ return Pair.create(signatureAndConfig.get(0), signatureAndConfig.get(1));
+ }
+
+ /**
+ * Verifies that the specified package was signed with a particular signature.
+ *
+ * @param packageName The package to check.
+ * @param signature The optional signature to verify. If empty, we return true.
+ * @return Whether the package is signed with that signature.
+ */
+ private boolean verifySignature(String packageName, String signature) {
+ try {
+ final boolean signatureValid = signature.isEmpty()
+ || mPackageManager.hasSigningCertificate(packageName,
+ HexEncoding.decode(signature), CERT_INPUT_SHA256);
+
+ if (!signatureValid) {
+ Slog.w(TAG, packageName + " did not have expected signature: " + signature);
+ }
+ return signatureValid;
+ } catch (IllegalArgumentException e) {
+ Slog.w(TAG, "Unable to verify signature " + signature + " for " + packageName, e);
+ return false;
+ }
+ }
+
+ /**
* A {@link Comparator} that compares @link PackageOverride} instances with respect to a
* specified {@code versionCode} as follows:
*
diff --git a/services/core/java/com/android/server/compat/overrides/AppCompatOverridesService.java b/services/core/java/com/android/server/compat/overrides/AppCompatOverridesService.java
index 6aed4b0..7e58b6c 100644
--- a/services/core/java/com/android/server/compat/overrides/AppCompatOverridesService.java
+++ b/services/core/java/com/android/server/compat/overrides/AppCompatOverridesService.java
@@ -196,8 +196,8 @@
private void applyPackageOverrides(String configStr, String packageName, long versionCode,
Set<Long> ownedChangeIds, Set<Long> changeIdsToSkip,
boolean removeOtherOwnedOverrides) {
- Map<Long, PackageOverride> overridesToAdd = AppCompatOverridesParser.parsePackageOverrides(
- configStr, versionCode, changeIdsToSkip);
+ Map<Long, PackageOverride> overridesToAdd = mOverridesParser.parsePackageOverrides(
+ configStr, packageName, versionCode, changeIdsToSkip);
putPackageOverrides(packageName, overridesToAdd);
if (!removeOtherOwnedOverrides) {
@@ -426,5 +426,5 @@
break;
}
}
- };
+ }
}
diff --git a/services/core/java/com/android/server/display/DisplayManagerService.java b/services/core/java/com/android/server/display/DisplayManagerService.java
index 8481ad0..d35fc19 100644
--- a/services/core/java/com/android/server/display/DisplayManagerService.java
+++ b/services/core/java/com/android/server/display/DisplayManagerService.java
@@ -3150,7 +3150,7 @@
@Override // Binder call
public void setUserPreferredDisplayMode(Display.Mode mode) {
mContext.enforceCallingOrSelfPermission(
- Manifest.permission.WRITE_SECURE_SETTINGS,
+ Manifest.permission.MODIFY_USER_PREFERRED_DISPLAY_MODE,
"Permission required to set the user preferred display mode.");
final long token = Binder.clearCallingIdentity();
try {
diff --git a/services/core/java/com/android/server/health/HealthHalCallbackHidl.java b/services/core/java/com/android/server/health/HealthHalCallbackHidl.java
new file mode 100644
index 0000000..6b4d7b7
--- /dev/null
+++ b/services/core/java/com/android/server/health/HealthHalCallbackHidl.java
@@ -0,0 +1,115 @@
+/*
+ * 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.health;
+
+import android.annotation.NonNull;
+import android.hardware.health.V2_0.IHealth;
+import android.hardware.health.V2_0.Result;
+import android.hardware.health.V2_1.BatteryCapacityLevel;
+import android.hardware.health.V2_1.Constants;
+import android.hardware.health.V2_1.IHealthInfoCallback;
+import android.os.RemoteException;
+import android.os.Trace;
+import android.util.Slog;
+
+/**
+ * On service registration, {@link HealthServiceWrapperHidl.Callback#onRegistration} is called,
+ * which registers {@code this}, a {@link IHealthInfoCallback}, to the health service.
+ *
+ * <p>When the health service has updates to health info, {@link HealthInfoCallback#update} is
+ * called.
+ *
+ * @hide
+ */
+class HealthHalCallbackHidl extends IHealthInfoCallback.Stub
+ implements HealthServiceWrapperHidl.Callback {
+
+ private static final String TAG = HealthHalCallbackHidl.class.getSimpleName();
+
+ private static void traceBegin(String name) {
+ Trace.traceBegin(Trace.TRACE_TAG_SYSTEM_SERVER, name);
+ }
+
+ private static void traceEnd() {
+ Trace.traceEnd(Trace.TRACE_TAG_SYSTEM_SERVER);
+ }
+
+ private HealthInfoCallback mCallback;
+
+ HealthHalCallbackHidl(@NonNull HealthInfoCallback callback) {
+ mCallback = callback;
+ }
+
+ @Override
+ public void healthInfoChanged(android.hardware.health.V2_0.HealthInfo props) {
+ android.hardware.health.V2_1.HealthInfo propsLatest =
+ new android.hardware.health.V2_1.HealthInfo();
+ propsLatest.legacy = props;
+
+ propsLatest.batteryCapacityLevel = BatteryCapacityLevel.UNSUPPORTED;
+ propsLatest.batteryChargeTimeToFullNowSeconds =
+ Constants.BATTERY_CHARGE_TIME_TO_FULL_NOW_SECONDS_UNSUPPORTED;
+
+ mCallback.update(propsLatest);
+ }
+
+ @Override
+ public void healthInfoChanged_2_1(android.hardware.health.V2_1.HealthInfo props) {
+ mCallback.update(props);
+ }
+
+ // on new service registered
+ @Override
+ public void onRegistration(IHealth oldService, IHealth newService, String instance) {
+ if (newService == null) return;
+
+ traceBegin("HealthUnregisterCallback");
+ try {
+ if (oldService != null) {
+ int r = oldService.unregisterCallback(this);
+ if (r != Result.SUCCESS) {
+ Slog.w(
+ TAG,
+ "health: cannot unregister previous callback: " + Result.toString(r));
+ }
+ }
+ } catch (RemoteException ex) {
+ Slog.w(
+ TAG,
+ "health: cannot unregister previous callback (transaction error): "
+ + ex.getMessage());
+ } finally {
+ traceEnd();
+ }
+
+ traceBegin("HealthRegisterCallback");
+ try {
+ int r = newService.registerCallback(this);
+ if (r != Result.SUCCESS) {
+ Slog.w(TAG, "health: cannot register callback: " + Result.toString(r));
+ return;
+ }
+ // registerCallback does NOT guarantee that update is called
+ // immediately, so request a manual update here.
+ newService.update();
+ } catch (RemoteException ex) {
+ Slog.e(TAG, "health: cannot register callback (transaction error): " + ex.getMessage());
+ } finally {
+ traceEnd();
+ }
+ }
+}
diff --git a/services/core/java/com/android/server/health/HealthInfoCallback.java b/services/core/java/com/android/server/health/HealthInfoCallback.java
new file mode 100644
index 0000000..8136ca0
--- /dev/null
+++ b/services/core/java/com/android/server/health/HealthInfoCallback.java
@@ -0,0 +1,32 @@
+/*
+ * 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.health;
+
+/**
+ * A wrapper over HIDL / AIDL IHealthInfoCallback.
+ *
+ * @hide
+ */
+public interface HealthInfoCallback {
+ /**
+ * Signals to the client that health info is changed.
+ *
+ * @param props the new health info.
+ */
+ // TODO(b/177269435): AIDL
+ void update(android.hardware.health.V2_1.HealthInfo props);
+}
diff --git a/services/core/java/com/android/server/health/HealthServiceWrapper.java b/services/core/java/com/android/server/health/HealthServiceWrapper.java
new file mode 100644
index 0000000..0b43f26
--- /dev/null
+++ b/services/core/java/com/android/server/health/HealthServiceWrapper.java
@@ -0,0 +1,108 @@
+/*
+ * 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.health;
+
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.os.BatteryProperty;
+import android.os.HandlerThread;
+import android.os.IBatteryPropertiesRegistrar;
+import android.os.RemoteException;
+
+import com.android.internal.annotations.VisibleForTesting;
+
+import java.util.NoSuchElementException;
+
+/**
+ * HealthServiceWrapper wraps the internal IHealth service and refreshes the service when necessary.
+ * This is essentially a wrapper over IHealth that is useful for BatteryService.
+ *
+ * <p>The implementation may be backed by a HIDL or AIDL HAL.
+ *
+ * <p>On new registration of IHealth service, the internal service is refreshed. On death of an
+ * existing IHealth service, the internal service is NOT cleared to avoid race condition between
+ * death notification and new service notification. Hence, a caller must check for transaction
+ * errors when calling into the service.
+ *
+ * @hide Should only be used internally.
+ */
+public abstract class HealthServiceWrapper {
+ /** @return the handler thread. Exposed for testing. */
+ @VisibleForTesting
+ abstract HandlerThread getHandlerThread();
+
+ /**
+ * Calls into get*() functions in the health HAL. This reads into the kernel interfaces
+ * directly.
+ *
+ * @see IBatteryPropertiesRegistrar#getProperty
+ */
+ public abstract int getProperty(int id, BatteryProperty prop) throws RemoteException;
+
+ /**
+ * Calls update() in the health HAL.
+ *
+ * @see IBatteryPropertiesRegistrar#scheduleUpdate
+ */
+ public abstract void scheduleUpdate() throws RemoteException;
+
+ /**
+ * Calls into getHealthInfo() in the health HAL. This returns a cached value in the health HAL
+ * implementation.
+ *
+ * @return health info. {@code null} if no health HAL service. {@code null} if any
+ * service-specific error when calling {@code getHealthInfo}, e.g. it is unsupported.
+ * @throws RemoteException for any transaction-level errors
+ */
+ // TODO(b/177269435): AIDL
+ public abstract android.hardware.health.V1_0.HealthInfo getHealthInfo() throws RemoteException;
+
+ /**
+ * Create a new HealthServiceWrapper instance.
+ *
+ * @param healthInfoCallback the callback to call when health info changes
+ * @return the new HealthServiceWrapper instance, which may be backed by HIDL or AIDL service.
+ * @throws RemoteException transaction errors
+ * @throws NoSuchElementException no HIDL or AIDL service is available
+ */
+ public static HealthServiceWrapper create(@Nullable HealthInfoCallback healthInfoCallback)
+ throws RemoteException, NoSuchElementException {
+ return create(
+ healthInfoCallback == null ? null : new HealthHalCallbackHidl(healthInfoCallback),
+ new HealthServiceWrapperHidl.IServiceManagerSupplier() {},
+ new HealthServiceWrapperHidl.IHealthSupplier() {});
+ }
+
+ /**
+ * Create a new HealthServiceWrapper instance for testing.
+ *
+ * @param hidlRegCallback callback for HIDL service registration, or {@code null} if the client
+ * does not care about HIDL service registration notifications
+ * @param hidlServiceManagerSupplier supplier of HIDL service manager
+ * @param hidlHealthSupplier supplier of HIDL health HAL
+ * @return the new HealthServiceWrapper instance, which may be backed by HIDL or AIDL service.
+ */
+ @VisibleForTesting
+ static @NonNull HealthServiceWrapper create(
+ @Nullable HealthServiceWrapperHidl.Callback hidlRegCallback,
+ @NonNull HealthServiceWrapperHidl.IServiceManagerSupplier hidlServiceManagerSupplier,
+ @NonNull HealthServiceWrapperHidl.IHealthSupplier hidlHealthSupplier)
+ throws RemoteException, NoSuchElementException {
+ return new HealthServiceWrapperHidl(
+ hidlRegCallback, hidlServiceManagerSupplier, hidlHealthSupplier);
+ }
+}
diff --git a/services/core/java/com/android/server/health/HealthServiceWrapperHidl.java b/services/core/java/com/android/server/health/HealthServiceWrapperHidl.java
new file mode 100644
index 0000000..3bff2f8
--- /dev/null
+++ b/services/core/java/com/android/server/health/HealthServiceWrapperHidl.java
@@ -0,0 +1,311 @@
+/*
+ * 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.health;
+
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.hardware.health.V1_0.HealthInfo;
+import android.hardware.health.V2_0.IHealth;
+import android.hardware.health.V2_0.Result;
+import android.hidl.manager.V1_0.IServiceManager;
+import android.hidl.manager.V1_0.IServiceNotification;
+import android.os.BatteryManager;
+import android.os.BatteryProperty;
+import android.os.HandlerThread;
+import android.os.RemoteException;
+import android.os.Trace;
+import android.util.MutableInt;
+import android.util.Slog;
+
+import com.android.internal.annotations.VisibleForTesting;
+
+import java.util.NoSuchElementException;
+import java.util.Objects;
+import java.util.concurrent.atomic.AtomicReference;
+
+/**
+ * Implement {@link HealthServiceWrapper} backed by the HIDL HAL.
+ *
+ * @hide
+ */
+final class HealthServiceWrapperHidl extends HealthServiceWrapper {
+ private static final String TAG = "HealthServiceWrapperHidl";
+ public static final String INSTANCE_VENDOR = "default";
+
+ private final IServiceNotification mNotification = new Notification();
+ private final HandlerThread mHandlerThread = new HandlerThread("HealthServiceHwbinder");
+ // These variables are fixed after init.
+ private Callback mCallback;
+ private IHealthSupplier mHealthSupplier;
+ private String mInstanceName;
+
+ // Last IHealth service received.
+ private final AtomicReference<IHealth> mLastService = new AtomicReference<>();
+
+ private static void traceBegin(String name) {
+ Trace.traceBegin(Trace.TRACE_TAG_SYSTEM_SERVER, name);
+ }
+
+ private static void traceEnd() {
+ Trace.traceEnd(Trace.TRACE_TAG_SYSTEM_SERVER);
+ }
+
+ @Override
+ public int getProperty(int id, final BatteryProperty prop) throws RemoteException {
+ traceBegin("HealthGetProperty");
+ try {
+ IHealth service = mLastService.get();
+ if (service == null) throw new RemoteException("no health service");
+ final MutableInt outResult = new MutableInt(Result.NOT_SUPPORTED);
+ switch (id) {
+ case BatteryManager.BATTERY_PROPERTY_CHARGE_COUNTER:
+ service.getChargeCounter(
+ (int result, int value) -> {
+ outResult.value = result;
+ if (result == Result.SUCCESS) prop.setLong(value);
+ });
+ break;
+ case BatteryManager.BATTERY_PROPERTY_CURRENT_NOW:
+ service.getCurrentNow(
+ (int result, int value) -> {
+ outResult.value = result;
+ if (result == Result.SUCCESS) prop.setLong(value);
+ });
+ break;
+ case BatteryManager.BATTERY_PROPERTY_CURRENT_AVERAGE:
+ service.getCurrentAverage(
+ (int result, int value) -> {
+ outResult.value = result;
+ if (result == Result.SUCCESS) prop.setLong(value);
+ });
+ break;
+ case BatteryManager.BATTERY_PROPERTY_CAPACITY:
+ service.getCapacity(
+ (int result, int value) -> {
+ outResult.value = result;
+ if (result == Result.SUCCESS) prop.setLong(value);
+ });
+ break;
+ case BatteryManager.BATTERY_PROPERTY_STATUS:
+ service.getChargeStatus(
+ (int result, int value) -> {
+ outResult.value = result;
+ if (result == Result.SUCCESS) prop.setLong(value);
+ });
+ break;
+ case BatteryManager.BATTERY_PROPERTY_ENERGY_COUNTER:
+ service.getEnergyCounter(
+ (int result, long value) -> {
+ outResult.value = result;
+ if (result == Result.SUCCESS) prop.setLong(value);
+ });
+ break;
+ }
+ return outResult.value;
+ } finally {
+ traceEnd();
+ }
+ }
+
+ @Override
+ public void scheduleUpdate() throws RemoteException {
+ getHandlerThread()
+ .getThreadHandler()
+ .post(
+ () -> {
+ traceBegin("HealthScheduleUpdate");
+ try {
+ IHealth service = mLastService.get();
+ if (service == null) {
+ Slog.e(TAG, "no health service");
+ return;
+ }
+ service.update();
+ } catch (RemoteException ex) {
+ Slog.e(TAG, "Cannot call update on health HAL", ex);
+ } finally {
+ traceEnd();
+ }
+ });
+ }
+
+ private static class Mutable<T> {
+ public T value;
+ }
+
+ @Override
+ public HealthInfo getHealthInfo() throws RemoteException {
+ IHealth service = mLastService.get();
+ if (service == null) return null;
+ final Mutable<HealthInfo> ret = new Mutable<>();
+ service.getHealthInfo(
+ (result, value) -> {
+ if (result == Result.SUCCESS) {
+ ret.value = value.legacy;
+ }
+ });
+ return ret.value;
+ }
+
+ /**
+ * Start monitoring registration of new IHealth services. Only instance {@link #INSTANCE_VENDOR}
+ * and in device / framework manifest are used. This function should only be called once.
+ *
+ * <p>mCallback.onRegistration() is called synchronously (aka in init thread) before this method
+ * returns if callback is not null.
+ *
+ * @throws RemoteException transaction error when talking to IServiceManager
+ * @throws NoSuchElementException if one of the following cases: - No service manager; - {@link
+ * #INSTANCE_VENDOR} is not in manifests (i.e. not available on this device), or none of
+ * these instances are available to current process.
+ * @throws NullPointerException when supplier is null
+ */
+ @VisibleForTesting
+ HealthServiceWrapperHidl(
+ @Nullable Callback callback,
+ @NonNull IServiceManagerSupplier managerSupplier,
+ @NonNull IHealthSupplier healthSupplier)
+ throws RemoteException, NoSuchElementException, NullPointerException {
+ if (managerSupplier == null || healthSupplier == null) {
+ throw new NullPointerException();
+ }
+ mHealthSupplier = healthSupplier;
+
+ // Initialize mLastService and call callback for the first time (in init thread)
+ IHealth newService = null;
+ traceBegin("HealthInitGetService_" + INSTANCE_VENDOR);
+ try {
+ newService = healthSupplier.get(INSTANCE_VENDOR);
+ } catch (NoSuchElementException ex) {
+ /* ignored, handled below */
+ } finally {
+ traceEnd();
+ }
+ if (newService != null) {
+ mInstanceName = INSTANCE_VENDOR;
+ mLastService.set(newService);
+ }
+
+ if (mInstanceName == null || newService == null) {
+ throw new NoSuchElementException(
+ String.format(
+ "IHealth service instance %s isn't available. Perhaps no permission?",
+ INSTANCE_VENDOR));
+ }
+
+ if (callback != null) {
+ mCallback = callback;
+ mCallback.onRegistration(null, newService, mInstanceName);
+ }
+
+ // Register for future service registrations
+ traceBegin("HealthInitRegisterNotification");
+ mHandlerThread.start();
+ try {
+ managerSupplier
+ .get()
+ .registerForNotifications(IHealth.kInterfaceName, mInstanceName, mNotification);
+ } finally {
+ traceEnd();
+ }
+ Slog.i(TAG, "health: HealthServiceWrapper listening to instance " + mInstanceName);
+ }
+
+ @VisibleForTesting
+ public HandlerThread getHandlerThread() {
+ return mHandlerThread;
+ }
+
+ /** Service registration callback. */
+ interface Callback {
+ /**
+ * This function is invoked asynchronously when a new and related IServiceNotification is
+ * received.
+ *
+ * @param service the recently retrieved service from IServiceManager. Can be a dead service
+ * before service notification of a new service is delivered. Implementation must handle
+ * cases for {@link RemoteException}s when calling into service.
+ * @param instance instance name.
+ */
+ void onRegistration(IHealth oldService, IHealth newService, String instance);
+ }
+
+ /**
+ * Supplier of services. Must not return null; throw {@link NoSuchElementException} if a service
+ * is not available.
+ */
+ interface IServiceManagerSupplier {
+ default IServiceManager get() throws NoSuchElementException, RemoteException {
+ return IServiceManager.getService();
+ }
+ }
+
+ /**
+ * Supplier of services. Must not return null; throw {@link NoSuchElementException} if a service
+ * is not available.
+ */
+ interface IHealthSupplier {
+ default IHealth get(String name) throws NoSuchElementException, RemoteException {
+ return IHealth.getService(name, true /* retry */);
+ }
+ }
+
+ private class Notification extends IServiceNotification.Stub {
+ @Override
+ public final void onRegistration(
+ String interfaceName, String instanceName, boolean preexisting) {
+ if (!IHealth.kInterfaceName.equals(interfaceName)) return;
+ if (!mInstanceName.equals(instanceName)) return;
+
+ // This runnable only runs on mHandlerThread and ordering is ensured, hence
+ // no locking is needed inside the runnable.
+ mHandlerThread
+ .getThreadHandler()
+ .post(
+ new Runnable() {
+ @Override
+ public void run() {
+ try {
+ IHealth newService = mHealthSupplier.get(mInstanceName);
+ IHealth oldService = mLastService.getAndSet(newService);
+
+ // preexisting may be inaccurate (race). Check for equality
+ // here.
+ if (Objects.equals(newService, oldService)) return;
+
+ Slog.i(
+ TAG,
+ "health: new instance registered " + mInstanceName);
+ // #init() may be called with null callback. Skip null
+ // callbacks.
+ if (mCallback == null) return;
+ mCallback.onRegistration(
+ oldService, newService, mInstanceName);
+ } catch (NoSuchElementException | RemoteException ex) {
+ Slog.e(
+ TAG,
+ "health: Cannot get instance '"
+ + mInstanceName
+ + "': "
+ + ex.getMessage()
+ + ". Perhaps no permission?");
+ }
+ }
+ });
+ }
+ }
+}
diff --git a/services/core/java/com/android/server/health/Utils.java b/services/core/java/com/android/server/health/Utils.java
new file mode 100644
index 0000000..fc039eb
--- /dev/null
+++ b/services/core/java/com/android/server/health/Utils.java
@@ -0,0 +1,53 @@
+/*
+ * 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.health;
+
+/**
+ * Utils for {@link om.android.server.BatteryService} to deal with health info structs.
+ *
+ * @hide
+ */
+public class Utils {
+ private Utils() {}
+
+ /**
+ * Copy health info struct.
+ *
+ * @param dst destination
+ * @param src source
+ */
+ public static void copy(
+ android.hardware.health.V1_0.HealthInfo dst,
+ android.hardware.health.V1_0.HealthInfo src) {
+ dst.chargerAcOnline = src.chargerAcOnline;
+ dst.chargerUsbOnline = src.chargerUsbOnline;
+ dst.chargerWirelessOnline = src.chargerWirelessOnline;
+ dst.maxChargingCurrent = src.maxChargingCurrent;
+ dst.maxChargingVoltage = src.maxChargingVoltage;
+ dst.batteryStatus = src.batteryStatus;
+ dst.batteryHealth = src.batteryHealth;
+ dst.batteryPresent = src.batteryPresent;
+ dst.batteryLevel = src.batteryLevel;
+ dst.batteryVoltage = src.batteryVoltage;
+ dst.batteryTemperature = src.batteryTemperature;
+ dst.batteryCurrent = src.batteryCurrent;
+ dst.batteryCycleCount = src.batteryCycleCount;
+ dst.batteryFullCharge = src.batteryFullCharge;
+ dst.batteryChargeCounter = src.batteryChargeCounter;
+ dst.batteryTechnology = src.batteryTechnology;
+ }
+}
diff --git a/services/core/java/com/android/server/notification/CountdownConditionProvider.java b/services/core/java/com/android/server/notification/CountdownConditionProvider.java
index d04aac2..471c9b9 100644
--- a/services/core/java/com/android/server/notification/CountdownConditionProvider.java
+++ b/services/core/java/com/android/server/notification/CountdownConditionProvider.java
@@ -32,6 +32,7 @@
import android.util.Slog;
import com.android.server.notification.NotificationManagerService.DumpFilter;
+import com.android.server.pm.PackageManagerService;
import java.io.PrintWriter;
@@ -114,11 +115,7 @@
mIsAlarm = ZenModeConfig.isValidCountdownToAlarmConditionId(conditionId);
final AlarmManager alarms = (AlarmManager)
mContext.getSystemService(Context.ALARM_SERVICE);
- final Intent intent = new Intent(ACTION)
- .putExtra(EXTRA_CONDITION_ID, conditionId)
- .setFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY);
- final PendingIntent pendingIntent = PendingIntent.getBroadcast(mContext, REQUEST_CODE,
- intent, PendingIntent.FLAG_UPDATE_CURRENT | PendingIntent.FLAG_MUTABLE_UNAUDITED);
+ final PendingIntent pendingIntent = getPendingIntent(conditionId);
alarms.cancel(pendingIntent);
if (mTime > 0) {
final long now = System.currentTimeMillis();
@@ -138,6 +135,16 @@
}
}
+ PendingIntent getPendingIntent(Uri conditionId) {
+ final Intent intent = new Intent(ACTION)
+ .setPackage(PackageManagerService.PLATFORM_PACKAGE_NAME)
+ .putExtra(EXTRA_CONDITION_ID, conditionId)
+ .setFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY);
+ final PendingIntent pendingIntent = PendingIntent.getBroadcast(mContext, REQUEST_CODE,
+ intent, PendingIntent.FLAG_UPDATE_CURRENT | PendingIntent.FLAG_IMMUTABLE);
+ return pendingIntent;
+ }
+
@Override
public void onUnsubscribe(Uri conditionId) {
// noop
diff --git a/services/core/java/com/android/server/notification/EventConditionProvider.java b/services/core/java/com/android/server/notification/EventConditionProvider.java
index 25ad9280..4be4f0a 100644
--- a/services/core/java/com/android/server/notification/EventConditionProvider.java
+++ b/services/core/java/com/android/server/notification/EventConditionProvider.java
@@ -41,6 +41,7 @@
import com.android.server.notification.CalendarTracker.CheckEventResult;
import com.android.server.notification.NotificationManagerService.DumpFilter;
+import com.android.server.pm.PackageManagerService;
import java.io.PrintWriter;
import java.util.ArrayList;
@@ -266,12 +267,7 @@
private void rescheduleAlarm(long now, long time) {
mNextAlarmTime = time;
final AlarmManager alarms = (AlarmManager) mContext.getSystemService(Context.ALARM_SERVICE);
- final PendingIntent pendingIntent = PendingIntent.getBroadcast(mContext,
- REQUEST_CODE_EVALUATE,
- new Intent(ACTION_EVALUATE)
- .addFlags(Intent.FLAG_RECEIVER_FOREGROUND)
- .putExtra(EXTRA_TIME, time),
- PendingIntent.FLAG_UPDATE_CURRENT | PendingIntent.FLAG_IMMUTABLE);
+ final PendingIntent pendingIntent = getPendingIntent(time);
alarms.cancel(pendingIntent);
if (time == 0 || time < now) {
if (DEBUG) Slog.d(TAG, "Not scheduling evaluate: " + (time == 0 ? "no time specified"
@@ -283,6 +279,17 @@
alarms.setExact(AlarmManager.RTC_WAKEUP, time, pendingIntent);
}
+ PendingIntent getPendingIntent(long time) {
+ final PendingIntent pendingIntent = PendingIntent.getBroadcast(mContext,
+ REQUEST_CODE_EVALUATE,
+ new Intent(ACTION_EVALUATE)
+ .addFlags(Intent.FLAG_RECEIVER_FOREGROUND)
+ .setPackage(PackageManagerService.PLATFORM_PACKAGE_NAME)
+ .putExtra(EXTRA_TIME, time),
+ PendingIntent.FLAG_UPDATE_CURRENT | PendingIntent.FLAG_IMMUTABLE);
+ return pendingIntent;
+ }
+
private Condition createCondition(Uri id, int state) {
final String summary = NOT_SHOWN;
final String line1 = NOT_SHOWN;
diff --git a/services/core/java/com/android/server/pm/DeletePackageHelper.java b/services/core/java/com/android/server/pm/DeletePackageHelper.java
index d8107fc..b792a17 100644
--- a/services/core/java/com/android/server/pm/DeletePackageHelper.java
+++ b/services/core/java/com/android/server/pm/DeletePackageHelper.java
@@ -83,19 +83,15 @@
private final UserManagerInternal mUserManagerInternal;
private final PermissionManagerServiceInternal mPermissionManager;
private final RemovePackageHelper mRemovePackageHelper;
- // TODO(b/201815903): remove dependency to InitAndSystemPackageHelper
- private final InitAndSystemPackageHelper mInitAndSystemPackageHelper;
private final AppDataHelper mAppDataHelper;
// TODO(b/198166813): remove PMS dependency
DeletePackageHelper(PackageManagerService pm, RemovePackageHelper removePackageHelper,
- InitAndSystemPackageHelper initAndSystemPackageHelper,
AppDataHelper appDataHelper) {
mPm = pm;
mUserManagerInternal = mPm.mInjector.getUserManagerInternal();
mPermissionManager = mPm.mInjector.getPermissionManagerServiceInternal();
mRemovePackageHelper = removePackageHelper;
- mInitAndSystemPackageHelper = initAndSystemPackageHelper;
mAppDataHelper = appDataHelper;
}
@@ -105,7 +101,6 @@
mUserManagerInternal = mPm.mInjector.getUserManagerInternal();
mPermissionManager = mPm.mInjector.getPermissionManagerServiceInternal();
mRemovePackageHelper = new RemovePackageHelper(mPm, mAppDataHelper);
- mInitAndSystemPackageHelper = mPm.getInitAndSystemPackageHelper();
}
/**
@@ -282,7 +277,7 @@
Slog.i(TAG, "Enabling system stub after removal; pkg: "
+ stubPkg.getPackageName());
}
- mInitAndSystemPackageHelper.enableCompressedPackage(stubPkg, stubPs);
+ new InstallPackageHelper(mPm).enableCompressedPackage(stubPkg, stubPs);
} else if (DEBUG_COMPRESSION) {
Slog.i(TAG, "System stub disabled for all users, leaving uncompressed "
+ "after removal; pkg: " + stubPkg.getPackageName());
@@ -422,7 +417,7 @@
// as well and fall back to existing code in system partition
PackageSetting disabledPs = deleteInstalledSystemPackage(action, ps, allUserHandles,
flags, outInfo, writeSettings);
- mInitAndSystemPackageHelper.restoreDisabledSystemPackageLIF(
+ new InstallPackageHelper(mPm).restoreDisabledSystemPackageLIF(
action, ps, allUserHandles, outInfo, writeSettings, disabledPs);
} else {
if (DEBUG_REMOVE) Slog.d(TAG, "Removing non-system package: " + ps.getPackageName());
diff --git a/services/core/java/com/android/server/pm/DomainVerificationConnection.java b/services/core/java/com/android/server/pm/DomainVerificationConnection.java
index 0ee0989..975ab4c1 100644
--- a/services/core/java/com/android/server/pm/DomainVerificationConnection.java
+++ b/services/core/java/com/android/server/pm/DomainVerificationConnection.java
@@ -42,14 +42,12 @@
final PackageManagerService mPm;
final PackageManagerInternal mPmInternal;
final UserManagerInternal mUmInternal;
- final VerificationHelper mVerificationHelper;
// TODO(b/198166813): remove PMS dependency
DomainVerificationConnection(PackageManagerService pm) {
mPm = pm;
mPmInternal = mPm.mInjector.getLocalService(PackageManagerInternal.class);
mUmInternal = mPm.mInjector.getLocalService(UserManagerInternal.class);
- mVerificationHelper = new VerificationHelper(mPm.mContext);
}
@Override
@@ -80,7 +78,7 @@
@Override
public long getPowerSaveTempWhitelistAppDuration() {
- return mVerificationHelper.getVerificationTimeout();
+ return VerificationUtils.getVerificationTimeout(mPm.mContext);
}
@Override
diff --git a/services/core/java/com/android/server/pm/HandlerParams.java b/services/core/java/com/android/server/pm/HandlerParams.java
index 034688b..57a8a7a 100644
--- a/services/core/java/com/android/server/pm/HandlerParams.java
+++ b/services/core/java/com/android/server/pm/HandlerParams.java
@@ -16,22 +16,12 @@
package com.android.server.pm;
-import static android.content.pm.PackageManager.INSTALL_FAILED_VERSION_DOWNGRADE;
-
-import android.annotation.NonNull;
-import android.content.pm.ApplicationInfo;
-import android.content.pm.PackageInfo;
-import android.content.pm.PackageInfoLite;
-import android.content.pm.PackageManager;
-import android.os.UserHandle;
-import android.util.Pair;
-import android.util.Slog;
-
import static com.android.server.pm.PackageManagerService.DEBUG_INSTALL;
import static com.android.server.pm.PackageManagerService.TAG;
-import com.android.internal.util.ArrayUtils;
-import com.android.server.pm.parsing.pkg.AndroidPackage;
+import android.annotation.NonNull;
+import android.os.UserHandle;
+import android.util.Slog;
abstract class HandlerParams {
/** User handle for the user requesting the information or installation. */
@@ -40,15 +30,13 @@
int mTraceCookie;
@NonNull
final PackageManagerService mPm;
- final VerificationHelper mVerificationHelper;
- final BroadcastHelper mBroadcastHelper;
+ final InstallPackageHelper mInstallPackageHelper;
// TODO(b/198166813): remove PMS dependency
HandlerParams(UserHandle user, PackageManagerService pm) {
mUser = user;
mPm = pm;
- mVerificationHelper = new VerificationHelper(mPm.mContext);
- mBroadcastHelper = new BroadcastHelper(mPm.mInjector);
+ mInstallPackageHelper = new InstallPackageHelper(mPm);
}
UserHandle getUser() {
@@ -73,135 +61,4 @@
abstract void handleStartCopy();
abstract void handleReturnCode();
-
- Pair<Integer, String> verifyReplacingVersionCode(PackageInfoLite pkgLite,
- long requiredInstalledVersionCode, int installFlags) {
- if ((installFlags & PackageManager.INSTALL_APEX) != 0) {
- return verifyReplacingVersionCodeForApex(
- pkgLite, requiredInstalledVersionCode, installFlags);
- }
-
- String packageName = pkgLite.packageName;
- synchronized (mPm.mLock) {
- // Package which currently owns the data that the new package will own if installed.
- // If an app is uninstalled while keeping data (e.g. adb uninstall -k), installedPkg
- // will be null whereas dataOwnerPkg will contain information about the package
- // which was uninstalled while keeping its data.
- AndroidPackage dataOwnerPkg = mPm.mPackages.get(packageName);
- if (dataOwnerPkg == null) {
- PackageSetting ps = mPm.mSettings.getPackageLPr(packageName);
- if (ps != null) {
- dataOwnerPkg = ps.getPkg();
- }
- }
-
- if (requiredInstalledVersionCode != PackageManager.VERSION_CODE_HIGHEST) {
- if (dataOwnerPkg == null) {
- String errorMsg = "Required installed version code was "
- + requiredInstalledVersionCode
- + " but package is not installed";
- Slog.w(TAG, errorMsg);
- return Pair.create(
- PackageManager.INSTALL_FAILED_WRONG_INSTALLED_VERSION, errorMsg);
- }
-
- if (dataOwnerPkg.getLongVersionCode() != requiredInstalledVersionCode) {
- String errorMsg = "Required installed version code was "
- + requiredInstalledVersionCode
- + " but actual installed version is "
- + dataOwnerPkg.getLongVersionCode();
- Slog.w(TAG, errorMsg);
- return Pair.create(
- PackageManager.INSTALL_FAILED_WRONG_INSTALLED_VERSION, errorMsg);
- }
- }
-
- if (dataOwnerPkg != null) {
- if (!PackageManagerServiceUtils.isDowngradePermitted(installFlags,
- dataOwnerPkg.isDebuggable())) {
- try {
- checkDowngrade(dataOwnerPkg, pkgLite);
- } catch (PackageManagerException e) {
- String errorMsg = "Downgrade detected: " + e.getMessage();
- Slog.w(TAG, errorMsg);
- return Pair.create(
- PackageManager.INSTALL_FAILED_VERSION_DOWNGRADE, errorMsg);
- }
- }
- }
- }
- return Pair.create(PackageManager.INSTALL_SUCCEEDED, null);
- }
-
- private Pair<Integer, String> verifyReplacingVersionCodeForApex(PackageInfoLite pkgLite,
- long requiredInstalledVersionCode, int installFlags) {
- String packageName = pkgLite.packageName;
-
- final PackageInfo activePackage = mPm.mApexManager.getPackageInfo(packageName,
- ApexManager.MATCH_ACTIVE_PACKAGE);
- if (activePackage == null) {
- String errorMsg = "Attempting to install new APEX package " + packageName;
- Slog.w(TAG, errorMsg);
- return Pair.create(PackageManager.INSTALL_FAILED_PACKAGE_CHANGED, errorMsg);
- }
-
- final long activeVersion = activePackage.getLongVersionCode();
- if (requiredInstalledVersionCode != PackageManager.VERSION_CODE_HIGHEST
- && activeVersion != requiredInstalledVersionCode) {
- String errorMsg = "Installed version of APEX package " + packageName
- + " does not match required. Active version: " + activeVersion
- + " required: " + requiredInstalledVersionCode;
- Slog.w(TAG, errorMsg);
- return Pair.create(PackageManager.INSTALL_FAILED_WRONG_INSTALLED_VERSION, errorMsg);
- }
-
- final boolean isAppDebuggable = (activePackage.applicationInfo.flags
- & ApplicationInfo.FLAG_DEBUGGABLE) != 0;
- final long newVersionCode = pkgLite.getLongVersionCode();
- if (!PackageManagerServiceUtils.isDowngradePermitted(installFlags, isAppDebuggable)
- && newVersionCode < activeVersion) {
- String errorMsg = "Downgrade of APEX package " + packageName
- + " is not allowed. Active version: " + activeVersion
- + " attempted: " + newVersionCode;
- Slog.w(TAG, errorMsg);
- return Pair.create(PackageManager.INSTALL_FAILED_VERSION_DOWNGRADE, errorMsg);
- }
-
- return Pair.create(PackageManager.INSTALL_SUCCEEDED, null);
- }
-
- /**
- * Check and throw if the given before/after packages would be considered a
- * downgrade.
- */
- private static void checkDowngrade(AndroidPackage before, PackageInfoLite after)
- throws PackageManagerException {
- if (after.getLongVersionCode() < before.getLongVersionCode()) {
- throw new PackageManagerException(INSTALL_FAILED_VERSION_DOWNGRADE,
- "Update version code " + after.versionCode + " is older than current "
- + before.getLongVersionCode());
- } else if (after.getLongVersionCode() == before.getLongVersionCode()) {
- if (after.baseRevisionCode < before.getBaseRevisionCode()) {
- throw new PackageManagerException(INSTALL_FAILED_VERSION_DOWNGRADE,
- "Update base revision code " + after.baseRevisionCode
- + " is older than current " + before.getBaseRevisionCode());
- }
-
- if (!ArrayUtils.isEmpty(after.splitNames)) {
- for (int i = 0; i < after.splitNames.length; i++) {
- final String splitName = after.splitNames[i];
- final int j = ArrayUtils.indexOf(before.getSplitNames(), splitName);
- if (j != -1) {
- if (after.splitRevisionCodes[i] < before.getSplitRevisionCodes()[j]) {
- throw new PackageManagerException(INSTALL_FAILED_VERSION_DOWNGRADE,
- "Update split " + splitName + " revision code "
- + after.splitRevisionCodes[i]
- + " is older than current "
- + before.getSplitRevisionCodes()[j]);
- }
- }
- }
- }
- }
- }
}
diff --git a/services/core/java/com/android/server/pm/InitAndSystemPackageHelper.java b/services/core/java/com/android/server/pm/InitAndSystemPackageHelper.java
index 5ff0a6f..239a9d71 100644
--- a/services/core/java/com/android/server/pm/InitAndSystemPackageHelper.java
+++ b/services/core/java/com/android/server/pm/InitAndSystemPackageHelper.java
@@ -16,22 +16,13 @@
package com.android.server.pm;
-import static android.content.pm.PackageManager.COMPONENT_ENABLED_STATE_DISABLED;
import static android.content.pm.PackageManager.INSTALL_SUCCEEDED;
-import static android.content.pm.PackageManager.UNINSTALL_REASON_UNKNOWN;
import static android.content.pm.parsing.ApkLiteParseUtils.isApkFile;
import static android.os.Trace.TRACE_TAG_PACKAGE_MANAGER;
-import static android.os.storage.StorageManager.FLAG_STORAGE_CE;
-import static android.os.storage.StorageManager.FLAG_STORAGE_DE;
-import static android.os.storage.StorageManager.FLAG_STORAGE_EXTERNAL;
-import static com.android.internal.content.NativeLibraryHelper.LIB_DIR_NAME;
import static com.android.internal.util.FrameworkStatsLog.BOOT_TIME_EVENT_DURATION__EVENT__OTA_PACKAGE_MANAGER_DATA_APP_AVG_SCAN_TIME;
import static com.android.internal.util.FrameworkStatsLog.BOOT_TIME_EVENT_DURATION__EVENT__OTA_PACKAGE_MANAGER_SYSTEM_APP_AVG_SCAN_TIME;
-import static com.android.server.pm.PackageManagerService.COMPRESSED_EXTENSION;
-import static com.android.server.pm.PackageManagerService.DEBUG_COMPRESSION;
import static com.android.server.pm.PackageManagerService.DEBUG_PACKAGE_SCANNING;
-import static com.android.server.pm.PackageManagerService.DEBUG_REMOVE;
import static com.android.server.pm.PackageManagerService.SCAN_AS_APK_IN_APEX;
import static com.android.server.pm.PackageManagerService.SCAN_AS_PRIVILEGED;
import static com.android.server.pm.PackageManagerService.SCAN_AS_SYSTEM;
@@ -42,31 +33,21 @@
import static com.android.server.pm.PackageManagerService.SCAN_REQUIRE_KNOWN;
import static com.android.server.pm.PackageManagerService.SYSTEM_PARTITIONS;
import static com.android.server.pm.PackageManagerService.TAG;
-import static com.android.server.pm.PackageManagerServiceUtils.decompressFile;
-import static com.android.server.pm.PackageManagerServiceUtils.getCompressedFiles;
import static com.android.server.pm.PackageManagerServiceUtils.logCriticalInfo;
-import static com.android.server.pm.PackageManagerServiceUtils.makeDirRecursive;
-import android.annotation.NonNull;
import android.annotation.Nullable;
-import android.content.ContentResolver;
import android.content.pm.ApplicationInfo;
import android.content.pm.PackageManager;
import android.content.pm.parsing.ParsingPackageUtils;
import android.os.Environment;
-import android.os.Process;
import android.os.SystemClock;
import android.os.Trace;
-import android.os.UserHandle;
-import android.system.ErrnoException;
import android.util.ArrayMap;
import android.util.EventLog;
import android.util.Log;
import android.util.Slog;
import com.android.internal.annotations.GuardedBy;
-import com.android.internal.content.F2fsUtils;
-import com.android.internal.content.NativeLibraryHelper;
import com.android.internal.content.om.OverlayConfig;
import com.android.internal.util.ArrayUtils;
import com.android.internal.util.FrameworkStatsLog;
@@ -74,15 +55,10 @@
import com.android.server.pm.parsing.PackageCacher;
import com.android.server.pm.parsing.PackageParser2;
import com.android.server.pm.parsing.pkg.AndroidPackage;
-import com.android.server.pm.permission.PermissionManagerServiceInternal;
import com.android.server.utils.WatchedArrayMap;
-import libcore.io.IoUtils;
-
import java.io.File;
-import java.io.IOException;
import java.util.ArrayList;
-import java.util.Collections;
import java.util.List;
import java.util.concurrent.ExecutorService;
@@ -270,6 +246,8 @@
* If the package is scanned, it's not erased.
*/
final AndroidPackage scannedPkg = mPm.mPackages.get(ps.getPackageName());
+ final PackageSetting disabledPs =
+ mPm.mSettings.getDisabledSystemPkgLPr(ps.getPackageName());
if (scannedPkg != null) {
/*
* If the system app is both scanned and in the
@@ -278,7 +256,7 @@
* scanned package so the previously user-installed
* application can be scanned.
*/
- if (mPm.mSettings.isDisabledSystemPackageLPr(ps.getPackageName())) {
+ if (disabledPs != null) {
logCriticalInfo(Log.WARN,
"Expecting better updated system app for "
+ ps.getPackageName()
@@ -294,7 +272,7 @@
continue;
}
- if (!mPm.mSettings.isDisabledSystemPackageLPr(ps.getPackageName())) {
+ if (disabledPs == null) {
logCriticalInfo(Log.WARN, "System package " + ps.getPackageName()
+ " no longer exists; its data will be wiped");
mRemovePackageHelper.removePackageDataLIF(ps, userIds, null, 0, false);
@@ -303,8 +281,6 @@
// been removed. check the code path still exists and check there's
// still a package. the latter can happen if an OTA keeps the same
// code path, but, changes the package name.
- final PackageSetting disabledPs =
- mPm.mSettings.getDisabledSystemPkgLPr(ps.getPackageName());
if (disabledPs.getPath() == null || !disabledPs.getPath().exists()
|| disabledPs.getPkg() == null) {
possiblyDeletedUpdatedSystemApps.add(ps.getPackageName());
@@ -454,7 +430,7 @@
// Uncompress and install any stubbed system applications.
// This must be done last to ensure all stubs are replaced or disabled.
- installSystemStubPackages(stubSystemApps, mScanFlags);
+ new InstallPackageHelper(mPm).installSystemStubPackages(stubSystemApps, mScanFlags);
final int cachedNonSystemApps = PackageCacher.sCachedPackageReadCount.get()
- cachedSystemApps;
@@ -568,403 +544,11 @@
}
}
- /**
- * Uncompress and install stub applications.
- * <p>In order to save space on the system partition, some applications are shipped in a
- * compressed form. In addition the compressed bits for the full application, the
- * system image contains a tiny stub comprised of only the Android manifest.
- * <p>During the first boot, attempt to uncompress and install the full application. If
- * the application can't be installed for any reason, disable the stub and prevent
- * uncompressing the full application during future boots.
- * <p>In order to forcefully attempt an installation of a full application, go to app
- * settings and enable the application.
- */
- @GuardedBy({"mPm.mLock", "mPm.mInstallLock"})
- private void installSystemStubPackages(@NonNull List<String> systemStubPackageNames,
- @PackageManagerService.ScanFlags int scanFlags) {
- for (int i = systemStubPackageNames.size() - 1; i >= 0; --i) {
- final String packageName = systemStubPackageNames.get(i);
- // skip if the system package is already disabled
- if (mPm.mSettings.isDisabledSystemPackageLPr(packageName)) {
- systemStubPackageNames.remove(i);
- continue;
- }
- // skip if the package isn't installed (?!); this should never happen
- final AndroidPackage pkg = mPm.mPackages.get(packageName);
- if (pkg == null) {
- systemStubPackageNames.remove(i);
- continue;
- }
- // skip if the package has been disabled by the user
- final PackageSetting ps = mPm.mSettings.getPackageLPr(packageName);
- if (ps != null) {
- final int enabledState = ps.getEnabled(UserHandle.USER_SYSTEM);
- if (enabledState == PackageManager.COMPONENT_ENABLED_STATE_DISABLED_USER) {
- systemStubPackageNames.remove(i);
- continue;
- }
- }
-
- // install the package to replace the stub on /system
- try {
- installStubPackageLI(pkg, 0, scanFlags);
- ps.setEnabled(PackageManager.COMPONENT_ENABLED_STATE_DEFAULT,
- UserHandle.USER_SYSTEM, "android");
- systemStubPackageNames.remove(i);
- } catch (PackageManagerException e) {
- Slog.e(TAG, "Failed to parse uncompressed system package: " + e.getMessage());
- }
-
- // any failed attempt to install the package will be cleaned up later
- }
-
- // disable any stub still left; these failed to install the full application
- for (int i = systemStubPackageNames.size() - 1; i >= 0; --i) {
- final String pkgName = systemStubPackageNames.get(i);
- final PackageSetting ps = mPm.mSettings.getPackageLPr(pkgName);
- ps.setEnabled(PackageManager.COMPONENT_ENABLED_STATE_DISABLED,
- UserHandle.USER_SYSTEM, "android");
- logCriticalInfo(Log.ERROR, "Stub disabled; pkg: " + pkgName);
- }
- }
-
- /**
- * Extract, install and enable a stub package.
- * <p>If the compressed file can not be extracted / installed for any reason, the stub
- * APK will be installed and the package will be disabled. To recover from this situation,
- * the user will need to go into system settings and re-enable the package.
- */
- @GuardedBy({"mPm.mLock", "mPm.mInstallLock"})
- public boolean enableCompressedPackage(AndroidPackage stubPkg,
- @NonNull PackageSetting stubPkgSetting) {
- final int parseFlags = mPm.getDefParseFlags() | ParsingPackageUtils.PARSE_CHATTY
- | ParsingPackageUtils.PARSE_ENFORCE_CODE;
- synchronized (mPm.mInstallLock) {
- final AndroidPackage pkg;
- try (PackageFreezer freezer =
- mPm.freezePackage(stubPkg.getPackageName(), "setEnabledSetting")) {
- pkg = installStubPackageLI(stubPkg, parseFlags, 0 /*scanFlags*/);
- synchronized (mPm.mLock) {
- mAppDataHelper.prepareAppDataAfterInstallLIF(pkg);
- try {
- mPm.updateSharedLibrariesLocked(pkg, stubPkgSetting, null, null,
- Collections.unmodifiableMap(mPm.mPackages));
- } catch (PackageManagerException e) {
- Slog.w(TAG, "updateAllSharedLibrariesLPw failed: ", e);
- }
- mPm.mPermissionManager.onPackageInstalled(pkg,
- Process.INVALID_UID /* previousAppId */,
- PermissionManagerServiceInternal.PackageInstalledParams.DEFAULT,
- UserHandle.USER_ALL);
- mPm.writeSettingsLPrTEMP();
- }
- } catch (PackageManagerException e) {
- // Whoops! Something went very wrong; roll back to the stub and disable the package
- try (PackageFreezer freezer =
- mPm.freezePackage(stubPkg.getPackageName(), "setEnabledSetting")) {
- synchronized (mPm.mLock) {
- // NOTE: Ensure the system package is enabled; even for a compressed stub.
- // If we don't, installing the system package fails during scan
- enableSystemPackageLPw(stubPkg);
- }
- installPackageFromSystemLIF(stubPkg.getPath(),
- mPm.mUserManager.getUserIds() /*allUserHandles*/,
- null /*origUserHandles*/,
- true /*writeSettings*/);
- } catch (PackageManagerException pme) {
- // Serious WTF; we have to be able to install the stub
- Slog.wtf(TAG, "Failed to restore system package:" + stubPkg.getPackageName(),
- pme);
- } finally {
- // Disable the package; the stub by itself is not runnable
- synchronized (mPm.mLock) {
- final PackageSetting stubPs = mPm.mSettings.getPackageLPr(
- stubPkg.getPackageName());
- if (stubPs != null) {
- stubPs.setEnabled(COMPONENT_ENABLED_STATE_DISABLED,
- UserHandle.USER_SYSTEM, "android");
- }
- mPm.writeSettingsLPrTEMP();
- }
- }
- return false;
- }
- mAppDataHelper.clearAppDataLIF(pkg, UserHandle.USER_ALL,
- FLAG_STORAGE_DE | FLAG_STORAGE_CE | FLAG_STORAGE_EXTERNAL
- | Installer.FLAG_CLEAR_CODE_CACHE_ONLY);
- mPm.getDexManager().notifyPackageUpdated(pkg.getPackageName(),
- pkg.getBaseApkPath(), pkg.getSplitCodePaths());
- }
- return true;
- }
-
- @GuardedBy({"mPm.mLock", "mPm.mInstallLock"})
- private AndroidPackage installStubPackageLI(AndroidPackage stubPkg,
- @ParsingPackageUtils.ParseFlags int parseFlags,
- @PackageManagerService.ScanFlags int scanFlags)
- throws PackageManagerException {
- if (DEBUG_COMPRESSION) {
- Slog.i(TAG, "Uncompressing system stub; pkg: " + stubPkg.getPackageName());
- }
- // uncompress the binary to its eventual destination on /data
- final File scanFile = decompressPackage(stubPkg.getPackageName(), stubPkg.getPath());
- if (scanFile == null) {
- throw new PackageManagerException(
- "Unable to decompress stub at " + stubPkg.getPath());
- }
- synchronized (mPm.mLock) {
- mPm.mSettings.disableSystemPackageLPw(stubPkg.getPackageName(), true /*replaced*/);
- }
- mRemovePackageHelper.removePackageLI(stubPkg, true /*chatty*/);
- final ScanPackageHelper scanPackageHelper = new ScanPackageHelper(mPm);
- try {
- return scanPackageHelper.scanPackageTracedLI(scanFile, parseFlags, scanFlags, 0, null);
- } catch (PackageManagerException e) {
- Slog.w(TAG, "Failed to install compressed system package:" + stubPkg.getPackageName(),
- e);
- // Remove the failed install
- mRemovePackageHelper.removeCodePathLI(scanFile);
- throw e;
- }
- }
-
- /**
- * Decompresses the given package on the system image onto
- * the /data partition.
- * @return The directory the package was decompressed into. Otherwise, {@code null}.
- */
- @GuardedBy("mPm.mInstallLock")
- private File decompressPackage(String packageName, String codePath) {
- final File[] compressedFiles = getCompressedFiles(codePath);
- if (compressedFiles == null || compressedFiles.length == 0) {
- if (DEBUG_COMPRESSION) {
- Slog.i(TAG, "No files to decompress: " + codePath);
- }
- return null;
- }
- final File dstCodePath =
- PackageManagerServiceUtils.getNextCodePath(Environment.getDataAppDirectory(null),
- packageName);
- int ret = PackageManager.INSTALL_SUCCEEDED;
- try {
- makeDirRecursive(dstCodePath, 0755);
- for (File srcFile : compressedFiles) {
- final String srcFileName = srcFile.getName();
- final String dstFileName = srcFileName.substring(
- 0, srcFileName.length() - COMPRESSED_EXTENSION.length());
- final File dstFile = new File(dstCodePath, dstFileName);
- ret = decompressFile(srcFile, dstFile);
- if (ret != PackageManager.INSTALL_SUCCEEDED) {
- logCriticalInfo(Log.ERROR, "Failed to decompress"
- + "; pkg: " + packageName
- + ", file: " + dstFileName);
- break;
- }
- }
- } catch (ErrnoException e) {
- logCriticalInfo(Log.ERROR, "Failed to decompress"
- + "; pkg: " + packageName
- + ", err: " + e.errno);
- }
- if (ret == PackageManager.INSTALL_SUCCEEDED) {
- final File libraryRoot = new File(dstCodePath, LIB_DIR_NAME);
- NativeLibraryHelper.Handle handle = null;
- try {
- handle = NativeLibraryHelper.Handle.create(dstCodePath);
- ret = NativeLibraryHelper.copyNativeBinariesWithOverride(handle, libraryRoot,
- null /*abiOverride*/, false /*isIncremental*/);
- } catch (IOException e) {
- logCriticalInfo(Log.ERROR, "Failed to extract native libraries"
- + "; pkg: " + packageName);
- ret = PackageManager.INSTALL_FAILED_INTERNAL_ERROR;
- } finally {
- IoUtils.closeQuietly(handle);
- }
- }
- if (ret == PackageManager.INSTALL_SUCCEEDED) {
- // NOTE: During boot, we have to delay releasing cblocks for no other reason than
- // we cannot retrieve the setting {@link Secure#RELEASE_COMPRESS_BLOCKS_ON_INSTALL}.
- // When we no longer need to read that setting, cblock release can occur always
- // occur here directly
- if (!mPm.isSystemReady()) {
- if (mPm.mReleaseOnSystemReady == null) {
- mPm.mReleaseOnSystemReady = new ArrayList<>();
- }
- mPm.mReleaseOnSystemReady.add(dstCodePath);
- } else {
- final ContentResolver resolver = mPm.mContext.getContentResolver();
- F2fsUtils.releaseCompressedBlocks(resolver, dstCodePath);
- }
- }
- if (ret != PackageManager.INSTALL_SUCCEEDED) {
- if (!dstCodePath.exists()) {
- return null;
- }
- mRemovePackageHelper.removeCodePathLI(dstCodePath);
- return null;
- }
-
- return dstCodePath;
- }
-
-
- @GuardedBy("mPm.mLock")
- private void enableSystemPackageLPw(AndroidPackage pkg) {
- mPm.mSettings.enableSystemPackageLPw(pkg.getPackageName());
- }
-
- /**
- * Tries to restore the disabled system package after an update has been deleted.
- */
- @GuardedBy({"mPm.mLock", "mPm.mInstallLock"})
- public void restoreDisabledSystemPackageLIF(DeletePackageAction action,
- PackageSetting deletedPs, @NonNull int[] allUserHandles,
- @Nullable PackageRemovedInfo outInfo,
- boolean writeSettings,
- PackageSetting disabledPs)
- throws SystemDeleteException {
- // writer
- synchronized (mPm.mLock) {
- // NOTE: The system package always needs to be enabled; even if it's for
- // a compressed stub. If we don't, installing the system package fails
- // during scan [scanning checks the disabled packages]. We will reverse
- // this later, after we've "installed" the stub.
- // Reinstate the old system package
- enableSystemPackageLPw(disabledPs.getPkg());
- // Remove any native libraries from the upgraded package.
- removeNativeBinariesLI(deletedPs);
- }
-
- // Install the system package
- if (DEBUG_REMOVE) Slog.d(TAG, "Re-installing system package: " + disabledPs);
- try {
- installPackageFromSystemLIF(disabledPs.getPathString(), allUserHandles,
- outInfo == null ? null : outInfo.mOrigUsers, writeSettings);
- } catch (PackageManagerException e) {
- Slog.w(TAG, "Failed to restore system package:" + deletedPs.getPackageName() + ": "
- + e.getMessage());
- // TODO(b/194319951): can we avoid this; throw would come from scan...
- throw new SystemDeleteException(e);
- } finally {
- if (disabledPs.getPkg().isStub()) {
- // We've re-installed the stub; make sure it's disabled here. If package was
- // originally enabled, we'll install the compressed version of the application
- // and re-enable it afterward.
- final PackageSetting stubPs = mPm.mSettings.getPackageLPr(
- deletedPs.getPackageName());
- if (stubPs != null) {
- int userId = action.mUser == null
- ? UserHandle.USER_ALL : action.mUser.getIdentifier();
- if (userId == UserHandle.USER_ALL) {
- for (int aUserId : allUserHandles) {
- stubPs.setEnabled(COMPONENT_ENABLED_STATE_DISABLED, aUserId, "android");
- }
- } else if (userId >= UserHandle.USER_SYSTEM) {
- stubPs.setEnabled(COMPONENT_ENABLED_STATE_DISABLED, userId, "android");
- }
- }
- }
- }
- }
-
- private void removeNativeBinariesLI(PackageSetting ps) {
- if (ps != null) {
- NativeLibraryHelper.removeNativeBinariesLI(ps.getLegacyNativeLibraryPath());
- }
- }
-
- /**
- * Installs a package that's already on the system partition.
- */
- @GuardedBy({"mPm.mLock", "mPm.mInstallLock"})
- private void installPackageFromSystemLIF(@NonNull String codePathString,
- @NonNull int[] allUserHandles, @Nullable int[] origUserHandles, boolean writeSettings)
- throws PackageManagerException {
- final File codePath = new File(codePathString);
- @ParsingPackageUtils.ParseFlags int parseFlags =
- mPm.getDefParseFlags()
- | ParsingPackageUtils.PARSE_MUST_BE_APK
- | ParsingPackageUtils.PARSE_IS_SYSTEM_DIR;
- @PackageManagerService.ScanFlags int scanFlags = SCAN_AS_SYSTEM;
- for (int i = mDirsToScanAsSystem.size() - 1; i >= 0; i--) {
- ScanPartition partition = mDirsToScanAsSystem.get(i);
- if (partition.containsFile(codePath)) {
- scanFlags |= partition.scanFlag;
- if (partition.containsPrivApp(codePath)) {
- scanFlags |= SCAN_AS_PRIVILEGED;
- }
- break;
- }
- }
-
- final ScanPackageHelper scanPackageHelper = new ScanPackageHelper(mPm);
- final AndroidPackage pkg =
- scanPackageHelper.scanPackageTracedLI(
- codePath, parseFlags, scanFlags, 0 /*currentTime*/, null);
-
- PackageSetting pkgSetting = mPm.mSettings.getPackageLPr(pkg.getPackageName());
-
- try {
- // update shared libraries for the newly re-installed system package
- mPm.updateSharedLibrariesLocked(pkg, pkgSetting, null, null,
- Collections.unmodifiableMap(mPm.mPackages));
- } catch (PackageManagerException e) {
- Slog.e(TAG, "updateAllSharedLibrariesLPw failed: " + e.getMessage());
- }
-
- mAppDataHelper.prepareAppDataAfterInstallLIF(pkg);
-
- // writer
- synchronized (mPm.mLock) {
- PackageSetting ps = mPm.mSettings.getPackageLPr(pkg.getPackageName());
-
- final boolean applyUserRestrictions = origUserHandles != null;
- if (applyUserRestrictions) {
- boolean installedStateChanged = false;
- if (DEBUG_REMOVE) {
- Slog.d(TAG, "Propagating install state across reinstall");
- }
- for (int userId : allUserHandles) {
- final boolean installed = ArrayUtils.contains(origUserHandles, userId);
- if (DEBUG_REMOVE) {
- Slog.d(TAG, " user " + userId + " => " + installed);
- }
- if (installed != ps.getInstalled(userId)) {
- installedStateChanged = true;
- }
- ps.setInstalled(installed, userId);
- if (installed) {
- ps.setUninstallReason(UNINSTALL_REASON_UNKNOWN, userId);
- }
- }
- // Regardless of writeSettings we need to ensure that this restriction
- // state propagation is persisted
- mPm.mSettings.writeAllUsersPackageRestrictionsLPr();
- if (installedStateChanged) {
- mPm.mSettings.writeKernelMappingLPr(ps);
- }
- }
-
- // The method below will take care of removing obsolete permissions and granting
- // install permissions.
- mPm.mPermissionManager.onPackageInstalled(pkg,
- Process.INVALID_UID /* previousAppId */,
- PermissionManagerServiceInternal.PackageInstalledParams.DEFAULT,
- UserHandle.USER_ALL);
- for (final int userId : allUserHandles) {
- if (applyUserRestrictions) {
- mPm.mSettings.writePermissionStateForUserLPr(userId, false);
- }
- }
-
- // can downgrade to reader here
- if (writeSettings) {
- mPm.writeSettingsLPrTEMP();
- }
- }
- }
-
public boolean isExpectingBetter(String packageName) {
return mExpectingBetter.containsKey(packageName);
}
+
+ public List<ScanPartition> getDirsToScanAsSystem() {
+ return mDirsToScanAsSystem;
+ }
}
diff --git a/services/core/java/com/android/server/pm/InstallPackageHelper.java b/services/core/java/com/android/server/pm/InstallPackageHelper.java
index dc5b85a..8ba8e45 100644
--- a/services/core/java/com/android/server/pm/InstallPackageHelper.java
+++ b/services/core/java/com/android/server/pm/InstallPackageHelper.java
@@ -16,41 +16,112 @@
package com.android.server.pm;
+import static android.content.pm.PackageManager.COMPONENT_ENABLED_STATE_DEFAULT;
+import static android.content.pm.PackageManager.COMPONENT_ENABLED_STATE_DISABLED;
+import static android.content.pm.PackageManager.INSTALL_FAILED_ALREADY_EXISTS;
+import static android.content.pm.PackageManager.INSTALL_FAILED_BAD_PERMISSION_GROUP;
+import static android.content.pm.PackageManager.INSTALL_FAILED_DUPLICATE_PACKAGE;
+import static android.content.pm.PackageManager.INSTALL_FAILED_DUPLICATE_PERMISSION;
+import static android.content.pm.PackageManager.INSTALL_FAILED_DUPLICATE_PERMISSION_GROUP;
+import static android.content.pm.PackageManager.INSTALL_FAILED_INSUFFICIENT_STORAGE;
+import static android.content.pm.PackageManager.INSTALL_FAILED_INTERNAL_ERROR;
+import static android.content.pm.PackageManager.INSTALL_FAILED_INVALID_APK;
+import static android.content.pm.PackageManager.INSTALL_FAILED_INVALID_INSTALL_LOCATION;
+import static android.content.pm.PackageManager.INSTALL_FAILED_PACKAGE_CHANGED;
+import static android.content.pm.PackageManager.INSTALL_FAILED_SESSION_INVALID;
+import static android.content.pm.PackageManager.INSTALL_FAILED_TEST_ONLY;
+import static android.content.pm.PackageManager.INSTALL_FAILED_UID_CHANGED;
import static android.content.pm.PackageManager.INSTALL_FAILED_UPDATE_INCOMPATIBLE;
+import static android.content.pm.PackageManager.INSTALL_FAILED_VERSION_DOWNGRADE;
import static android.content.pm.PackageManager.INSTALL_PARSE_FAILED_INCONSISTENT_CERTIFICATES;
+import static android.content.pm.PackageManager.INSTALL_REASON_DEVICE_RESTORE;
+import static android.content.pm.PackageManager.INSTALL_REASON_DEVICE_SETUP;
+import static android.content.pm.PackageManager.UNINSTALL_REASON_UNKNOWN;
+import static android.content.pm.SigningDetails.SignatureSchemeVersion.SIGNING_BLOCK_V4;
+import static android.os.PowerExemptionManager.REASON_PACKAGE_REPLACED;
import static android.os.Trace.TRACE_TAG_PACKAGE_MANAGER;
+import static android.os.incremental.IncrementalManager.isIncrementalPath;
+import static android.os.storage.StorageManager.FLAG_STORAGE_CE;
+import static android.os.storage.StorageManager.FLAG_STORAGE_DE;
+import static android.os.storage.StorageManager.FLAG_STORAGE_EXTERNAL;
+import static com.android.internal.content.NativeLibraryHelper.LIB_DIR_NAME;
+import static com.android.server.pm.InstructionSets.getAppDexInstructionSets;
import static com.android.server.pm.InstructionSets.getDexCodeInstructionSet;
import static com.android.server.pm.InstructionSets.getPreferredInstructionSet;
+import static com.android.server.pm.PackageManagerService.DEBUG_BACKUP;
+import static com.android.server.pm.PackageManagerService.DEBUG_COMPRESSION;
import static com.android.server.pm.PackageManagerService.DEBUG_INSTALL;
import static com.android.server.pm.PackageManagerService.DEBUG_PACKAGE_SCANNING;
+import static com.android.server.pm.PackageManagerService.DEBUG_REMOVE;
+import static com.android.server.pm.PackageManagerService.DEBUG_VERIFY;
+import static com.android.server.pm.PackageManagerService.EMPTY_INT_ARRAY;
+import static com.android.server.pm.PackageManagerService.PLATFORM_PACKAGE_NAME;
import static com.android.server.pm.PackageManagerService.POST_INSTALL;
+import static com.android.server.pm.PackageManagerService.PRECOMPILE_LAYOUTS;
import static com.android.server.pm.PackageManagerService.SCAN_AS_APK_IN_APEX;
+import static com.android.server.pm.PackageManagerService.SCAN_AS_FULL_APP;
import static com.android.server.pm.PackageManagerService.SCAN_AS_INSTANT_APP;
+import static com.android.server.pm.PackageManagerService.SCAN_AS_ODM;
+import static com.android.server.pm.PackageManagerService.SCAN_AS_OEM;
+import static com.android.server.pm.PackageManagerService.SCAN_AS_PRIVILEGED;
+import static com.android.server.pm.PackageManagerService.SCAN_AS_PRODUCT;
+import static com.android.server.pm.PackageManagerService.SCAN_AS_SYSTEM;
+import static com.android.server.pm.PackageManagerService.SCAN_AS_SYSTEM_EXT;
+import static com.android.server.pm.PackageManagerService.SCAN_AS_VENDOR;
+import static com.android.server.pm.PackageManagerService.SCAN_AS_VIRTUAL_PRELOAD;
import static com.android.server.pm.PackageManagerService.SCAN_BOOTING;
import static com.android.server.pm.PackageManagerService.SCAN_DONT_KILL_APP;
import static com.android.server.pm.PackageManagerService.SCAN_IGNORE_FROZEN;
+import static com.android.server.pm.PackageManagerService.SCAN_INITIAL;
+import static com.android.server.pm.PackageManagerService.SCAN_MOVE;
+import static com.android.server.pm.PackageManagerService.SCAN_NEW_INSTALL;
+import static com.android.server.pm.PackageManagerService.SCAN_NO_DEX;
+import static com.android.server.pm.PackageManagerService.SCAN_UPDATE_SIGNATURE;
import static com.android.server.pm.PackageManagerService.TAG;
import static com.android.server.pm.PackageManagerServiceUtils.compareSignatures;
+import static com.android.server.pm.PackageManagerServiceUtils.compressedFileExists;
+import static com.android.server.pm.PackageManagerServiceUtils.deriveAbiOverride;
+import static com.android.server.pm.PackageManagerServiceUtils.logCriticalInfo;
import static com.android.server.pm.PackageManagerServiceUtils.verifySignatures;
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.annotation.UserIdInt;
+import android.app.AppOpsManager;
+import android.app.ApplicationPackageManager;
import android.app.backup.IBackupManager;
+import android.content.ContentResolver;
import android.content.Context;
import android.content.Intent;
import android.content.IntentSender;
+import android.content.pm.ApplicationInfo;
import android.content.pm.DataLoaderType;
+import android.content.pm.IPackageInstallObserver2;
+import android.content.pm.PackageChangeEvent;
+import android.content.pm.PackageInfo;
+import android.content.pm.PackageInfoLite;
import android.content.pm.PackageInstaller;
import android.content.pm.PackageManager;
+import android.content.pm.PermissionGroupInfo;
+import android.content.pm.PermissionInfo;
import android.content.pm.SharedLibraryInfo;
import android.content.pm.Signature;
import android.content.pm.SigningDetails;
+import android.content.pm.VerifierInfo;
+import android.content.pm.dex.DexMetadataHelper;
import android.content.pm.parsing.ParsingPackageUtils;
import android.content.pm.parsing.component.ComponentMutateUtils;
import android.content.pm.parsing.component.ParsedInstrumentation;
+import android.content.pm.parsing.component.ParsedPermission;
+import android.content.pm.parsing.component.ParsedPermissionGroup;
+import android.content.pm.parsing.result.ParseResult;
+import android.content.pm.parsing.result.ParseTypeImpl;
+import android.net.Uri;
import android.os.Binder;
+import android.os.Build;
+import android.os.Bundle;
+import android.os.Environment;
import android.os.Message;
import android.os.Process;
import android.os.RemoteException;
@@ -60,34 +131,78 @@
import android.os.UserHandle;
import android.os.UserManager;
import android.os.incremental.IncrementalManager;
+import android.os.incremental.IncrementalStorage;
+import android.os.storage.StorageManager;
+import android.os.storage.VolumeInfo;
+import android.stats.storage.StorageEnums;
import android.util.ArrayMap;
+import android.util.ArraySet;
+import android.util.EventLog;
import android.util.Log;
+import android.util.Pair;
import android.util.Slog;
+import android.util.SparseArray;
import com.android.internal.annotations.GuardedBy;
+import com.android.internal.content.F2fsUtils;
+import com.android.internal.content.NativeLibraryHelper;
+import com.android.internal.content.PackageHelper;
+import com.android.internal.security.VerityUtils;
import com.android.internal.util.ArrayUtils;
+import com.android.internal.util.FrameworkStatsLog;
+import com.android.server.EventLogTags;
+import com.android.server.Watchdog;
+import com.android.server.pm.dex.DexoptOptions;
+import com.android.server.pm.parsing.PackageParser2;
import com.android.server.pm.parsing.pkg.AndroidPackage;
import com.android.server.pm.parsing.pkg.AndroidPackageUtils;
import com.android.server.pm.parsing.pkg.ParsedPackage;
+import com.android.server.pm.permission.Permission;
import com.android.server.pm.permission.PermissionManagerServiceInternal;
import com.android.server.pm.pkg.PackageUserState;
+import com.android.server.pm.pkg.PackageStateInternal;
import com.android.server.rollback.RollbackManagerInternal;
import com.android.server.utils.WatchedLongSparseArray;
+import dalvik.system.VMRuntime;
+
+import libcore.io.IoUtils;
+
+import java.io.File;
+import java.io.FileDescriptor;
+import java.io.FileInputStream;
+import java.io.IOException;
+import java.security.DigestException;
+import java.security.DigestInputStream;
+import java.security.MessageDigest;
+import java.security.NoSuchAlgorithmException;
+import java.security.PublicKey;
+import java.security.cert.CertificateException;
import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Collections;
import java.util.List;
import java.util.Map;
+import java.util.Objects;
+import java.util.Set;
final class InstallPackageHelper {
+ /**
+ * Whether verification is enabled by default.
+ */
+ private static final boolean DEFAULT_VERIFY_ENABLE = true;
+
private final PackageManagerService mPm;
private final AppDataHelper mAppDataHelper;
private final PackageManagerServiceInjector mInjector;
+ private final BroadcastHelper mBroadcastHelper;
// TODO(b/198166813): remove PMS dependency
InstallPackageHelper(PackageManagerService pm, AppDataHelper appDataHelper) {
mPm = pm;
mInjector = pm.mInjector;
mAppDataHelper = appDataHelper;
+ mBroadcastHelper = new BroadcastHelper(mInjector);
}
InstallPackageHelper(PackageManagerService pm) {
@@ -98,6 +213,7 @@
mPm = pm;
mInjector = injector;
mAppDataHelper = new AppDataHelper(pm, mInjector);
+ mBroadcastHelper = new BroadcastHelper(injector);
}
@GuardedBy("mPm.mLock")
@@ -700,9 +816,7 @@
mPm.mPermissionManager.onPackageInstalled(pkgSetting.getPkg(),
Process.INVALID_UID /* previousAppId */,
permissionParamsBuilder.build(), userId);
- }
- if (pkgSetting.getPkg() != null) {
synchronized (mPm.mInstallLock) {
// We don't need to freeze for a brand new install
mAppDataHelper.prepareAppDataAfterInstallLIF(pkgSetting.getPkg());
@@ -882,4 +996,2590 @@
}
return false;
}
+
+ public void processInstallRequests(boolean success, List<InstallRequest> installRequests) {
+ List<InstallRequest> apexInstallRequests = new ArrayList<>();
+ List<InstallRequest> apkInstallRequests = new ArrayList<>();
+ for (InstallRequest request : installRequests) {
+ if ((request.mArgs.mInstallFlags & PackageManager.INSTALL_APEX) != 0) {
+ apexInstallRequests.add(request);
+ } else {
+ apkInstallRequests.add(request);
+ }
+ }
+ // Note: supporting multi package install of both APEXes and APKs might requir some
+ // thinking to ensure atomicity of the install.
+ if (!apexInstallRequests.isEmpty() && !apkInstallRequests.isEmpty()) {
+ // This should've been caught at the validation step, but for some reason wasn't.
+ throw new IllegalStateException(
+ "Attempted to do a multi package install of both APEXes and APKs");
+ }
+ if (!apexInstallRequests.isEmpty()) {
+ if (success) {
+ // Since installApexPackages requires talking to external service (apexd), we
+ // schedule to run it async. Once it finishes, it will resume the install.
+ Thread t = new Thread(() -> installApexPackagesTraced(apexInstallRequests),
+ "installApexPackages");
+ t.start();
+ } else {
+ // Non-staged APEX installation failed somewhere before
+ // processInstallRequestAsync. In that case just notify the observer about the
+ // failure.
+ InstallRequest request = apexInstallRequests.get(0);
+ mPm.notifyInstallObserver(request.mInstallResult,
+ request.mArgs.mObserver);
+ }
+ return;
+ }
+ if (success) {
+ for (InstallRequest request : apkInstallRequests) {
+ request.mArgs.doPreInstall(request.mInstallResult.mReturnCode);
+ }
+ synchronized (mPm.mInstallLock) {
+ installPackagesTracedLI(apkInstallRequests);
+ }
+ for (InstallRequest request : apkInstallRequests) {
+ request.mArgs.doPostInstall(
+ request.mInstallResult.mReturnCode, request.mInstallResult.mUid);
+ }
+ }
+ for (InstallRequest request : apkInstallRequests) {
+ restoreAndPostInstall(request.mArgs.mUser.getIdentifier(),
+ request.mInstallResult,
+ new PostInstallData(request.mArgs,
+ request.mInstallResult, null));
+ }
+ }
+
+ private void installApexPackagesTraced(List<InstallRequest> requests) {
+ try {
+ Trace.traceBegin(TRACE_TAG_PACKAGE_MANAGER, "installApexPackages");
+ installApexPackages(requests);
+ } finally {
+ Trace.traceEnd(TRACE_TAG_PACKAGE_MANAGER);
+ }
+ }
+
+ private void installApexPackages(List<InstallRequest> requests) {
+ if (requests.isEmpty()) {
+ return;
+ }
+ if (requests.size() != 1) {
+ throw new IllegalStateException(
+ "Only a non-staged install of a single APEX is supported");
+ }
+ InstallRequest request = requests.get(0);
+ try {
+ // Should directory scanning logic be moved to ApexManager for better test coverage?
+ final File dir = request.mArgs.mOriginInfo.mResolvedFile;
+ final File[] apexes = dir.listFiles();
+ if (apexes == null) {
+ throw new PackageManagerException(INSTALL_FAILED_INTERNAL_ERROR,
+ dir.getAbsolutePath() + " is not a directory");
+ }
+ if (apexes.length != 1) {
+ throw new PackageManagerException(INSTALL_FAILED_INTERNAL_ERROR,
+ "Expected exactly one .apex file under " + dir.getAbsolutePath()
+ + " got: " + apexes.length);
+ }
+ try (PackageParser2 packageParser = mPm.mInjector.getScanningPackageParser()) {
+ mPm.mApexManager.installPackage(apexes[0], packageParser);
+ }
+ } catch (PackageManagerException e) {
+ request.mInstallResult.setError("APEX installation failed", e);
+ }
+ PackageManagerService.invalidatePackageInfoCache();
+ mPm.notifyInstallObserver(request.mInstallResult, request.mArgs.mObserver);
+ }
+
+ @GuardedBy("mInstallLock")
+ private void installPackagesTracedLI(List<InstallRequest> requests) {
+ try {
+ Trace.traceBegin(TRACE_TAG_PACKAGE_MANAGER, "installPackages");
+ installPackagesLI(requests);
+ } finally {
+ Trace.traceEnd(TRACE_TAG_PACKAGE_MANAGER);
+ }
+ }
+
+ /**
+ * Installs one or more packages atomically. This operation is broken up into four phases:
+ * <ul>
+ * <li><b>Prepare</b>
+ * <br/>Analyzes any current install state, parses the package and does initial
+ * validation on it.</li>
+ * <li><b>Scan</b>
+ * <br/>Interrogates the parsed packages given the context collected in prepare.</li>
+ * <li><b>Reconcile</b>
+ * <br/>Validates scanned packages in the context of each other and the current system
+ * state to ensure that the install will be successful.
+ * <li><b>Commit</b>
+ * <br/>Commits all scanned packages and updates system state. This is the only place
+ * that system state may be modified in the install flow and all predictable errors
+ * must be determined before this phase.</li>
+ * </ul>
+ *
+ * Failure at any phase will result in a full failure to install all packages.
+ */
+ @GuardedBy("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());
+ final Map<String, PackageInstalledInfo> installResults = new ArrayMap<>(requests.size());
+ final Map<String, PrepareResult> prepareResults = new ArrayMap<>(requests.size());
+ final Map<String, Settings.VersionInfo> versionInfos = new ArrayMap<>(requests.size());
+ final Map<String, PackageSetting> lastStaticSharedLibSettings =
+ new ArrayMap<>(requests.size());
+ final Map<String, Boolean> createdAppId = new ArrayMap<>(requests.size());
+ boolean success = false;
+ final ScanPackageHelper scanPackageHelper = new ScanPackageHelper(mPm);
+ try {
+ Trace.traceBegin(TRACE_TAG_PACKAGE_MANAGER, "installPackagesLI");
+ for (InstallRequest request : requests) {
+ // TODO(b/109941548): remove this once we've pulled everything from it and into
+ // scan, reconcile or commit.
+ final PrepareResult prepareResult;
+ try {
+ Trace.traceBegin(TRACE_TAG_PACKAGE_MANAGER, "preparePackage");
+ prepareResult =
+ preparePackageLI(request.mArgs, request.mInstallResult);
+ } catch (PrepareFailure prepareFailure) {
+ request.mInstallResult.setError(prepareFailure.error,
+ prepareFailure.getMessage());
+ request.mInstallResult.mOrigPackage = prepareFailure.mConflictingPackage;
+ request.mInstallResult.mOrigPermission = prepareFailure.mConflictingPermission;
+ return;
+ } finally {
+ Trace.traceEnd(TRACE_TAG_PACKAGE_MANAGER);
+ }
+ request.mInstallResult.setReturnCode(PackageManager.INSTALL_SUCCEEDED);
+ request.mInstallResult.mInstallerPackageName =
+ request.mArgs.mInstallSource.installerPackageName;
+
+ final String packageName = prepareResult.mPackageToScan.getPackageName();
+ prepareResults.put(packageName, prepareResult);
+ installResults.put(packageName, request.mInstallResult);
+ installArgs.put(packageName, request.mArgs);
+ try {
+ final ScanResult result = scanPackageHelper.scanPackageTracedLI(
+ prepareResult.mPackageToScan, prepareResult.mParseFlags,
+ prepareResult.mScanFlags, System.currentTimeMillis(),
+ request.mArgs.mUser, request.mArgs.mAbiOverride);
+ if (null != preparedScans.put(result.mPkgSetting.getPkg().getPackageName(),
+ result)) {
+ request.mInstallResult.setError(
+ PackageManager.INSTALL_FAILED_DUPLICATE_PACKAGE,
+ "Duplicate package "
+ + result.mPkgSetting.getPkg().getPackageName()
+ + " in multi-package install request.");
+ return;
+ }
+ createdAppId.put(packageName,
+ scanPackageHelper.optimisticallyRegisterAppId(result));
+ versionInfos.put(result.mPkgSetting.getPkg().getPackageName(),
+ mPm.getSettingsVersionForPackage(result.mPkgSetting.getPkg()));
+ if (result.mStaticSharedLibraryInfo != null) {
+ final PackageSetting sharedLibLatestVersionSetting =
+ mPm.getSharedLibLatestVersionSetting(result);
+ if (sharedLibLatestVersionSetting != null) {
+ lastStaticSharedLibSettings.put(
+ result.mPkgSetting.getPkg().getPackageName(),
+ sharedLibLatestVersionSetting);
+ }
+ }
+ } catch (PackageManagerException e) {
+ request.mInstallResult.setError("Scanning Failed.", e);
+ return;
+ }
+ }
+ ReconcileRequest
+ reconcileRequest = new ReconcileRequest(preparedScans, installArgs,
+ installResults,
+ prepareResults,
+ mPm.mSharedLibraries,
+ Collections.unmodifiableMap(mPm.mPackages), versionInfos,
+ lastStaticSharedLibSettings);
+ CommitRequest commitRequest = null;
+ synchronized (mPm.mLock) {
+ Map<String, ReconciledPackage> reconciledPackages;
+ try {
+ Trace.traceBegin(TRACE_TAG_PACKAGE_MANAGER, "reconcilePackages");
+ reconciledPackages = reconcilePackagesLocked(
+ reconcileRequest, mPm.mSettings.getKeySetManagerService(),
+ mPm.mInjector);
+ } catch (ReconcileFailure e) {
+ for (InstallRequest request : requests) {
+ request.mInstallResult.setError("Reconciliation failed...", e);
+ }
+ return;
+ } finally {
+ Trace.traceEnd(TRACE_TAG_PACKAGE_MANAGER);
+ }
+ try {
+ Trace.traceBegin(TRACE_TAG_PACKAGE_MANAGER, "commitPackages");
+ commitRequest = new CommitRequest(reconciledPackages,
+ mPm.mUserManager.getUserIds());
+ commitPackagesLocked(commitRequest);
+ success = true;
+ } finally {
+ Trace.traceEnd(TRACE_TAG_PACKAGE_MANAGER);
+ }
+ }
+ executePostCommitSteps(commitRequest);
+ } finally {
+ if (success) {
+ for (InstallRequest request : requests) {
+ final InstallArgs args = request.mArgs;
+ if (args.mDataLoaderType != DataLoaderType.INCREMENTAL) {
+ continue;
+ }
+ if (args.mSigningDetails.getSignatureSchemeVersion() != SIGNING_BLOCK_V4) {
+ continue;
+ }
+ // For incremental installs, we bypass the verifier prior to install. Now
+ // that we know the package is valid, send a notice to the verifier with
+ // the root hash of the base.apk.
+ final String baseCodePath = request.mInstallResult.mPkg.getBaseApkPath();
+ final String[] splitCodePaths = request.mInstallResult.mPkg.getSplitCodePaths();
+ final Uri originUri = Uri.fromFile(args.mOriginInfo.mResolvedFile);
+ final int verificationId = mPm.mPendingVerificationToken++;
+ final String rootHashString = PackageManagerServiceUtils
+ .buildVerificationRootHashString(baseCodePath, splitCodePaths);
+ VerificationUtils.broadcastPackageVerified(verificationId, originUri,
+ PackageManager.VERIFICATION_ALLOW, rootHashString,
+ args.mDataLoaderType, args.getUser(), mPm.mContext);
+ }
+ } else {
+ for (ScanResult result : preparedScans.values()) {
+ if (createdAppId.getOrDefault(result.mRequest.mParsedPackage.getPackageName(),
+ false)) {
+ scanPackageHelper.cleanUpAppIdCreation(result);
+ }
+ }
+ // TODO(b/194319951): create a more descriptive reason than unknown
+ // mark all non-failure installs as UNKNOWN so we do not treat them as success
+ for (InstallRequest request : requests) {
+ if (request.mInstallResult.mFreezer != null) {
+ request.mInstallResult.mFreezer.close();
+ }
+ if (request.mInstallResult.mReturnCode == PackageManager.INSTALL_SUCCEEDED) {
+ request.mInstallResult.mReturnCode = PackageManager.INSTALL_UNKNOWN;
+ }
+ }
+ }
+ Trace.traceEnd(TRACE_TAG_PACKAGE_MANAGER);
+ }
+ }
+
+ @GuardedBy("mInstallLock")
+ private PrepareResult preparePackageLI(InstallArgs args, PackageInstalledInfo res)
+ throws PrepareFailure {
+ final int installFlags = args.mInstallFlags;
+ final File tmpPackageFile = new File(args.getCodePath());
+ final boolean onExternal = args.mVolumeUuid != null;
+ final boolean instantApp = ((installFlags & PackageManager.INSTALL_INSTANT_APP) != 0);
+ final boolean fullApp = ((installFlags & PackageManager.INSTALL_FULL_APP) != 0);
+ final boolean virtualPreload =
+ ((installFlags & PackageManager.INSTALL_VIRTUAL_PRELOAD) != 0);
+ final boolean isRollback = args.mInstallReason == PackageManager.INSTALL_REASON_ROLLBACK;
+ @PackageManagerService.ScanFlags int scanFlags = SCAN_NEW_INSTALL | SCAN_UPDATE_SIGNATURE;
+ if (args.mMoveInfo != null) {
+ // moving a complete application; perform an initial scan on the new install location
+ scanFlags |= SCAN_INITIAL;
+ }
+ if ((installFlags & PackageManager.INSTALL_DONT_KILL_APP) != 0) {
+ scanFlags |= SCAN_DONT_KILL_APP;
+ }
+ if (instantApp) {
+ scanFlags |= SCAN_AS_INSTANT_APP;
+ }
+ if (fullApp) {
+ scanFlags |= SCAN_AS_FULL_APP;
+ }
+ if (virtualPreload) {
+ scanFlags |= SCAN_AS_VIRTUAL_PRELOAD;
+ }
+
+ if (DEBUG_INSTALL) Slog.d(TAG, "installPackageLI: path=" + tmpPackageFile);
+
+ // Validity check
+ if (instantApp && onExternal) {
+ Slog.i(TAG, "Incompatible ephemeral install; external=" + onExternal);
+ throw new PrepareFailure(PackageManager.INSTALL_FAILED_SESSION_INVALID);
+ }
+
+ // Retrieve PackageSettings and parse package
+ @ParsingPackageUtils.ParseFlags final int parseFlags =
+ mPm.getDefParseFlags() | ParsingPackageUtils.PARSE_CHATTY
+ | ParsingPackageUtils.PARSE_ENFORCE_CODE
+ | (onExternal ? ParsingPackageUtils.PARSE_EXTERNAL_STORAGE : 0);
+
+ Trace.traceBegin(TRACE_TAG_PACKAGE_MANAGER, "parsePackage");
+ final ParsedPackage parsedPackage;
+ try (PackageParser2 pp = mPm.mInjector.getPreparingPackageParser()) {
+ parsedPackage = pp.parsePackage(tmpPackageFile, parseFlags, false);
+ AndroidPackageUtils.validatePackageDexMetadata(parsedPackage);
+ } catch (PackageManagerException e) {
+ throw new PrepareFailure("Failed parse during installPackageLI", e);
+ } finally {
+ Trace.traceEnd(TRACE_TAG_PACKAGE_MANAGER);
+ }
+
+ // Instant apps have several additional install-time checks.
+ if (instantApp) {
+ if (parsedPackage.getTargetSdkVersion() < Build.VERSION_CODES.O) {
+ Slog.w(TAG, "Instant app package " + parsedPackage.getPackageName()
+ + " does not target at least O");
+ throw new PrepareFailure(INSTALL_FAILED_SESSION_INVALID,
+ "Instant app package must target at least O");
+ }
+ if (parsedPackage.getSharedUserId() != null) {
+ Slog.w(TAG, "Instant app package " + parsedPackage.getPackageName()
+ + " may not declare sharedUserId.");
+ throw new PrepareFailure(INSTALL_FAILED_SESSION_INVALID,
+ "Instant app package may not declare a sharedUserId");
+ }
+ }
+
+ if (parsedPackage.isStaticSharedLibrary()) {
+ // Static shared libraries have synthetic package names
+ PackageManagerService.renameStaticSharedLibraryPackage(parsedPackage);
+
+ // No static shared libs on external storage
+ if (onExternal) {
+ Slog.i(TAG, "Static shared libs can only be installed on internal storage.");
+ throw new PrepareFailure(INSTALL_FAILED_INVALID_INSTALL_LOCATION,
+ "Packages declaring static-shared libs cannot be updated");
+ }
+ }
+
+ String pkgName = res.mName = parsedPackage.getPackageName();
+ if (parsedPackage.isTestOnly()) {
+ if ((installFlags & PackageManager.INSTALL_ALLOW_TEST) == 0) {
+ throw new PrepareFailure(INSTALL_FAILED_TEST_ONLY, "installPackageLI");
+ }
+ }
+
+ // either use what we've been given or parse directly from the APK
+ if (args.mSigningDetails != SigningDetails.UNKNOWN) {
+ parsedPackage.setSigningDetails(args.mSigningDetails);
+ } else {
+ final ParseTypeImpl input = ParseTypeImpl.forDefaultParsing();
+ final ParseResult<SigningDetails> result = ParsingPackageUtils.getSigningDetails(
+ input, parsedPackage, false /*skipVerify*/);
+ if (result.isError()) {
+ throw new PrepareFailure("Failed collect during installPackageLI",
+ result.getException());
+ }
+ parsedPackage.setSigningDetails(result.getResult());
+ }
+
+ if (instantApp && parsedPackage.getSigningDetails().getSignatureSchemeVersion()
+ < SigningDetails.SignatureSchemeVersion.SIGNING_BLOCK_V2) {
+ Slog.w(TAG, "Instant app package " + parsedPackage.getPackageName()
+ + " is not signed with at least APK Signature Scheme v2");
+ throw new PrepareFailure(INSTALL_FAILED_SESSION_INVALID,
+ "Instant app package must be signed with APK Signature Scheme v2 or greater");
+ }
+
+ boolean systemApp = false;
+ boolean replace = false;
+ synchronized (mPm.mLock) {
+ // Check if installing already existing package
+ if ((installFlags & PackageManager.INSTALL_REPLACE_EXISTING) != 0) {
+ String oldName = mPm.mSettings.getRenamedPackageLPr(pkgName);
+ if (parsedPackage.getOriginalPackages().contains(oldName)
+ && mPm.mPackages.containsKey(oldName)) {
+ // This package is derived from an original package,
+ // and this device has been updating from that original
+ // name. We must continue using the original name, so
+ // rename the new package here.
+ parsedPackage.setPackageName(oldName);
+ pkgName = parsedPackage.getPackageName();
+ replace = true;
+ if (DEBUG_INSTALL) {
+ Slog.d(TAG, "Replacing existing renamed package: oldName="
+ + oldName + " pkgName=" + pkgName);
+ }
+ } else if (mPm.mPackages.containsKey(pkgName)) {
+ // This package, under its official name, already exists
+ // on the device; we should replace it.
+ replace = true;
+ if (DEBUG_INSTALL) Slog.d(TAG, "Replace existing package: " + pkgName);
+ }
+
+ if (replace) {
+ // Prevent apps opting out from runtime permissions
+ AndroidPackage oldPackage = mPm.mPackages.get(pkgName);
+ final int oldTargetSdk = oldPackage.getTargetSdkVersion();
+ final int newTargetSdk = parsedPackage.getTargetSdkVersion();
+ if (oldTargetSdk > Build.VERSION_CODES.LOLLIPOP_MR1
+ && newTargetSdk <= Build.VERSION_CODES.LOLLIPOP_MR1) {
+ throw new PrepareFailure(
+ PackageManager.INSTALL_FAILED_PERMISSION_MODEL_DOWNGRADE,
+ "Package " + parsedPackage.getPackageName()
+ + " new target SDK " + newTargetSdk
+ + " doesn't support runtime permissions but the old"
+ + " target SDK " + oldTargetSdk + " does.");
+ }
+ // Prevent persistent apps from being updated
+ if (oldPackage.isPersistent()
+ && ((installFlags & PackageManager.INSTALL_STAGED) == 0)) {
+ throw new PrepareFailure(PackageManager.INSTALL_FAILED_INVALID_APK,
+ "Package " + oldPackage.getPackageName() + " is a persistent app. "
+ + "Persistent apps are not updateable.");
+ }
+ }
+ }
+
+ PackageSetting ps = mPm.mSettings.getPackageLPr(pkgName);
+ if (ps != null) {
+ if (DEBUG_INSTALL) Slog.d(TAG, "Existing package: " + ps);
+
+ // Static shared libs have same package with different versions where
+ // we internally use a synthetic package name to allow multiple versions
+ // of the same package, therefore we need to compare signatures against
+ // the package setting for the latest library version.
+ PackageSetting signatureCheckPs = ps;
+ if (parsedPackage.isStaticSharedLibrary()) {
+ SharedLibraryInfo libraryInfo = mPm.getLatestSharedLibraVersionLPr(
+ parsedPackage);
+ if (libraryInfo != null) {
+ signatureCheckPs = mPm.mSettings.getPackageLPr(
+ libraryInfo.getPackageName());
+ }
+ }
+
+ // Quick validity check that we're signed correctly if updating;
+ // we'll check this again later when scanning, but we want to
+ // bail early here before tripping over redefined permissions.
+ final KeySetManagerService ksms = mPm.mSettings.getKeySetManagerService();
+ if (ksms.shouldCheckUpgradeKeySetLocked(signatureCheckPs, scanFlags)) {
+ if (!ksms.checkUpgradeKeySetLocked(signatureCheckPs, parsedPackage)) {
+ throw new PrepareFailure(INSTALL_FAILED_UPDATE_INCOMPATIBLE, "Package "
+ + parsedPackage.getPackageName() + " upgrade keys do not match the "
+ + "previously installed version");
+ }
+ } else {
+ try {
+ final boolean compareCompat =
+ isCompatSignatureUpdateNeeded(parsedPackage);
+ final boolean compareRecover =
+ isRecoverSignatureUpdateNeeded(parsedPackage);
+ // We don't care about disabledPkgSetting on install for now.
+ final boolean compatMatch = verifySignatures(signatureCheckPs, null,
+ parsedPackage.getSigningDetails(), compareCompat, compareRecover,
+ isRollback);
+ // The new KeySets will be re-added later in the scanning process.
+ if (compatMatch) {
+ synchronized (mPm.mLock) {
+ ksms.removeAppKeySetDataLPw(parsedPackage.getPackageName());
+ }
+ }
+ } catch (PackageManagerException e) {
+ throw new PrepareFailure(e.error, e.getMessage());
+ }
+ }
+
+ if (ps.getPkg() != null) {
+ systemApp = ps.getPkg().isSystem();
+ }
+ res.mOrigUsers = ps.queryInstalledUsers(mPm.mUserManager.getUserIds(), true);
+ }
+
+ final int numGroups = ArrayUtils.size(parsedPackage.getPermissionGroups());
+ for (int groupNum = 0; groupNum < numGroups; groupNum++) {
+ final ParsedPermissionGroup group =
+ parsedPackage.getPermissionGroups().get(groupNum);
+ final PermissionGroupInfo sourceGroup = mPm.getPermissionGroupInfo(group.getName(),
+ 0);
+
+ if (sourceGroup != null && cannotInstallWithBadPermissionGroups(parsedPackage)) {
+ final String sourcePackageName = sourceGroup.packageName;
+
+ if ((replace || !parsedPackage.getPackageName().equals(sourcePackageName))
+ && !doesSignatureMatchForPermissions(sourcePackageName, parsedPackage,
+ scanFlags)) {
+ EventLog.writeEvent(0x534e4554, "146211400", -1,
+ parsedPackage.getPackageName());
+
+ throw new PrepareFailure(INSTALL_FAILED_DUPLICATE_PERMISSION_GROUP,
+ "Package "
+ + parsedPackage.getPackageName()
+ + " attempting to redeclare permission group "
+ + group.getName() + " already owned by "
+ + sourcePackageName);
+ }
+ }
+ }
+
+ // TODO: Move logic for checking permission compatibility into PermissionManagerService
+ final int n = ArrayUtils.size(parsedPackage.getPermissions());
+ for (int i = n - 1; i >= 0; i--) {
+ final ParsedPermission perm = parsedPackage.getPermissions().get(i);
+ final Permission bp = mPm.mPermissionManager.getPermissionTEMP(perm.getName());
+
+ // Don't allow anyone but the system to define ephemeral permissions.
+ if ((perm.getProtectionLevel() & PermissionInfo.PROTECTION_FLAG_INSTANT) != 0
+ && !systemApp) {
+ Slog.w(TAG, "Non-System package " + parsedPackage.getPackageName()
+ + " attempting to delcare ephemeral permission "
+ + perm.getName() + "; Removing ephemeral.");
+ ComponentMutateUtils.setProtectionLevel(perm,
+ perm.getProtectionLevel() & ~PermissionInfo.PROTECTION_FLAG_INSTANT);
+ }
+
+ // Check whether the newly-scanned package wants to define an already-defined perm
+ if (bp != null) {
+ final String sourcePackageName = bp.getPackageName();
+
+ if (!doesSignatureMatchForPermissions(sourcePackageName, parsedPackage,
+ scanFlags)) {
+ // If the owning package is the system itself, we log but allow
+ // install to proceed; we fail the install on all other permission
+ // redefinitions.
+ if (!sourcePackageName.equals("android")) {
+ throw new PrepareFailure(INSTALL_FAILED_DUPLICATE_PERMISSION,
+ "Package "
+ + parsedPackage.getPackageName()
+ + " attempting to redeclare permission "
+ + perm.getName() + " already owned by "
+ + sourcePackageName)
+ .conflictsWithExistingPermission(perm.getName(),
+ sourcePackageName);
+ } else {
+ Slog.w(TAG, "Package " + parsedPackage.getPackageName()
+ + " attempting to redeclare system permission "
+ + perm.getName() + "; ignoring new declaration");
+ parsedPackage.removePermission(i);
+ }
+ } else if (!PLATFORM_PACKAGE_NAME.equals(parsedPackage.getPackageName())) {
+ // Prevent apps to change protection level to dangerous from any other
+ // type as this would allow a privilege escalation where an app adds a
+ // normal/signature permission in other app's group and later redefines
+ // it as dangerous leading to the group auto-grant.
+ if ((perm.getProtectionLevel() & PermissionInfo.PROTECTION_MASK_BASE)
+ == PermissionInfo.PROTECTION_DANGEROUS) {
+ if (bp != null && !bp.isRuntime()) {
+ Slog.w(TAG, "Package " + parsedPackage.getPackageName()
+ + " trying to change a non-runtime permission "
+ + perm.getName()
+ + " to runtime; keeping old protection level");
+ ComponentMutateUtils.setProtectionLevel(perm,
+ bp.getProtectionLevel());
+ }
+ }
+ }
+ }
+
+ if (perm.getGroup() != null
+ && cannotInstallWithBadPermissionGroups(parsedPackage)) {
+ boolean isPermGroupDefinedByPackage = false;
+ for (int groupNum = 0; groupNum < numGroups; groupNum++) {
+ if (parsedPackage.getPermissionGroups().get(groupNum).getName()
+ .equals(perm.getGroup())) {
+ isPermGroupDefinedByPackage = true;
+ break;
+ }
+ }
+
+ if (!isPermGroupDefinedByPackage) {
+ final PermissionGroupInfo sourceGroup =
+ mPm.getPermissionGroupInfo(perm.getGroup(), 0);
+
+ if (sourceGroup == null) {
+ EventLog.writeEvent(0x534e4554, "146211400", -1,
+ parsedPackage.getPackageName());
+
+ throw new PrepareFailure(INSTALL_FAILED_BAD_PERMISSION_GROUP,
+ "Package "
+ + parsedPackage.getPackageName()
+ + " attempting to declare permission "
+ + perm.getName() + " in non-existing group "
+ + perm.getGroup());
+ } else {
+ String groupSourcePackageName = sourceGroup.packageName;
+
+ if (!PLATFORM_PACKAGE_NAME.equals(groupSourcePackageName)
+ && !doesSignatureMatchForPermissions(groupSourcePackageName,
+ parsedPackage, scanFlags)) {
+ EventLog.writeEvent(0x534e4554, "146211400", -1,
+ parsedPackage.getPackageName());
+
+ throw new PrepareFailure(INSTALL_FAILED_BAD_PERMISSION_GROUP,
+ "Package "
+ + parsedPackage.getPackageName()
+ + " attempting to declare permission "
+ + perm.getName() + " in group "
+ + perm.getGroup() + " owned by package "
+ + groupSourcePackageName
+ + " with incompatible certificate");
+ }
+ }
+ }
+ }
+ }
+ }
+
+ if (systemApp) {
+ if (onExternal) {
+ // Abort update; system app can't be replaced with app on sdcard
+ throw new PrepareFailure(INSTALL_FAILED_INVALID_INSTALL_LOCATION,
+ "Cannot install updates to system apps on sdcard");
+ } else if (instantApp) {
+ // Abort update; system app can't be replaced with an instant app
+ throw new PrepareFailure(INSTALL_FAILED_SESSION_INVALID,
+ "Cannot update a system app with an instant app");
+ }
+ }
+
+ if (args.mMoveInfo != null) {
+ // We did an in-place move, so dex is ready to roll
+ scanFlags |= SCAN_NO_DEX;
+ scanFlags |= SCAN_MOVE;
+
+ synchronized (mPm.mLock) {
+ final PackageSetting ps = mPm.mSettings.getPackageLPr(pkgName);
+ if (ps == null) {
+ res.setError(INSTALL_FAILED_INTERNAL_ERROR,
+ "Missing settings for moved package " + pkgName);
+ }
+
+ // We moved the entire application as-is, so bring over the
+ // previously derived ABI information.
+ parsedPackage.setPrimaryCpuAbi(ps.getPrimaryCpuAbi())
+ .setSecondaryCpuAbi(ps.getSecondaryCpuAbi());
+ }
+
+ } else {
+ // Enable SCAN_NO_DEX flag to skip dexopt at a later stage
+ scanFlags |= SCAN_NO_DEX;
+
+ try {
+ PackageSetting pkgSetting;
+ AndroidPackage oldPackage;
+ synchronized (mPm.mLock) {
+ pkgSetting = mPm.mSettings.getPackageLPr(pkgName);
+ oldPackage = mPm.mPackages.get(pkgName);
+ }
+ boolean isUpdatedSystemAppFromExistingSetting = pkgSetting != null
+ && pkgSetting.getPkgState().isUpdatedSystemApp();
+ final String abiOverride = deriveAbiOverride(args.mAbiOverride);
+ boolean isUpdatedSystemAppInferred = oldPackage != null && oldPackage.isSystem();
+ final Pair<PackageAbiHelper.Abis, PackageAbiHelper.NativeLibraryPaths>
+ derivedAbi = mPm.mInjector.getAbiHelper().derivePackageAbi(parsedPackage,
+ isUpdatedSystemAppFromExistingSetting || isUpdatedSystemAppInferred,
+ abiOverride, mPm.mAppLib32InstallDir);
+ derivedAbi.first.applyTo(parsedPackage);
+ derivedAbi.second.applyTo(parsedPackage);
+ } catch (PackageManagerException pme) {
+ Slog.e(TAG, "Error deriving application ABI", pme);
+ throw new PrepareFailure(INSTALL_FAILED_INTERNAL_ERROR,
+ "Error deriving application ABI: " + pme.getMessage());
+ }
+ }
+
+ if (!args.doRename(res.mReturnCode, parsedPackage)) {
+ throw new PrepareFailure(INSTALL_FAILED_INSUFFICIENT_STORAGE, "Failed rename");
+ }
+
+ try {
+ setUpFsVerityIfPossible(parsedPackage);
+ } catch (Installer.InstallerException | IOException | DigestException
+ | NoSuchAlgorithmException e) {
+ throw new PrepareFailure(INSTALL_FAILED_INTERNAL_ERROR,
+ "Failed to set up verity: " + e);
+ }
+
+ final PackageFreezer freezer =
+ freezePackageForInstall(pkgName, installFlags, "installPackageLI");
+ boolean shouldCloseFreezerBeforeReturn = true;
+ try {
+ final AndroidPackage oldPackage;
+ String renamedPackage;
+ boolean sysPkg = false;
+ int targetScanFlags = scanFlags;
+ int targetParseFlags = parseFlags;
+ final PackageSetting ps;
+ final PackageSetting disabledPs;
+ if (replace) {
+ final String pkgName11 = parsedPackage.getPackageName();
+ synchronized (mPm.mLock) {
+ oldPackage = mPm.mPackages.get(pkgName11);
+ }
+ if (parsedPackage.isStaticSharedLibrary()) {
+ // Static libs have a synthetic package name containing the version
+ // and cannot be updated as an update would get a new package name,
+ // unless this is installed from adb which is useful for development.
+ if (oldPackage != null
+ && (installFlags & PackageManager.INSTALL_FROM_ADB) == 0) {
+ throw new PrepareFailure(INSTALL_FAILED_DUPLICATE_PACKAGE,
+ "Packages declaring "
+ + "static-shared libs cannot be updated");
+ }
+ }
+
+ final boolean isInstantApp = (scanFlags & SCAN_AS_INSTANT_APP) != 0;
+
+ final int[] allUsers;
+ final int[] installedUsers;
+ final int[] uninstalledUsers;
+
+ synchronized (mPm.mLock) {
+ if (DEBUG_INSTALL) {
+ Slog.d(TAG,
+ "replacePackageLI: new=" + parsedPackage + ", old=" + oldPackage);
+ }
+
+ ps = mPm.mSettings.getPackageLPr(pkgName11);
+ disabledPs = mPm.mSettings.getDisabledSystemPkgLPr(ps);
+
+ // verify signatures are valid
+ final KeySetManagerService ksms = mPm.mSettings.getKeySetManagerService();
+ if (ksms.shouldCheckUpgradeKeySetLocked(ps, scanFlags)) {
+ if (!ksms.checkUpgradeKeySetLocked(ps, parsedPackage)) {
+ throw new PrepareFailure(INSTALL_FAILED_UPDATE_INCOMPATIBLE,
+ "New package not signed by keys specified by upgrade-keysets: "
+ + pkgName11);
+ }
+ } else {
+ SigningDetails parsedPkgSigningDetails = parsedPackage.getSigningDetails();
+ SigningDetails oldPkgSigningDetails = oldPackage.getSigningDetails();
+ // default to original signature matching
+ if (!parsedPkgSigningDetails.checkCapability(oldPkgSigningDetails,
+ SigningDetails.CertCapabilities.INSTALLED_DATA)
+ && !oldPkgSigningDetails.checkCapability(parsedPkgSigningDetails,
+ SigningDetails.CertCapabilities.ROLLBACK)) {
+ // Allow the update to proceed if this is a rollback and the parsed
+ // package's current signing key is the current signer or in the lineage
+ // of the old package; this allows a rollback to a previously installed
+ // version after an app's signing key has been rotated without requiring
+ // the rollback capability on the previous signing key.
+ if (!isRollback || !oldPkgSigningDetails.hasAncestorOrSelf(
+ parsedPkgSigningDetails)) {
+ throw new PrepareFailure(INSTALL_FAILED_UPDATE_INCOMPATIBLE,
+ "New package has a different signature: " + pkgName11);
+ }
+ }
+ }
+
+ // don't allow a system upgrade unless the upgrade hash matches
+ if (oldPackage.getRestrictUpdateHash() != null && oldPackage.isSystem()) {
+ final byte[] digestBytes;
+ try {
+ final MessageDigest digest = MessageDigest.getInstance("SHA-512");
+ updateDigest(digest, new File(parsedPackage.getBaseApkPath()));
+ if (!ArrayUtils.isEmpty(parsedPackage.getSplitCodePaths())) {
+ for (String path : parsedPackage.getSplitCodePaths()) {
+ updateDigest(digest, new File(path));
+ }
+ }
+ digestBytes = digest.digest();
+ } catch (NoSuchAlgorithmException | IOException e) {
+ throw new PrepareFailure(INSTALL_FAILED_INVALID_APK,
+ "Could not compute hash: " + pkgName11);
+ }
+ if (!Arrays.equals(oldPackage.getRestrictUpdateHash(), digestBytes)) {
+ throw new PrepareFailure(INSTALL_FAILED_INVALID_APK,
+ "New package fails restrict-update check: " + pkgName11);
+ }
+ // retain upgrade restriction
+ parsedPackage.setRestrictUpdateHash(oldPackage.getRestrictUpdateHash());
+ }
+
+ // Check for shared user id changes
+ if (!Objects.equals(oldPackage.getSharedUserId(),
+ parsedPackage.getSharedUserId())
+ // Don't mark as invalid if the app is trying to
+ // leave a sharedUserId
+ && parsedPackage.getSharedUserId() != null) {
+ throw new PrepareFailure(INSTALL_FAILED_UID_CHANGED,
+ "Package " + parsedPackage.getPackageName()
+ + " shared user changed from "
+ + (oldPackage.getSharedUserId() != null
+ ? oldPackage.getSharedUserId() : "<nothing>")
+ + " to " + parsedPackage.getSharedUserId());
+ }
+
+ // In case of rollback, remember per-user/profile install state
+ allUsers = mPm.mUserManager.getUserIds();
+ installedUsers = ps.queryInstalledUsers(allUsers, true);
+ uninstalledUsers = ps.queryInstalledUsers(allUsers, false);
+
+
+ // don't allow an upgrade from full to ephemeral
+ if (isInstantApp) {
+ if (args.mUser == null
+ || args.mUser.getIdentifier() == UserHandle.USER_ALL) {
+ for (int currentUser : allUsers) {
+ if (!ps.getInstantApp(currentUser)) {
+ // can't downgrade from full to instant
+ Slog.w(TAG,
+ "Can't replace full app with instant app: " + pkgName11
+ + " for user: " + currentUser);
+ throw new PrepareFailure(
+ PackageManager.INSTALL_FAILED_SESSION_INVALID);
+ }
+ }
+ } else if (!ps.getInstantApp(args.mUser.getIdentifier())) {
+ // can't downgrade from full to instant
+ Slog.w(TAG, "Can't replace full app with instant app: " + pkgName11
+ + " for user: " + args.mUser.getIdentifier());
+ throw new PrepareFailure(
+ PackageManager.INSTALL_FAILED_SESSION_INVALID);
+ }
+ }
+ }
+
+ // Update what is removed
+ res.mRemovedInfo = new PackageRemovedInfo(mPm);
+ res.mRemovedInfo.mUid = oldPackage.getUid();
+ res.mRemovedInfo.mRemovedPackage = oldPackage.getPackageName();
+ res.mRemovedInfo.mInstallerPackageName = ps.getInstallSource().installerPackageName;
+ res.mRemovedInfo.mIsStaticSharedLib =
+ parsedPackage.getStaticSharedLibName() != null;
+ res.mRemovedInfo.mIsUpdate = true;
+ res.mRemovedInfo.mOrigUsers = installedUsers;
+ res.mRemovedInfo.mInstallReasons = new SparseArray<>(installedUsers.length);
+ for (int i = 0; i < installedUsers.length; i++) {
+ final int userId = installedUsers[i];
+ res.mRemovedInfo.mInstallReasons.put(userId, ps.getInstallReason(userId));
+ }
+ res.mRemovedInfo.mUninstallReasons = new SparseArray<>(uninstalledUsers.length);
+ for (int i = 0; i < uninstalledUsers.length; i++) {
+ final int userId = uninstalledUsers[i];
+ res.mRemovedInfo.mUninstallReasons.put(userId, ps.getUninstallReason(userId));
+ }
+
+ sysPkg = oldPackage.isSystem();
+ if (sysPkg) {
+ // Set the system/privileged/oem/vendor/product flags as needed
+ final boolean privileged = oldPackage.isPrivileged();
+ final boolean oem = oldPackage.isOem();
+ final boolean vendor = oldPackage.isVendor();
+ final boolean product = oldPackage.isProduct();
+ final boolean odm = oldPackage.isOdm();
+ final boolean systemExt = oldPackage.isSystemExt();
+ final @ParsingPackageUtils.ParseFlags int systemParseFlags = parseFlags;
+ final @PackageManagerService.ScanFlags int systemScanFlags = scanFlags
+ | SCAN_AS_SYSTEM
+ | (privileged ? SCAN_AS_PRIVILEGED : 0)
+ | (oem ? SCAN_AS_OEM : 0)
+ | (vendor ? SCAN_AS_VENDOR : 0)
+ | (product ? SCAN_AS_PRODUCT : 0)
+ | (odm ? SCAN_AS_ODM : 0)
+ | (systemExt ? SCAN_AS_SYSTEM_EXT : 0);
+
+ if (DEBUG_INSTALL) {
+ Slog.d(TAG, "replaceSystemPackageLI: new=" + parsedPackage
+ + ", old=" + oldPackage);
+ }
+ res.setReturnCode(PackageManager.INSTALL_SUCCEEDED);
+ targetParseFlags = systemParseFlags;
+ targetScanFlags = systemScanFlags;
+ } else { // non system replace
+ replace = true;
+ if (DEBUG_INSTALL) {
+ Slog.d(TAG,
+ "replaceNonSystemPackageLI: new=" + parsedPackage + ", old="
+ + oldPackage);
+ }
+ }
+ } else { // new package install
+ ps = null;
+ disabledPs = null;
+ replace = false;
+ oldPackage = null;
+ // Remember this for later, in case we need to rollback this install
+ String pkgName1 = parsedPackage.getPackageName();
+
+ if (DEBUG_INSTALL) Slog.d(TAG, "installNewPackageLI: " + parsedPackage);
+
+ // TODO(b/194319951): MOVE TO RECONCILE
+ synchronized (mPm.mLock) {
+ renamedPackage = mPm.mSettings.getRenamedPackageLPr(pkgName1);
+ if (renamedPackage != null) {
+ // A package with the same name is already installed, though
+ // it has been renamed to an older name. The package we
+ // are trying to install should be installed as an update to
+ // the existing one, but that has not been requested, so bail.
+ throw new PrepareFailure(INSTALL_FAILED_ALREADY_EXISTS,
+ "Attempt to re-install " + pkgName1
+ + " without first uninstalling package running as "
+ + renamedPackage);
+ }
+ if (mPm.mPackages.containsKey(pkgName1)) {
+ // Don't allow installation over an existing package with the same name.
+ throw new PrepareFailure(INSTALL_FAILED_ALREADY_EXISTS,
+ "Attempt to re-install " + pkgName1
+ + " without first uninstalling.");
+ }
+ }
+ }
+ // we're passing the freezer back to be closed in a later phase of install
+ shouldCloseFreezerBeforeReturn = false;
+
+ return new PrepareResult(replace, targetScanFlags, targetParseFlags,
+ oldPackage, parsedPackage, replace /* clearCodeCache */, sysPkg,
+ ps, disabledPs);
+ } finally {
+ res.mFreezer = freezer;
+ if (shouldCloseFreezerBeforeReturn) {
+ freezer.close();
+ }
+ }
+ }
+
+ /*
+ * Cannot properly check CANNOT_INSTALL_WITH_BAD_PERMISSION_GROUPS using CompatChanges
+ * as this only works for packages that are installed
+ *
+ * TODO: Move logic for permission group compatibility into PermissionManagerService
+ */
+ @SuppressWarnings("AndroidFrameworkCompatChange")
+ private static boolean cannotInstallWithBadPermissionGroups(ParsedPackage parsedPackage) {
+ return parsedPackage.getTargetSdkVersion() >= Build.VERSION_CODES.S;
+ }
+
+ private boolean doesSignatureMatchForPermissions(@NonNull String sourcePackageName,
+ @NonNull ParsedPackage parsedPackage, int scanFlags) {
+ // If the defining package is signed with our cert, it's okay. This
+ // also includes the "updating the same package" case, of course.
+ // "updating same package" could also involve key-rotation.
+
+ final PackageSetting sourcePackageSetting;
+ final KeySetManagerService ksms;
+ synchronized (mPm.mLock) {
+ sourcePackageSetting = mPm.mSettings.getPackageLPr(sourcePackageName);
+ ksms = mPm.mSettings.getKeySetManagerService();
+ }
+
+ final SigningDetails sourceSigningDetails = (sourcePackageSetting == null
+ ? SigningDetails.UNKNOWN : sourcePackageSetting.getSigningDetails());
+ if (sourcePackageName.equals(parsedPackage.getPackageName())
+ && (ksms.shouldCheckUpgradeKeySetLocked(
+ sourcePackageSetting, scanFlags))) {
+ return ksms.checkUpgradeKeySetLocked(sourcePackageSetting, parsedPackage);
+ } else {
+
+ // in the event of signing certificate rotation, we need to see if the
+ // package's certificate has rotated from the current one, or if it is an
+ // older certificate with which the current is ok with sharing permissions
+ if (sourceSigningDetails.checkCapability(
+ parsedPackage.getSigningDetails(),
+ SigningDetails.CertCapabilities.PERMISSION)) {
+ return true;
+ } else if (parsedPackage.getSigningDetails().checkCapability(
+ sourceSigningDetails,
+ SigningDetails.CertCapabilities.PERMISSION)) {
+ // the scanned package checks out, has signing certificate rotation
+ // history, and is newer; bring it over
+ synchronized (mPm.mLock) {
+ sourcePackageSetting.setSigningDetails(parsedPackage.getSigningDetails());
+ }
+ return true;
+ } else {
+ return false;
+ }
+ }
+ }
+
+ /**
+ * Set up fs-verity for the given package if possible. This requires a feature flag of system
+ * property to be enabled only if the kernel supports fs-verity.
+ *
+ * <p>When the feature flag is set to legacy mode, only APK is supported (with some experimental
+ * kernel patches). In normal mode, all file format can be supported.
+ */
+ private void setUpFsVerityIfPossible(AndroidPackage pkg) throws Installer.InstallerException,
+ PrepareFailure, IOException, DigestException, NoSuchAlgorithmException {
+ final boolean standardMode = PackageManagerServiceUtils.isApkVerityEnabled();
+ final boolean legacyMode = PackageManagerServiceUtils.isLegacyApkVerityEnabled();
+ if (!standardMode && !legacyMode) {
+ return;
+ }
+
+ if (isIncrementalPath(pkg.getPath()) && IncrementalManager.getVersion()
+ < IncrementalManager.MIN_VERSION_TO_SUPPORT_FSVERITY) {
+ return;
+ }
+
+ // Collect files we care for fs-verity setup.
+ ArrayMap<String, String> fsverityCandidates = new ArrayMap<>();
+ if (legacyMode) {
+ synchronized (mPm.mLock) {
+ final PackageSetting ps = mPm.mSettings.getPackageLPr(pkg.getPackageName());
+ if (ps != null && ps.isPrivileged()) {
+ fsverityCandidates.put(pkg.getBaseApkPath(), null);
+ if (pkg.getSplitCodePaths() != null) {
+ for (String splitPath : pkg.getSplitCodePaths()) {
+ fsverityCandidates.put(splitPath, null);
+ }
+ }
+ }
+ }
+ } else {
+ // NB: These files will become only accessible if the signing key is loaded in kernel's
+ // .fs-verity keyring.
+ fsverityCandidates.put(pkg.getBaseApkPath(),
+ VerityUtils.getFsveritySignatureFilePath(pkg.getBaseApkPath()));
+
+ final String dmPath = DexMetadataHelper.buildDexMetadataPathForApk(
+ pkg.getBaseApkPath());
+ if (new File(dmPath).exists()) {
+ fsverityCandidates.put(dmPath, VerityUtils.getFsveritySignatureFilePath(dmPath));
+ }
+
+ if (pkg.getSplitCodePaths() != null) {
+ for (String path : pkg.getSplitCodePaths()) {
+ fsverityCandidates.put(path, VerityUtils.getFsveritySignatureFilePath(path));
+
+ final String splitDmPath = DexMetadataHelper.buildDexMetadataPathForApk(path);
+ if (new File(splitDmPath).exists()) {
+ fsverityCandidates.put(splitDmPath,
+ VerityUtils.getFsveritySignatureFilePath(splitDmPath));
+ }
+ }
+ }
+ }
+
+ for (Map.Entry<String, String> entry : fsverityCandidates.entrySet()) {
+ final String filePath = entry.getKey();
+ final String signaturePath = entry.getValue();
+
+ if (!legacyMode) {
+ // fs-verity is optional for now. Only set up if signature is provided.
+ if (new File(signaturePath).exists() && !VerityUtils.hasFsverity(filePath)) {
+ try {
+ VerityUtils.setUpFsverity(filePath, signaturePath);
+ } catch (IOException e) {
+ throw new PrepareFailure(PackageManager.INSTALL_FAILED_BAD_SIGNATURE,
+ "Failed to enable fs-verity: " + e);
+ }
+ }
+ continue;
+ }
+
+ // In legacy mode, fs-verity can only be enabled by process with CAP_SYS_ADMIN.
+ final VerityUtils.SetupResult result = VerityUtils.generateApkVeritySetupData(filePath);
+ if (result.isOk()) {
+ if (Build.IS_DEBUGGABLE) Slog.i(TAG, "Enabling verity to " + filePath);
+ final FileDescriptor fd = result.getUnownedFileDescriptor();
+ try {
+ final byte[] rootHash = VerityUtils.generateApkVerityRootHash(filePath);
+ try {
+ // A file may already have fs-verity, e.g. when reused during a split
+ // install. If the measurement succeeds, no need to attempt to set up.
+ mPm.mInstaller.assertFsverityRootHashMatches(filePath, rootHash);
+ } catch (Installer.InstallerException e) {
+ mPm.mInstaller.installApkVerity(filePath, fd, result.getContentSize());
+ mPm.mInstaller.assertFsverityRootHashMatches(filePath, rootHash);
+ }
+ } finally {
+ IoUtils.closeQuietly(fd);
+ }
+ } else if (result.isFailed()) {
+ throw new PrepareFailure(PackageManager.INSTALL_FAILED_BAD_SIGNATURE,
+ "Failed to generate verity");
+ }
+ }
+ }
+
+ private PackageFreezer freezePackageForInstall(String packageName, int installFlags,
+ String killReason) {
+ return freezePackageForInstall(packageName, UserHandle.USER_ALL, installFlags, killReason);
+ }
+
+ private PackageFreezer freezePackageForInstall(String packageName, int userId, int installFlags,
+ String killReason) {
+ if ((installFlags & PackageManager.INSTALL_DONT_KILL_APP) != 0) {
+ return new PackageFreezer(mPm);
+ } else {
+ return mPm.freezePackage(packageName, userId, killReason);
+ }
+ }
+
+ private static void updateDigest(MessageDigest digest, File file) throws IOException {
+ try (DigestInputStream digestStream =
+ new DigestInputStream(new FileInputStream(file), digest)) {
+ int length, total = 0;
+ while ((length = digestStream.read()) != -1) {
+ total += length;
+ } // just plow through the file
+ }
+ }
+
+ @GuardedBy("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.)
+ for (ReconciledPackage reconciledPkg : request.mReconciledPackages.values()) {
+ final ScanResult scanResult = reconciledPkg.mScanResult;
+ final ScanRequest scanRequest = scanResult.mRequest;
+ final ParsedPackage parsedPackage = scanRequest.mParsedPackage;
+ final String packageName = parsedPackage.getPackageName();
+ final PackageInstalledInfo res = reconciledPkg.mInstallResult;
+ final RemovePackageHelper removePackageHelper = new RemovePackageHelper(mPm);
+ final DeletePackageHelper deletePackageHelper = new DeletePackageHelper(mPm);
+
+ if (reconciledPkg.mPrepareResult.mReplace) {
+ AndroidPackage oldPackage = mPm.mPackages.get(packageName);
+
+ // Set the update and install times
+ PackageStateInternal deletedPkgSetting = mPm.getPackageStateInternal(
+ oldPackage.getPackageName());
+ reconciledPkg.mPkgSetting
+ .setFirstInstallTime(deletedPkgSetting.getFirstInstallTime())
+ .setLastUpdateTime(System.currentTimeMillis());
+
+ res.mRemovedInfo.mBroadcastAllowList = mPm.mAppsFilter.getVisibilityAllowList(
+ reconciledPkg.mPkgSetting, request.mAllUsers,
+ mPm.mSettings.getPackagesLocked());
+ if (reconciledPkg.mPrepareResult.mSystem) {
+ // Remove existing system package
+ removePackageHelper.removePackageLI(oldPackage, true);
+ if (!disableSystemPackageLPw(oldPackage)) {
+ // We didn't need to disable the .apk as a current system package,
+ // which means we are replacing another update that is already
+ // installed. We need to make sure to delete the older one's .apk.
+ res.mRemovedInfo.mArgs = new FileInstallArgs(
+ oldPackage.getPath(),
+ getAppDexInstructionSets(
+ AndroidPackageUtils.getPrimaryCpuAbi(oldPackage,
+ deletedPkgSetting),
+ AndroidPackageUtils.getSecondaryCpuAbi(oldPackage,
+ deletedPkgSetting)), mPm);
+ } else {
+ res.mRemovedInfo.mArgs = null;
+ }
+ } else {
+ try {
+ // Settings will be written during the call to updateSettingsLI().
+ deletePackageHelper.executeDeletePackageLIF(
+ reconciledPkg.mDeletePackageAction, packageName,
+ true, request.mAllUsers, false);
+ } catch (SystemDeleteException e) {
+ if (mPm.mIsEngBuild) {
+ throw new RuntimeException("Unexpected failure", e);
+ // ignore; not possible for non-system app
+ }
+ }
+ // Successfully deleted the old package; proceed with replace.
+
+ // If deleted package lived in a container, give users a chance to
+ // relinquish resources before killing.
+ if (oldPackage.isExternalStorage()) {
+ if (DEBUG_INSTALL) {
+ Slog.i(TAG, "upgrading pkg " + oldPackage
+ + " is ASEC-hosted -> UNAVAILABLE");
+ }
+ final int[] uidArray = new int[]{oldPackage.getUid()};
+ final ArrayList<String> pkgList = new ArrayList<>(1);
+ pkgList.add(oldPackage.getPackageName());
+ mBroadcastHelper.sendResourcesChangedBroadcast(
+ false, true, pkgList, uidArray, null);
+ }
+
+ // Update the in-memory copy of the previous code paths.
+ PackageSetting ps1 = mPm.mSettings.getPackageLPr(
+ reconciledPkg.mPrepareResult.mExistingPackage.getPackageName());
+ if ((reconciledPkg.mInstallArgs.mInstallFlags & PackageManager.DONT_KILL_APP)
+ == 0) {
+ if (ps1.mOldCodePaths == null) {
+ ps1.mOldCodePaths = new ArraySet<>();
+ }
+ Collections.addAll(ps1.mOldCodePaths, oldPackage.getBaseApkPath());
+ if (oldPackage.getSplitCodePaths() != null) {
+ Collections.addAll(ps1.mOldCodePaths, oldPackage.getSplitCodePaths());
+ }
+ } else {
+ ps1.mOldCodePaths = null;
+ }
+
+ if (reconciledPkg.mInstallResult.mReturnCode
+ == PackageManager.INSTALL_SUCCEEDED) {
+ PackageSetting ps2 = mPm.mSettings.getPackageLPr(
+ parsedPackage.getPackageName());
+ if (ps2 != null) {
+ res.mRemovedInfo.mRemovedForAllUsers =
+ mPm.mPackages.get(ps2.getPackageName()) == null;
+ }
+ }
+ }
+ }
+
+ AndroidPackage pkg = commitReconciledScanResultLocked(
+ reconciledPkg, request.mAllUsers);
+ updateSettingsLI(pkg, reconciledPkg, request.mAllUsers, res);
+
+ final PackageSetting ps = mPm.mSettings.getPackageLPr(packageName);
+ if (ps != null) {
+ res.mNewUsers = ps.queryInstalledUsers(mPm.mUserManager.getUserIds(), true);
+ ps.setUpdateAvailable(false /*updateAvailable*/);
+ }
+ if (res.mReturnCode == PackageManager.INSTALL_SUCCEEDED) {
+ mPm.updateSequenceNumberLP(ps, res.mNewUsers);
+ mPm.updateInstantAppInstallerLocked(packageName);
+ }
+ }
+ ApplicationPackageManager.invalidateGetPackagesForUidCache();
+ }
+
+ @GuardedBy("mLock")
+ private boolean disableSystemPackageLPw(AndroidPackage oldPkg) {
+ return mPm.mSettings.disableSystemPackageLPw(oldPkg.getPackageName(), true);
+ }
+
+ private void updateSettingsLI(AndroidPackage newPackage, ReconciledPackage reconciledPkg,
+ int[] allUsers, PackageInstalledInfo res) {
+ updateSettingsInternalLI(newPackage, reconciledPkg, allUsers, res);
+ }
+
+ private void updateSettingsInternalLI(AndroidPackage pkg, ReconciledPackage reconciledPkg,
+ int[] allUsers, PackageInstalledInfo res) {
+ Trace.traceBegin(TRACE_TAG_PACKAGE_MANAGER, "updateSettings");
+
+ final String pkgName = pkg.getPackageName();
+ final int[] installedForUsers = res.mOrigUsers;
+ final InstallArgs installArgs = reconciledPkg.mInstallArgs;
+ final int installReason = installArgs.mInstallReason;
+ InstallSource installSource = installArgs.mInstallSource;
+ final String installerPackageName = installSource.installerPackageName;
+
+ if (DEBUG_INSTALL) Slog.d(TAG, "New package installed in " + pkg.getPath());
+ synchronized (mPm.mLock) {
+ // For system-bundled packages, we assume that installing an upgraded version
+ // of the package implies that the user actually wants to run that new code,
+ // so we enable the package.
+ final PackageSetting ps = mPm.mSettings.getPackageLPr(pkgName);
+ final int userId = installArgs.mUser.getIdentifier();
+ if (ps != null) {
+ if (pkg.isSystem()) {
+ if (DEBUG_INSTALL) {
+ Slog.d(TAG, "Implicitly enabling system package on upgrade: " + pkgName);
+ }
+ // Enable system package for requested users
+ if (res.mOrigUsers != null) {
+ for (int origUserId : res.mOrigUsers) {
+ if (userId == UserHandle.USER_ALL || userId == origUserId) {
+ ps.setEnabled(COMPONENT_ENABLED_STATE_DEFAULT,
+ origUserId, installerPackageName);
+ }
+ }
+ }
+ // Also convey the prior install/uninstall state
+ if (allUsers != null && installedForUsers != null) {
+ for (int currentUserId : allUsers) {
+ final boolean installed = ArrayUtils.contains(
+ installedForUsers, currentUserId);
+ if (DEBUG_INSTALL) {
+ Slog.d(TAG, " user " + currentUserId + " => " + installed);
+ }
+ ps.setInstalled(installed, currentUserId);
+ }
+ // these install state changes will be persisted in the
+ // upcoming call to mSettings.writeLPr().
+ }
+
+ if (allUsers != null) {
+ for (int currentUserId : allUsers) {
+ ps.resetOverrideComponentLabelIcon(currentUserId);
+ }
+ }
+ }
+
+ // Retrieve the overlays for shared libraries of the package.
+ if (!ps.getPkgState().getUsesLibraryInfos().isEmpty()) {
+ for (SharedLibraryInfo sharedLib : ps.getPkgState().getUsesLibraryInfos()) {
+ for (int currentUserId : UserManagerService.getInstance().getUserIds()) {
+ if (!sharedLib.isDynamic()) {
+ // TODO(146804378): Support overlaying static shared libraries
+ continue;
+ }
+ final PackageSetting libPs = mPm.mSettings.getPackageLPr(
+ sharedLib.getPackageName());
+ if (libPs == null) {
+ continue;
+ }
+ ps.setOverlayPathsForLibrary(sharedLib.getName(),
+ libPs.getOverlayPaths(currentUserId), currentUserId);
+ }
+ }
+ }
+
+ // It's implied that when a user requests installation, they want the app to be
+ // installed and enabled. (This does not apply to USER_ALL, which here means only
+ // install on users for which the app is already installed).
+ if (userId != UserHandle.USER_ALL) {
+ ps.setInstalled(true, userId);
+ ps.setEnabled(COMPONENT_ENABLED_STATE_DEFAULT, userId, installerPackageName);
+ }
+
+ mPm.mSettings.addInstallerPackageNames(ps.getInstallSource());
+
+ // When replacing an existing package, preserve the original install reason for all
+ // users that had the package installed before. Similarly for uninstall reasons.
+ final Set<Integer> previousUserIds = new ArraySet<>();
+ if (res.mRemovedInfo != null && res.mRemovedInfo.mInstallReasons != null) {
+ final int installReasonCount = res.mRemovedInfo.mInstallReasons.size();
+ for (int i = 0; i < installReasonCount; i++) {
+ final int previousUserId = res.mRemovedInfo.mInstallReasons.keyAt(i);
+ final int previousInstallReason =
+ res.mRemovedInfo.mInstallReasons.valueAt(i);
+ ps.setInstallReason(previousInstallReason, previousUserId);
+ previousUserIds.add(previousUserId);
+ }
+ }
+ if (res.mRemovedInfo != null && res.mRemovedInfo.mUninstallReasons != null) {
+ for (int i = 0; i < res.mRemovedInfo.mUninstallReasons.size(); i++) {
+ final int previousUserId = res.mRemovedInfo.mUninstallReasons.keyAt(i);
+ final int previousReason = res.mRemovedInfo.mUninstallReasons.valueAt(i);
+ ps.setUninstallReason(previousReason, previousUserId);
+ }
+ }
+
+ // Set install reason for users that are having the package newly installed.
+ final int[] allUsersList = mPm.mUserManager.getUserIds();
+ if (userId == UserHandle.USER_ALL) {
+ // TODO(b/152629990): It appears that the package doesn't actually get newly
+ // installed in this case, so the installReason shouldn't get modified?
+ for (int currentUserId : allUsersList) {
+ if (!previousUserIds.contains(currentUserId)) {
+ ps.setInstallReason(installReason, currentUserId);
+ }
+ }
+ } else if (!previousUserIds.contains(userId)) {
+ ps.setInstallReason(installReason, userId);
+ }
+
+ // TODO(b/169721400): generalize Incremental States and create a Callback object
+ // that can be used for all the packages.
+ final String codePath = ps.getPathString();
+ if (IncrementalManager.isIncrementalPath(codePath)
+ && mPm.mIncrementalManager != null) {
+ mPm.mIncrementalManager.registerLoadingProgressCallback(codePath,
+ new IncrementalProgressListener(ps.getPackageName(), mPm));
+ }
+
+ // Ensure that the uninstall reason is UNKNOWN for users with the package installed.
+ for (int currentUserId : allUsersList) {
+ if (ps.getInstalled(currentUserId)) {
+ ps.setUninstallReason(UNINSTALL_REASON_UNKNOWN, currentUserId);
+ }
+ }
+
+ mPm.mSettings.writeKernelMappingLPr(ps);
+
+ final PermissionManagerServiceInternal.PackageInstalledParams.Builder
+ permissionParamsBuilder =
+ new PermissionManagerServiceInternal.PackageInstalledParams.Builder();
+ final boolean grantPermissions = (installArgs.mInstallFlags
+ & PackageManager.INSTALL_GRANT_RUNTIME_PERMISSIONS) != 0;
+ if (grantPermissions) {
+ final List<String> grantedPermissions =
+ installArgs.mInstallGrantPermissions != null
+ ? Arrays.asList(installArgs.mInstallGrantPermissions)
+ : pkg.getRequestedPermissions();
+ permissionParamsBuilder.setGrantedPermissions(grantedPermissions);
+ }
+ final boolean allowlistAllRestrictedPermissions =
+ (installArgs.mInstallFlags
+ & PackageManager.INSTALL_ALL_WHITELIST_RESTRICTED_PERMISSIONS) != 0;
+ final List<String> allowlistedRestrictedPermissions =
+ allowlistAllRestrictedPermissions ? pkg.getRequestedPermissions()
+ : installArgs.mAllowlistedRestrictedPermissions;
+ if (allowlistedRestrictedPermissions != null) {
+ permissionParamsBuilder.setAllowlistedRestrictedPermissions(
+ allowlistedRestrictedPermissions);
+ }
+ final int autoRevokePermissionsMode = installArgs.mAutoRevokePermissionsMode;
+ permissionParamsBuilder.setAutoRevokePermissionsMode(autoRevokePermissionsMode);
+ final ScanResult scanResult = reconciledPkg.mScanResult;
+ mPm.mPermissionManager.onPackageInstalled(pkg, scanResult.mPreviousAppId,
+ permissionParamsBuilder.build(), userId);
+ }
+ res.mName = pkgName;
+ res.mUid = pkg.getUid();
+ res.mPkg = pkg;
+ res.setReturnCode(PackageManager.INSTALL_SUCCEEDED);
+ //to update install status
+ Trace.traceBegin(TRACE_TAG_PACKAGE_MANAGER, "writeSettings");
+ mPm.writeSettingsLPrTEMP();
+ Trace.traceEnd(TRACE_TAG_PACKAGE_MANAGER);
+ }
+
+ Trace.traceEnd(TRACE_TAG_PACKAGE_MANAGER);
+ }
+
+ /**
+ * On successful install, executes remaining steps after commit completes and the package lock
+ * is released. These are typically more expensive or require calls to installd, which often
+ * locks on {@link com.android.server.pm.PackageManagerService.mLock}.
+ */
+ private void executePostCommitSteps(CommitRequest commitRequest) {
+ final ArraySet<IncrementalStorage> incrementalStorages = new ArraySet<>();
+ final AppDataHelper appDataHelper = new AppDataHelper(mPm);
+ for (ReconciledPackage reconciledPkg : commitRequest.mReconciledPackages.values()) {
+ final boolean instantApp = ((reconciledPkg.mScanResult.mRequest.mScanFlags
+ & SCAN_AS_INSTANT_APP) != 0);
+ final AndroidPackage pkg = reconciledPkg.mPkgSetting.getPkg();
+ final String packageName = pkg.getPackageName();
+ final String codePath = pkg.getPath();
+ final boolean onIncremental = mPm.mIncrementalManager != null
+ && isIncrementalPath(codePath);
+ if (onIncremental) {
+ IncrementalStorage storage = mPm.mIncrementalManager.openStorage(codePath);
+ if (storage == null) {
+ throw new IllegalArgumentException(
+ "Install: null storage for incremental package " + packageName);
+ }
+ incrementalStorages.add(storage);
+ }
+ int previousAppId = 0;
+ if (reconciledPkg.mScanResult.needsNewAppId()) {
+ // Only set previousAppId if the app is migrating out of shared UID
+ previousAppId = reconciledPkg.mScanResult.mPreviousAppId;
+ }
+ appDataHelper.prepareAppDataPostCommitLIF(pkg, previousAppId);
+ if (reconciledPkg.mPrepareResult.mClearCodeCache) {
+ appDataHelper.clearAppDataLIF(pkg, UserHandle.USER_ALL,
+ FLAG_STORAGE_DE | FLAG_STORAGE_CE | FLAG_STORAGE_EXTERNAL
+ | Installer.FLAG_CLEAR_CODE_CACHE_ONLY);
+ }
+ if (reconciledPkg.mPrepareResult.mReplace) {
+ mPm.getDexManager().notifyPackageUpdated(pkg.getPackageName(),
+ pkg.getBaseApkPath(), pkg.getSplitCodePaths());
+ }
+
+ // Prepare the application profiles for the new code paths.
+ // This needs to be done before invoking dexopt so that any install-time profile
+ // can be used for optimizations.
+ mPm.mArtManagerService.prepareAppProfiles(
+ pkg,
+ mPm.resolveUserIds(reconciledPkg.mInstallArgs.mUser.getIdentifier()),
+ /* updateReferenceProfileContent= */ true);
+
+ // Compute the compilation reason from the installation scenario.
+ final int compilationReason =
+ mPm.getDexManager().getCompilationReasonForInstallScenario(
+ reconciledPkg.mInstallArgs.mInstallScenario);
+
+ // Construct the DexoptOptions early to see if we should skip running dexopt.
+ //
+ // Do not run PackageDexOptimizer through the local performDexOpt
+ // method because `pkg` may not be in `mPackages` yet.
+ //
+ // Also, don't fail application installs if the dexopt step fails.
+ final boolean isBackupOrRestore =
+ reconciledPkg.mInstallArgs.mInstallReason == INSTALL_REASON_DEVICE_RESTORE
+ || reconciledPkg.mInstallArgs.mInstallReason
+ == INSTALL_REASON_DEVICE_SETUP;
+
+ final int dexoptFlags = DexoptOptions.DEXOPT_BOOT_COMPLETE
+ | DexoptOptions.DEXOPT_INSTALL_WITH_DEX_METADATA_FILE
+ | (isBackupOrRestore ? DexoptOptions.DEXOPT_FOR_RESTORE : 0);
+ DexoptOptions dexoptOptions =
+ new DexoptOptions(packageName, compilationReason, dexoptFlags);
+
+ // Check whether we need to dexopt the app.
+ //
+ // NOTE: it is IMPORTANT to call dexopt:
+ // - after doRename which will sync the package data from AndroidPackage and
+ // its corresponding ApplicationInfo.
+ // - after installNewPackageLIF or replacePackageLIF which will update result with the
+ // uid of the application (pkg.applicationInfo.uid).
+ // This update happens in place!
+ //
+ // We only need to dexopt if the package meets ALL of the following conditions:
+ // 1) it is not an instant app or if it is then dexopt is enabled via gservices.
+ // 2) it is not debuggable.
+ // 3) it is not on Incremental File System.
+ //
+ // Note that we do not dexopt instant apps by default. dexopt can take some time to
+ // complete, so we skip this step during installation. Instead, we'll take extra time
+ // the first time the instant app starts. It's preferred to do it this way to provide
+ // continuous progress to the useur instead of mysteriously blocking somewhere in the
+ // middle of running an instant app. The default behaviour can be overridden
+ // via gservices.
+ //
+ // Furthermore, dexopt may be skipped, depending on the install scenario and current
+ // state of the device.
+ //
+ // TODO(b/174695087): instantApp and onIncremental should be removed and their install
+ // path moved to SCENARIO_FAST.
+ final boolean performDexopt =
+ (!instantApp || android.provider.Settings.Global.getInt(
+ mPm.mContext.getContentResolver(),
+ android.provider.Settings.Global.INSTANT_APP_DEXOPT_ENABLED, 0) != 0)
+ && !pkg.isDebuggable()
+ && (!onIncremental)
+ && dexoptOptions.isCompilationEnabled();
+
+ if (performDexopt) {
+ // Compile the layout resources.
+ if (SystemProperties.getBoolean(PRECOMPILE_LAYOUTS, false)) {
+ Trace.traceBegin(TRACE_TAG_PACKAGE_MANAGER, "compileLayouts");
+ mPm.mViewCompiler.compileLayouts(pkg);
+ Trace.traceEnd(TRACE_TAG_PACKAGE_MANAGER);
+ }
+
+ Trace.traceBegin(TRACE_TAG_PACKAGE_MANAGER, "dexopt");
+ ScanResult result = reconciledPkg.mScanResult;
+
+ // This mirrors logic from commitReconciledScanResultLocked, where the library files
+ // needed for dexopt are assigned.
+ // TODO: Fix this to have 1 mutable PackageSetting for scan/install. If the previous
+ // setting needs to be passed to have a comparison, hide it behind an immutable
+ // interface. There's no good reason to have 3 different ways to access the real
+ // PackageSetting object, only one of which is actually correct.
+ PackageSetting realPkgSetting = result.mExistingSettingCopied
+ ? result.mRequest.mPkgSetting : result.mPkgSetting;
+ if (realPkgSetting == null) {
+ realPkgSetting = reconciledPkg.mPkgSetting;
+ }
+
+ // Unfortunately, the updated system app flag is only tracked on this PackageSetting
+ boolean isUpdatedSystemApp = reconciledPkg.mPkgSetting.getPkgState()
+ .isUpdatedSystemApp();
+
+ realPkgSetting.getPkgState().setUpdatedSystemApp(isUpdatedSystemApp);
+
+ mPm.mPackageDexOptimizer.performDexOpt(pkg, realPkgSetting,
+ null /* instructionSets */,
+ mPm.getOrCreateCompilerPackageStats(pkg),
+ mPm.getDexManager().getPackageUseInfoOrDefault(packageName),
+ dexoptOptions);
+ Trace.traceEnd(TRACE_TAG_PACKAGE_MANAGER);
+ }
+
+ // Notify BackgroundDexOptService that the package has been changed.
+ // If this is an update of a package which used to fail to compile,
+ // BackgroundDexOptService will remove it from its denylist.
+ // TODO: Layering violation
+ BackgroundDexOptService.getService().notifyPackageChanged(packageName);
+
+ notifyPackageChangeObserversOnUpdate(reconciledPkg);
+ }
+ waitForNativeBinariesExtraction(incrementalStorages);
+ }
+
+ private void notifyPackageChangeObserversOnUpdate(ReconciledPackage reconciledPkg) {
+ final PackageSetting pkgSetting = reconciledPkg.mPkgSetting;
+ final PackageInstalledInfo pkgInstalledInfo = reconciledPkg.mInstallResult;
+ final PackageRemovedInfo pkgRemovedInfo = pkgInstalledInfo.mRemovedInfo;
+
+ PackageChangeEvent pkgChangeEvent = new PackageChangeEvent();
+ pkgChangeEvent.packageName = pkgSetting.getPkg().getPackageName();
+ pkgChangeEvent.version = pkgSetting.getVersionCode();
+ pkgChangeEvent.lastUpdateTimeMillis = pkgSetting.getLastUpdateTime();
+ pkgChangeEvent.newInstalled = (pkgRemovedInfo == null || !pkgRemovedInfo.mIsUpdate);
+ pkgChangeEvent.dataRemoved = (pkgRemovedInfo != null && pkgRemovedInfo.mDataRemoved);
+ pkgChangeEvent.isDeleted = false;
+
+ mPm.notifyPackageChangeObservers(pkgChangeEvent);
+ }
+
+ private static void waitForNativeBinariesExtraction(
+ ArraySet<IncrementalStorage> incrementalStorages) {
+ if (incrementalStorages.isEmpty()) {
+ return;
+ }
+ try {
+ // Native library extraction may take very long time: each page could potentially
+ // wait for either 10s or 100ms (adb vs non-adb data loader), and that easily adds
+ // up to a full watchdog timeout of 1 min, killing the system after that. It doesn't
+ // make much sense as blocking here doesn't lock up the framework, but only blocks
+ // the installation session and the following ones.
+ Watchdog.getInstance().pauseWatchingCurrentThread("native_lib_extract");
+ for (int i = 0; i < incrementalStorages.size(); ++i) {
+ IncrementalStorage storage = incrementalStorages.valueAtUnchecked(i);
+ storage.waitForNativeBinariesExtraction();
+ }
+ } finally {
+ Watchdog.getInstance().resumeWatchingCurrentThread("native_lib_extract");
+ }
+ }
+
+ public int installLocationPolicy(PackageInfoLite pkgLite, int installFlags) {
+ String packageName = pkgLite.packageName;
+ int installLocation = pkgLite.installLocation;
+ // reader
+ synchronized (mPm.mLock) {
+ // Currently installed package which the new package is attempting to replace or
+ // null if no such package is installed.
+ AndroidPackage installedPkg = mPm.mPackages.get(packageName);
+
+ if (installedPkg != null) {
+ if ((installFlags & PackageManager.INSTALL_REPLACE_EXISTING) != 0) {
+ // Check for updated system application.
+ if (installedPkg.isSystem()) {
+ return PackageHelper.RECOMMEND_INSTALL_INTERNAL;
+ } else {
+ // If current upgrade specifies particular preference
+ if (installLocation == PackageInfo.INSTALL_LOCATION_INTERNAL_ONLY) {
+ // Application explicitly specified internal.
+ return PackageHelper.RECOMMEND_INSTALL_INTERNAL;
+ } else if (
+ installLocation == PackageInfo.INSTALL_LOCATION_PREFER_EXTERNAL) {
+ // App explicitly prefers external. Let policy decide
+ } else {
+ // Prefer previous location
+ if (installedPkg.isExternalStorage()) {
+ return PackageHelper.RECOMMEND_INSTALL_EXTERNAL;
+ }
+ return PackageHelper.RECOMMEND_INSTALL_INTERNAL;
+ }
+ }
+ } else {
+ // Invalid install. Return error code
+ return PackageHelper.RECOMMEND_FAILED_ALREADY_EXISTS;
+ }
+ }
+ }
+ return pkgLite.recommendedInstallLocation;
+ }
+
+ Pair<Integer, String> verifyReplacingVersionCode(PackageInfoLite pkgLite,
+ long requiredInstalledVersionCode, int installFlags) {
+ if ((installFlags & PackageManager.INSTALL_APEX) != 0) {
+ return verifyReplacingVersionCodeForApex(
+ pkgLite, requiredInstalledVersionCode, installFlags);
+ }
+
+ String packageName = pkgLite.packageName;
+ synchronized (mPm.mLock) {
+ // Package which currently owns the data that the new package will own if installed.
+ // If an app is uninstalled while keeping data (e.g. adb uninstall -k), installedPkg
+ // will be null whereas dataOwnerPkg will contain information about the package
+ // which was uninstalled while keeping its data.
+ AndroidPackage dataOwnerPkg = mPm.mPackages.get(packageName);
+ if (dataOwnerPkg == null) {
+ PackageSetting ps = mPm.mSettings.getPackageLPr(packageName);
+ if (ps != null) {
+ dataOwnerPkg = ps.getPkg();
+ }
+ }
+
+ if (requiredInstalledVersionCode != PackageManager.VERSION_CODE_HIGHEST) {
+ if (dataOwnerPkg == null) {
+ String errorMsg = "Required installed version code was "
+ + requiredInstalledVersionCode
+ + " but package is not installed";
+ Slog.w(TAG, errorMsg);
+ return Pair.create(
+ PackageManager.INSTALL_FAILED_WRONG_INSTALLED_VERSION, errorMsg);
+ }
+
+ if (dataOwnerPkg.getLongVersionCode() != requiredInstalledVersionCode) {
+ String errorMsg = "Required installed version code was "
+ + requiredInstalledVersionCode
+ + " but actual installed version is "
+ + dataOwnerPkg.getLongVersionCode();
+ Slog.w(TAG, errorMsg);
+ return Pair.create(
+ PackageManager.INSTALL_FAILED_WRONG_INSTALLED_VERSION, errorMsg);
+ }
+ }
+
+ if (dataOwnerPkg != null) {
+ if (!PackageManagerServiceUtils.isDowngradePermitted(installFlags,
+ dataOwnerPkg.isDebuggable())) {
+ try {
+ checkDowngrade(dataOwnerPkg, pkgLite);
+ } catch (PackageManagerException e) {
+ String errorMsg = "Downgrade detected: " + e.getMessage();
+ Slog.w(TAG, errorMsg);
+ return Pair.create(
+ PackageManager.INSTALL_FAILED_VERSION_DOWNGRADE, errorMsg);
+ }
+ }
+ }
+ }
+ return Pair.create(PackageManager.INSTALL_SUCCEEDED, null);
+ }
+
+ private Pair<Integer, String> verifyReplacingVersionCodeForApex(PackageInfoLite pkgLite,
+ long requiredInstalledVersionCode, int installFlags) {
+ String packageName = pkgLite.packageName;
+
+ final PackageInfo activePackage = mPm.mApexManager.getPackageInfo(packageName,
+ ApexManager.MATCH_ACTIVE_PACKAGE);
+ if (activePackage == null) {
+ String errorMsg = "Attempting to install new APEX package " + packageName;
+ Slog.w(TAG, errorMsg);
+ return Pair.create(PackageManager.INSTALL_FAILED_PACKAGE_CHANGED, errorMsg);
+ }
+
+ final long activeVersion = activePackage.getLongVersionCode();
+ if (requiredInstalledVersionCode != PackageManager.VERSION_CODE_HIGHEST
+ && activeVersion != requiredInstalledVersionCode) {
+ String errorMsg = "Installed version of APEX package " + packageName
+ + " does not match required. Active version: " + activeVersion
+ + " required: " + requiredInstalledVersionCode;
+ Slog.w(TAG, errorMsg);
+ return Pair.create(PackageManager.INSTALL_FAILED_WRONG_INSTALLED_VERSION, errorMsg);
+ }
+
+ final boolean isAppDebuggable = (activePackage.applicationInfo.flags
+ & ApplicationInfo.FLAG_DEBUGGABLE) != 0;
+ final long newVersionCode = pkgLite.getLongVersionCode();
+ if (!PackageManagerServiceUtils.isDowngradePermitted(installFlags, isAppDebuggable)
+ && newVersionCode < activeVersion) {
+ String errorMsg = "Downgrade of APEX package " + packageName
+ + " is not allowed. Active version: " + activeVersion
+ + " attempted: " + newVersionCode;
+ Slog.w(TAG, errorMsg);
+ return Pair.create(PackageManager.INSTALL_FAILED_VERSION_DOWNGRADE, errorMsg);
+ }
+
+ return Pair.create(PackageManager.INSTALL_SUCCEEDED, null);
+ }
+
+ /**
+ * Check and throw if the given before/after packages would be considered a
+ * downgrade.
+ */
+ private static void checkDowngrade(AndroidPackage before, PackageInfoLite after)
+ throws PackageManagerException {
+ if (after.getLongVersionCode() < before.getLongVersionCode()) {
+ throw new PackageManagerException(INSTALL_FAILED_VERSION_DOWNGRADE,
+ "Update version code " + after.versionCode + " is older than current "
+ + before.getLongVersionCode());
+ } else if (after.getLongVersionCode() == before.getLongVersionCode()) {
+ if (after.baseRevisionCode < before.getBaseRevisionCode()) {
+ throw new PackageManagerException(INSTALL_FAILED_VERSION_DOWNGRADE,
+ "Update base revision code " + after.baseRevisionCode
+ + " is older than current " + before.getBaseRevisionCode());
+ }
+
+ if (!ArrayUtils.isEmpty(after.splitNames)) {
+ for (int i = 0; i < after.splitNames.length; i++) {
+ final String splitName = after.splitNames[i];
+ final int j = ArrayUtils.indexOf(before.getSplitNames(), splitName);
+ if (j != -1) {
+ if (after.splitRevisionCodes[i] < before.getSplitRevisionCodes()[j]) {
+ throw new PackageManagerException(INSTALL_FAILED_VERSION_DOWNGRADE,
+ "Update split " + splitName + " revision code "
+ + after.splitRevisionCodes[i]
+ + " is older than current "
+ + before.getSplitRevisionCodes()[j]);
+ }
+ }
+ }
+ }
+ }
+ }
+
+ int getUidForVerifier(VerifierInfo verifierInfo) {
+ synchronized (mPm.mLock) {
+ final AndroidPackage pkg = mPm.mPackages.get(verifierInfo.packageName);
+ if (pkg == null) {
+ return -1;
+ } else if (pkg.getSigningDetails().getSignatures().length != 1) {
+ Slog.i(TAG, "Verifier package " + verifierInfo.packageName
+ + " has more than one signature; ignoring");
+ return -1;
+ }
+
+ /*
+ * If the public key of the package's signature does not match
+ * our expected public key, then this is a different package and
+ * we should skip.
+ */
+
+ final byte[] expectedPublicKey;
+ try {
+ final Signature verifierSig = pkg.getSigningDetails().getSignatures()[0];
+ final PublicKey publicKey = verifierSig.getPublicKey();
+ expectedPublicKey = publicKey.getEncoded();
+ } catch (CertificateException e) {
+ return -1;
+ }
+
+ final byte[] actualPublicKey = verifierInfo.publicKey.getEncoded();
+
+ if (!Arrays.equals(actualPublicKey, expectedPublicKey)) {
+ Slog.i(TAG, "Verifier package " + verifierInfo.packageName
+ + " does not have the expected public key; ignoring");
+ return -1;
+ }
+
+ return pkg.getUid();
+ }
+ }
+
+ /**
+ * Check whether or not package verification has been enabled.
+ *
+ * @return true if verification should be performed
+ */
+ boolean isVerificationEnabled(PackageInfoLite pkgInfoLite, int userId, int installFlags,
+ int installerUid) {
+ if (!DEFAULT_VERIFY_ENABLE) {
+ return false;
+ }
+
+ // Check if installing from ADB
+ if ((installFlags & PackageManager.INSTALL_FROM_ADB) != 0) {
+ if (mPm.isUserRestricted(userId, UserManager.ENSURE_VERIFY_APPS)) {
+ return true;
+ }
+ // Check if the developer wants to skip verification for ADB installs
+ if ((installFlags & PackageManager.INSTALL_DISABLE_VERIFICATION) != 0) {
+ synchronized (mPm.mLock) {
+ if (mPm.mSettings.getPackageLPr(pkgInfoLite.packageName) == null) {
+ // Always verify fresh install
+ return true;
+ }
+ }
+ // Only skip when apk is debuggable
+ return !pkgInfoLite.debuggable;
+ }
+ return android.provider.Settings.Global.getInt(mPm.mContext.getContentResolver(),
+ android.provider.Settings.Global.PACKAGE_VERIFIER_INCLUDE_ADB, 1) != 0;
+ }
+
+ // only when not installed from ADB, skip verification for instant apps when
+ // the installer and verifier are the same.
+ if ((installFlags & PackageManager.INSTALL_INSTANT_APP) != 0) {
+ if (mPm.mInstantAppInstallerActivity != null
+ && mPm.mInstantAppInstallerActivity.packageName.equals(
+ mPm.mRequiredVerifierPackage)) {
+ try {
+ mPm.mInjector.getSystemService(AppOpsManager.class)
+ .checkPackage(installerUid, mPm.mRequiredVerifierPackage);
+ if (DEBUG_VERIFY) {
+ Slog.i(TAG, "disable verification for instant app");
+ }
+ return false;
+ } catch (SecurityException ignore) { }
+ }
+ }
+ return true;
+ }
+
+ public void sendPendingBroadcasts() {
+ String[] packages;
+ ArrayList<String>[] components;
+ int size = 0;
+ int[] uids;
+ Process.setThreadPriority(Process.THREAD_PRIORITY_DEFAULT);
+
+ synchronized (mPm.mLock) {
+ size = mPm.mPendingBroadcasts.size();
+ if (size <= 0) {
+ // Nothing to be done. Just return
+ return;
+ }
+ packages = new String[size];
+ components = new ArrayList[size];
+ uids = new int[size];
+ int i = 0; // filling out the above arrays
+
+ for (int n = 0; n < mPm.mPendingBroadcasts.userIdCount(); n++) {
+ final int packageUserId = mPm.mPendingBroadcasts.userIdAt(n);
+ final ArrayMap<String, ArrayList<String>> componentsToBroadcast =
+ mPm.mPendingBroadcasts.packagesForUserId(packageUserId);
+ final int numComponents = componentsToBroadcast.size();
+ for (int index = 0; i < size && index < numComponents; index++) {
+ packages[i] = componentsToBroadcast.keyAt(index);
+ components[i] = componentsToBroadcast.valueAt(index);
+ final PackageSetting ps = mPm.mSettings.getPackageLPr(packages[i]);
+ uids[i] = (ps != null)
+ ? UserHandle.getUid(packageUserId, ps.getAppId())
+ : -1;
+ i++;
+ }
+ }
+ size = i;
+ mPm.mPendingBroadcasts.clear();
+ }
+ // Send broadcasts
+ for (int i = 0; i < size; i++) {
+ mPm.sendPackageChangedBroadcast(packages[i], true /* dontKillApp */,
+ components[i], uids[i], null /* reason */);
+ }
+ Process.setThreadPriority(Process.THREAD_PRIORITY_BACKGROUND);
+ }
+
+ void handlePackagePostInstall(PackageInstalledInfo res, InstallArgs installArgs,
+ boolean launchedForRestore) {
+ final boolean killApp =
+ (installArgs.mInstallFlags & PackageManager.INSTALL_DONT_KILL_APP) == 0;
+ final boolean virtualPreload =
+ ((installArgs.mInstallFlags & PackageManager.INSTALL_VIRTUAL_PRELOAD) != 0);
+ final String installerPackage = installArgs.mInstallSource.installerPackageName;
+ final IPackageInstallObserver2 installObserver = installArgs.mObserver;
+ final int dataLoaderType = installArgs.mDataLoaderType;
+ final boolean succeeded = res.mReturnCode == PackageManager.INSTALL_SUCCEEDED;
+ final boolean update = res.mRemovedInfo != null && res.mRemovedInfo.mRemovedPackage != null;
+ final String packageName = res.mName;
+ final PackageStateInternal pkgSetting =
+ succeeded ? mPm.getPackageStateInternal(packageName) : null;
+ final boolean removedBeforeUpdate = (pkgSetting == null)
+ || (pkgSetting.isSystem() && !pkgSetting.getPath().getPath().equals(
+ res.mPkg.getPath()));
+ if (succeeded && removedBeforeUpdate) {
+ Slog.e(TAG, packageName + " was removed before handlePackagePostInstall "
+ + "could be executed");
+ res.mReturnCode = INSTALL_FAILED_PACKAGE_CHANGED;
+ res.mReturnMsg = "Package was removed before install could complete.";
+
+ // Remove the update failed package's older resources safely now
+ InstallArgs args = res.mRemovedInfo != null ? res.mRemovedInfo.mArgs : null;
+ if (args != null) {
+ synchronized (mPm.mInstallLock) {
+ args.doPostDeleteLI(true);
+ }
+ }
+ mPm.notifyInstallObserver(res, installObserver);
+ return;
+ }
+
+ if (succeeded) {
+ // Clear the uid cache after we installed a new package.
+ mPm.mPerUidReadTimeoutsCache = null;
+
+ // Send the removed broadcasts
+ if (res.mRemovedInfo != null) {
+ res.mRemovedInfo.sendPackageRemovedBroadcasts(killApp, false /*removedBySystem*/);
+ }
+
+ final String installerPackageName =
+ res.mInstallerPackageName != null
+ ? res.mInstallerPackageName
+ : res.mRemovedInfo != null
+ ? res.mRemovedInfo.mInstallerPackageName
+ : null;
+
+ synchronized (mPm.mLock) {
+ mPm.mInstantAppRegistry.onPackageInstalledLPw(res.mPkg, res.mNewUsers);
+ }
+
+ // Determine the set of users who are adding this package for
+ // the first time vs. those who are seeing an update.
+ int[] firstUserIds = EMPTY_INT_ARRAY;
+ int[] firstInstantUserIds = EMPTY_INT_ARRAY;
+ int[] updateUserIds = EMPTY_INT_ARRAY;
+ int[] instantUserIds = EMPTY_INT_ARRAY;
+ final boolean allNewUsers = res.mOrigUsers == null || res.mOrigUsers.length == 0;
+ for (int newUser : res.mNewUsers) {
+ final boolean isInstantApp = pkgSetting.getUserStateOrDefault(newUser)
+ .isInstantApp();
+ if (allNewUsers) {
+ if (isInstantApp) {
+ firstInstantUserIds = ArrayUtils.appendInt(firstInstantUserIds, newUser);
+ } else {
+ firstUserIds = ArrayUtils.appendInt(firstUserIds, newUser);
+ }
+ continue;
+ }
+ boolean isNew = true;
+ for (int origUser : res.mOrigUsers) {
+ if (origUser == newUser) {
+ isNew = false;
+ break;
+ }
+ }
+ if (isNew) {
+ if (isInstantApp) {
+ firstInstantUserIds = ArrayUtils.appendInt(firstInstantUserIds, newUser);
+ } else {
+ firstUserIds = ArrayUtils.appendInt(firstUserIds, newUser);
+ }
+ } else {
+ if (isInstantApp) {
+ instantUserIds = ArrayUtils.appendInt(instantUserIds, newUser);
+ } else {
+ updateUserIds = ArrayUtils.appendInt(updateUserIds, newUser);
+ }
+ }
+ }
+
+ // Send installed broadcasts if the package is not a static shared lib.
+ if (res.mPkg.getStaticSharedLibName() == null) {
+ mPm.mProcessLoggingHandler.invalidateBaseApkHash(res.mPkg.getBaseApkPath());
+
+ // Send added for users that see the package for the first time
+ // sendPackageAddedForNewUsers also deals with system apps
+ int appId = UserHandle.getAppId(res.mUid);
+ boolean isSystem = res.mPkg.isSystem();
+ mPm.sendPackageAddedForNewUsers(packageName, isSystem || virtualPreload,
+ virtualPreload /*startReceiver*/, appId, firstUserIds, firstInstantUserIds,
+ dataLoaderType);
+
+ // Send added for users that don't see the package for the first time
+ Bundle extras = new Bundle(1);
+ extras.putInt(Intent.EXTRA_UID, res.mUid);
+ if (update) {
+ extras.putBoolean(Intent.EXTRA_REPLACING, true);
+ }
+ extras.putInt(PackageInstaller.EXTRA_DATA_LOADER_TYPE, dataLoaderType);
+ // Send to all running apps.
+ final SparseArray<int[]> newBroadcastAllowList;
+ synchronized (mPm.mLock) {
+ newBroadcastAllowList = mPm.mAppsFilter.getVisibilityAllowList(
+ mPm.getPackageStateInternal(packageName, Process.SYSTEM_UID),
+ updateUserIds, mPm.mSettings.getPackagesLocked());
+ }
+ mPm.sendPackageBroadcast(Intent.ACTION_PACKAGE_ADDED, packageName,
+ extras, 0 /*flags*/,
+ null /*targetPackage*/, null /*finishedReceiver*/,
+ updateUserIds, instantUserIds, newBroadcastAllowList, null);
+ if (installerPackageName != null) {
+ // Send to the installer, even if it's not running.
+ mPm.sendPackageBroadcast(Intent.ACTION_PACKAGE_ADDED, packageName,
+ extras, 0 /*flags*/,
+ installerPackageName, null /*finishedReceiver*/,
+ updateUserIds, instantUserIds, null /* broadcastAllowList */, null);
+ }
+ // if the required verifier is defined, but, is not the installer of record
+ // for the package, it gets notified
+ final boolean notifyVerifier = mPm.mRequiredVerifierPackage != null
+ && !mPm.mRequiredVerifierPackage.equals(installerPackageName);
+ if (notifyVerifier) {
+ mPm.sendPackageBroadcast(Intent.ACTION_PACKAGE_ADDED, packageName,
+ extras, 0 /*flags*/,
+ mPm.mRequiredVerifierPackage, null /*finishedReceiver*/,
+ updateUserIds, instantUserIds, null /* broadcastAllowList */, null);
+ }
+ // If package installer is defined, notify package installer about new
+ // app installed
+ if (mPm.mRequiredInstallerPackage != null) {
+ mPm.sendPackageBroadcast(Intent.ACTION_PACKAGE_ADDED, packageName,
+ extras, Intent.FLAG_RECEIVER_INCLUDE_BACKGROUND /*flags*/,
+ mPm.mRequiredInstallerPackage, null /*finishedReceiver*/,
+ firstUserIds, instantUserIds, null /* broadcastAllowList */, null);
+ }
+
+ // Send replaced for users that don't see the package for the first time
+ if (update) {
+ mPm.sendPackageBroadcast(Intent.ACTION_PACKAGE_REPLACED,
+ packageName, extras, 0 /*flags*/,
+ null /*targetPackage*/, null /*finishedReceiver*/,
+ updateUserIds, instantUserIds, res.mRemovedInfo.mBroadcastAllowList,
+ null);
+ if (installerPackageName != null) {
+ mPm.sendPackageBroadcast(Intent.ACTION_PACKAGE_REPLACED, packageName,
+ extras, 0 /*flags*/,
+ installerPackageName, null /*finishedReceiver*/,
+ updateUserIds, instantUserIds, null /*broadcastAllowList*/, null);
+ }
+ if (notifyVerifier) {
+ mPm.sendPackageBroadcast(Intent.ACTION_PACKAGE_REPLACED, packageName,
+ extras, 0 /*flags*/,
+ mPm.mRequiredVerifierPackage, null /*finishedReceiver*/,
+ updateUserIds, instantUserIds, null /*broadcastAllowList*/, null);
+ }
+ mPm.sendPackageBroadcast(Intent.ACTION_MY_PACKAGE_REPLACED,
+ null /*package*/, null /*extras*/, 0 /*flags*/,
+ packageName /*targetPackage*/,
+ null /*finishedReceiver*/, updateUserIds, instantUserIds,
+ null /*broadcastAllowList*/,
+ mBroadcastHelper.getTemporaryAppAllowlistBroadcastOptions(
+ REASON_PACKAGE_REPLACED).toBundle());
+ } else if (launchedForRestore && !res.mPkg.isSystem()) {
+ // First-install and we did a restore, so we're responsible for the
+ // first-launch broadcast.
+ if (DEBUG_BACKUP) {
+ Slog.i(TAG, "Post-restore of " + packageName
+ + " sending FIRST_LAUNCH in " + Arrays.toString(firstUserIds));
+ }
+ mBroadcastHelper.sendFirstLaunchBroadcast(packageName, installerPackage,
+ firstUserIds, firstInstantUserIds);
+ }
+
+ // Send broadcast package appeared if external for all users
+ if (res.mPkg.isExternalStorage()) {
+ if (!update) {
+ final StorageManager storage = mPm.mInjector.getSystemService(
+ StorageManager.class);
+ VolumeInfo volume =
+ storage.findVolumeByUuid(
+ StorageManager.convert(
+ res.mPkg.getVolumeUuid()).toString());
+ int packageExternalStorageType =
+ PackageManagerServiceUtils.getPackageExternalStorageType(volume,
+ res.mPkg.isExternalStorage());
+ // If the package was installed externally, log it.
+ if (packageExternalStorageType != StorageEnums.UNKNOWN) {
+ FrameworkStatsLog.write(
+ FrameworkStatsLog.APP_INSTALL_ON_EXTERNAL_STORAGE_REPORTED,
+ packageExternalStorageType, packageName);
+ }
+ }
+ if (DEBUG_INSTALL) {
+ Slog.i(TAG, "upgrading pkg " + res.mPkg + " is external");
+ }
+ final int[] uidArray = new int[]{res.mPkg.getUid()};
+ ArrayList<String> pkgList = new ArrayList<>(1);
+ pkgList.add(packageName);
+ mBroadcastHelper.sendResourcesChangedBroadcast(
+ true, true, pkgList, uidArray, null);
+ }
+ } else if (!ArrayUtils.isEmpty(res.mLibraryConsumers)) { // if static shared lib
+ for (int i = 0; i < res.mLibraryConsumers.size(); i++) {
+ AndroidPackage pkg = res.mLibraryConsumers.get(i);
+ // send broadcast that all consumers of the static shared library have changed
+ mPm.sendPackageChangedBroadcast(pkg.getPackageName(), false /* dontKillApp */,
+ new ArrayList<>(Collections.singletonList(pkg.getPackageName())),
+ pkg.getUid(), null);
+ }
+ }
+
+ // Work that needs to happen on first install within each user
+ if (firstUserIds.length > 0) {
+ for (int userId : firstUserIds) {
+ mPm.restorePermissionsAndUpdateRolesForNewUserInstall(packageName,
+ userId);
+ }
+ }
+
+ if (allNewUsers && !update) {
+ mPm.notifyPackageAdded(packageName, res.mUid);
+ } else {
+ mPm.notifyPackageChanged(packageName, res.mUid);
+ }
+
+ // Log current value of "unknown sources" setting
+ EventLog.writeEvent(EventLogTags.UNKNOWN_SOURCES_ENABLED,
+ getUnknownSourcesSettings());
+
+ // Remove the replaced package's older resources safely now
+ InstallArgs args = res.mRemovedInfo != null ? res.mRemovedInfo.mArgs : null;
+ if (args != null) {
+ if (!killApp) {
+ // If we didn't kill the app, defer the deletion of code/resource files, since
+ // they may still be in use by the running application. This mitigates problems
+ // in cases where resources or code is loaded by a new Activity before
+ // ApplicationInfo changes have propagated to all application threads.
+ mPm.scheduleDeferredNoKillPostDelete(args);
+ } else {
+ synchronized (mPm.mInstallLock) {
+ args.doPostDeleteLI(true);
+ }
+ }
+ } else {
+ // Force a gc to clear up things. Ask for a background one, it's fine to go on
+ // and not block here.
+ VMRuntime.getRuntime().requestConcurrentGC();
+ }
+
+ // Notify DexManager that the package was installed for new users.
+ // The updated users should already be indexed and the package code paths
+ // should not change.
+ // Don't notify the manager for ephemeral apps as they are not expected to
+ // survive long enough to benefit of background optimizations.
+ for (int userId : firstUserIds) {
+ PackageInfo info = mPm.getPackageInfo(packageName, /*flags*/ 0, userId);
+ // There's a race currently where some install events may interleave with an
+ // uninstall. This can lead to package info being null (b/36642664).
+ if (info != null) {
+ mPm.getDexManager().notifyPackageInstalled(info, userId);
+ }
+ }
+ }
+
+ final boolean deferInstallObserver = succeeded && update && !killApp;
+ if (deferInstallObserver) {
+ mPm.scheduleDeferredNoKillInstallObserver(res, installObserver);
+ } else {
+ mPm.notifyInstallObserver(res, installObserver);
+ }
+
+ // Prune unused static shared libraries which have been cached a period of time
+ mPm.schedulePruneUnusedStaticSharedLibraries(true /* delay */);
+
+ // Log tracing if needed
+ if (installArgs.mTraceMethod != null) {
+ Trace.asyncTraceEnd(TRACE_TAG_PACKAGE_MANAGER, installArgs.mTraceMethod,
+ installArgs.mTraceCookie);
+ }
+ }
+
+ /**
+ * Get the "allow unknown sources" setting.
+ *
+ * @return the current "allow unknown sources" setting
+ */
+ private int getUnknownSourcesSettings() {
+ return android.provider.Settings.Secure.getIntForUser(mPm.mContext.getContentResolver(),
+ android.provider.Settings.Secure.INSTALL_NON_MARKET_APPS,
+ -1, UserHandle.USER_SYSTEM);
+ }
+
+ /**
+ * Uncompress and install stub applications.
+ * <p>In order to save space on the system partition, some applications are shipped in a
+ * compressed form. In addition the compressed bits for the full application, the
+ * system image contains a tiny stub comprised of only the Android manifest.
+ * <p>During the first boot, attempt to uncompress and install the full application. If
+ * the application can't be installed for any reason, disable the stub and prevent
+ * uncompressing the full application during future boots.
+ * <p>In order to forcefully attempt an installation of a full application, go to app
+ * settings and enable the application.
+ */
+ @GuardedBy({"mPm.mLock", "mPm.mInstallLock"})
+ void installSystemStubPackages(@NonNull List<String> systemStubPackageNames,
+ @PackageManagerService.ScanFlags int scanFlags) {
+ for (int i = systemStubPackageNames.size() - 1; i >= 0; --i) {
+ final String packageName = systemStubPackageNames.get(i);
+ // skip if the system package is already disabled
+ if (mPm.mSettings.isDisabledSystemPackageLPr(packageName)) {
+ systemStubPackageNames.remove(i);
+ continue;
+ }
+ // skip if the package isn't installed (?!); this should never happen
+ final AndroidPackage pkg = mPm.mPackages.get(packageName);
+ if (pkg == null) {
+ systemStubPackageNames.remove(i);
+ continue;
+ }
+ // skip if the package has been disabled by the user
+ final PackageSetting ps = mPm.mSettings.getPackageLPr(packageName);
+ if (ps != null) {
+ final int enabledState = ps.getEnabled(UserHandle.USER_SYSTEM);
+ if (enabledState == PackageManager.COMPONENT_ENABLED_STATE_DISABLED_USER) {
+ systemStubPackageNames.remove(i);
+ continue;
+ }
+ }
+
+ // install the package to replace the stub on /system
+ try {
+ installStubPackageLI(pkg, 0, scanFlags);
+ ps.setEnabled(PackageManager.COMPONENT_ENABLED_STATE_DEFAULT,
+ UserHandle.USER_SYSTEM, "android");
+ systemStubPackageNames.remove(i);
+ } catch (PackageManagerException e) {
+ Slog.e(TAG, "Failed to parse uncompressed system package: " + e.getMessage());
+ }
+
+ // any failed attempt to install the package will be cleaned up later
+ }
+
+ // disable any stub still left; these failed to install the full application
+ for (int i = systemStubPackageNames.size() - 1; i >= 0; --i) {
+ final String pkgName = systemStubPackageNames.get(i);
+ final PackageSetting ps = mPm.mSettings.getPackageLPr(pkgName);
+ ps.setEnabled(PackageManager.COMPONENT_ENABLED_STATE_DISABLED,
+ UserHandle.USER_SYSTEM, "android");
+ logCriticalInfo(Log.ERROR, "Stub disabled; pkg: " + pkgName);
+ }
+ }
+
+ /**
+ * Extract, install and enable a stub package.
+ * <p>If the compressed file can not be extracted / installed for any reason, the stub
+ * APK will be installed and the package will be disabled. To recover from this situation,
+ * the user will need to go into system settings and re-enable the package.
+ */
+ @GuardedBy({"mPm.mLock", "mPm.mInstallLock"})
+ boolean enableCompressedPackage(AndroidPackage stubPkg,
+ @NonNull PackageSetting stubPkgSetting) {
+ final int parseFlags = mPm.getDefParseFlags() | ParsingPackageUtils.PARSE_CHATTY
+ | ParsingPackageUtils.PARSE_ENFORCE_CODE;
+ synchronized (mPm.mInstallLock) {
+ final AndroidPackage pkg;
+ try (PackageFreezer freezer =
+ mPm.freezePackage(stubPkg.getPackageName(), "setEnabledSetting")) {
+ pkg = installStubPackageLI(stubPkg, parseFlags, 0 /*scanFlags*/);
+ synchronized (mPm.mLock) {
+ mAppDataHelper.prepareAppDataAfterInstallLIF(pkg);
+ try {
+ mPm.updateSharedLibrariesLocked(pkg, stubPkgSetting, null, null,
+ Collections.unmodifiableMap(mPm.mPackages));
+ } catch (PackageManagerException e) {
+ Slog.w(TAG, "updateAllSharedLibrariesLPw failed: ", e);
+ }
+ mPm.mPermissionManager.onPackageInstalled(pkg,
+ Process.INVALID_UID /* previousAppId */,
+ PermissionManagerServiceInternal.PackageInstalledParams.DEFAULT,
+ UserHandle.USER_ALL);
+ mPm.writeSettingsLPrTEMP();
+ }
+ } catch (PackageManagerException e) {
+ // Whoops! Something went very wrong; roll back to the stub and disable the package
+ try (PackageFreezer freezer =
+ mPm.freezePackage(stubPkg.getPackageName(), "setEnabledSetting")) {
+ synchronized (mPm.mLock) {
+ // NOTE: Ensure the system package is enabled; even for a compressed stub.
+ // If we don't, installing the system package fails during scan
+ mPm.mSettings.enableSystemPackageLPw(stubPkg.getPackageName());
+ }
+ installPackageFromSystemLIF(stubPkg.getPath(),
+ mPm.mUserManager.getUserIds() /*allUserHandles*/,
+ null /*origUserHandles*/,
+ true /*writeSettings*/);
+ } catch (PackageManagerException pme) {
+ // Serious WTF; we have to be able to install the stub
+ Slog.wtf(TAG, "Failed to restore system package:" + stubPkg.getPackageName(),
+ pme);
+ } finally {
+ // Disable the package; the stub by itself is not runnable
+ synchronized (mPm.mLock) {
+ final PackageSetting stubPs = mPm.mSettings.getPackageLPr(
+ stubPkg.getPackageName());
+ if (stubPs != null) {
+ stubPs.setEnabled(COMPONENT_ENABLED_STATE_DISABLED,
+ UserHandle.USER_SYSTEM, "android");
+ }
+ mPm.writeSettingsLPrTEMP();
+ }
+ }
+ return false;
+ }
+ mAppDataHelper.clearAppDataLIF(pkg, UserHandle.USER_ALL,
+ FLAG_STORAGE_DE | FLAG_STORAGE_CE | FLAG_STORAGE_EXTERNAL
+ | Installer.FLAG_CLEAR_CODE_CACHE_ONLY);
+ mPm.getDexManager().notifyPackageUpdated(pkg.getPackageName(),
+ pkg.getBaseApkPath(), pkg.getSplitCodePaths());
+ }
+ return true;
+ }
+
+ @GuardedBy({"mPm.mLock", "mPm.mInstallLock"})
+ private AndroidPackage installStubPackageLI(AndroidPackage stubPkg,
+ @ParsingPackageUtils.ParseFlags int parseFlags,
+ @PackageManagerService.ScanFlags int scanFlags)
+ throws PackageManagerException {
+ if (DEBUG_COMPRESSION) {
+ Slog.i(TAG, "Uncompressing system stub; pkg: " + stubPkg.getPackageName());
+ }
+ // uncompress the binary to its eventual destination on /data
+ final File scanFile = decompressPackage(stubPkg.getPackageName(), stubPkg.getPath());
+ if (scanFile == null) {
+ throw new PackageManagerException(
+ "Unable to decompress stub at " + stubPkg.getPath());
+ }
+ synchronized (mPm.mLock) {
+ mPm.mSettings.disableSystemPackageLPw(stubPkg.getPackageName(), true /*replaced*/);
+ }
+ final RemovePackageHelper removePackageHelper = new RemovePackageHelper(mPm);
+ removePackageHelper.removePackageLI(stubPkg, true /*chatty*/);
+ final ScanPackageHelper scanPackageHelper = new ScanPackageHelper(mPm);
+ try {
+ return scanPackageHelper.scanPackageTracedLI(scanFile, parseFlags, scanFlags, 0, null);
+ } catch (PackageManagerException e) {
+ Slog.w(TAG, "Failed to install compressed system package:" + stubPkg.getPackageName(),
+ e);
+ // Remove the failed install
+ removePackageHelper.removeCodePathLI(scanFile);
+ throw e;
+ }
+ }
+
+ /**
+ * Decompresses the given package on the system image onto
+ * the /data partition.
+ * @return The directory the package was decompressed into. Otherwise, {@code null}.
+ */
+ @GuardedBy("mPm.mInstallLock")
+ private File decompressPackage(String packageName, String codePath) {
+ if (!compressedFileExists(codePath)) {
+ if (DEBUG_COMPRESSION) {
+ Slog.i(TAG, "No files to decompress at: " + codePath);
+ }
+ return null;
+ }
+ final File dstCodePath =
+ PackageManagerServiceUtils.getNextCodePath(Environment.getDataAppDirectory(null),
+ packageName);
+ int ret = PackageManagerServiceUtils.decompressFiles(codePath, dstCodePath, packageName);
+ if (ret == PackageManager.INSTALL_SUCCEEDED) {
+ ret = extractNativeBinaries(dstCodePath, packageName);
+ }
+ if (ret == PackageManager.INSTALL_SUCCEEDED) {
+ // NOTE: During boot, we have to delay releasing cblocks for no other reason than
+ // we cannot retrieve the setting {@link Secure#RELEASE_COMPRESS_BLOCKS_ON_INSTALL}.
+ // When we no longer need to read that setting, cblock release can occur always
+ // occur here directly
+ if (!mPm.isSystemReady()) {
+ if (mPm.mReleaseOnSystemReady == null) {
+ mPm.mReleaseOnSystemReady = new ArrayList<>();
+ }
+ mPm.mReleaseOnSystemReady.add(dstCodePath);
+ } else {
+ final ContentResolver resolver = mPm.mContext.getContentResolver();
+ F2fsUtils.releaseCompressedBlocks(resolver, dstCodePath);
+ }
+ } else {
+ if (!dstCodePath.exists()) {
+ return null;
+ }
+ new RemovePackageHelper(mPm).removeCodePathLI(dstCodePath);
+ return null;
+ }
+
+ return dstCodePath;
+ }
+
+ private static int extractNativeBinaries(File dstCodePath, String packageName) {
+ final File libraryRoot = new File(dstCodePath, LIB_DIR_NAME);
+ NativeLibraryHelper.Handle handle = null;
+ try {
+ handle = NativeLibraryHelper.Handle.create(dstCodePath);
+ return NativeLibraryHelper.copyNativeBinariesWithOverride(handle, libraryRoot,
+ null /*abiOverride*/, false /*isIncremental*/);
+ } catch (IOException e) {
+ logCriticalInfo(Log.ERROR, "Failed to extract native libraries"
+ + "; pkg: " + packageName);
+ return PackageManager.INSTALL_FAILED_INTERNAL_ERROR;
+ } finally {
+ IoUtils.closeQuietly(handle);
+ }
+ }
+
+ /**
+ * Tries to restore the disabled system package after an update has been deleted.
+ */
+ @GuardedBy({"mPm.mLock", "mPm.mInstallLock"})
+ public void restoreDisabledSystemPackageLIF(DeletePackageAction action,
+ PackageSetting deletedPs, @NonNull int[] allUserHandles,
+ @Nullable PackageRemovedInfo outInfo,
+ boolean writeSettings,
+ PackageSetting disabledPs)
+ throws SystemDeleteException {
+ // writer
+ synchronized (mPm.mLock) {
+ // NOTE: The system package always needs to be enabled; even if it's for
+ // a compressed stub. If we don't, installing the system package fails
+ // during scan [scanning checks the disabled packages]. We will reverse
+ // this later, after we've "installed" the stub.
+ // Reinstate the old system package
+ mPm.mSettings.enableSystemPackageLPw(disabledPs.getPkg().getPackageName());
+ // Remove any native libraries from the upgraded package.
+ removeNativeBinariesLI(deletedPs);
+ }
+
+ // Install the system package
+ if (DEBUG_REMOVE) Slog.d(TAG, "Re-installing system package: " + disabledPs);
+ try {
+ installPackageFromSystemLIF(disabledPs.getPathString(), allUserHandles,
+ outInfo == null ? null : outInfo.mOrigUsers, writeSettings);
+ } catch (PackageManagerException e) {
+ Slog.w(TAG, "Failed to restore system package:" + deletedPs.getPackageName() + ": "
+ + e.getMessage());
+ // TODO(b/194319951): can we avoid this; throw would come from scan...
+ throw new SystemDeleteException(e);
+ } finally {
+ if (disabledPs.getPkg().isStub()) {
+ // We've re-installed the stub; make sure it's disabled here. If package was
+ // originally enabled, we'll install the compressed version of the application
+ // and re-enable it afterward.
+ disableStubPackage(action, deletedPs, allUserHandles);
+ }
+ }
+ }
+
+ @GuardedBy("mPm.mLock")
+ private void disableStubPackage(DeletePackageAction action, PackageSetting deletedPs,
+ @NonNull int[] allUserHandles) {
+ final PackageSetting stubPs = mPm.mSettings.getPackageLPr(
+ deletedPs.getPackageName());
+ if (stubPs != null) {
+ int userId = action.mUser == null
+ ? UserHandle.USER_ALL : action.mUser.getIdentifier();
+ if (userId == UserHandle.USER_ALL) {
+ for (int aUserId : allUserHandles) {
+ stubPs.setEnabled(COMPONENT_ENABLED_STATE_DISABLED, aUserId, "android");
+ }
+ } else if (userId >= UserHandle.USER_SYSTEM) {
+ stubPs.setEnabled(COMPONENT_ENABLED_STATE_DISABLED, userId, "android");
+ }
+ }
+ }
+
+ private static void removeNativeBinariesLI(PackageSetting ps) {
+ if (ps != null) {
+ NativeLibraryHelper.removeNativeBinariesLI(ps.getLegacyNativeLibraryPath());
+ }
+ }
+
+ /**
+ * Installs a package that's already on the system partition.
+ */
+ @GuardedBy({"mPm.mLock", "mPm.mInstallLock"})
+ private void installPackageFromSystemLIF(@NonNull String codePathString,
+ @NonNull int[] allUserHandles, @Nullable int[] origUserHandles, boolean writeSettings)
+ throws PackageManagerException {
+ final File codePath = new File(codePathString);
+ @ParsingPackageUtils.ParseFlags int parseFlags =
+ mPm.getDefParseFlags()
+ | ParsingPackageUtils.PARSE_MUST_BE_APK
+ | ParsingPackageUtils.PARSE_IS_SYSTEM_DIR;
+ @PackageManagerService.ScanFlags int scanFlags = mPm.getSystemPackageScanFlags(codePath);
+ final ScanPackageHelper scanPackageHelper = new ScanPackageHelper(mPm);
+ final AndroidPackage pkg =
+ scanPackageHelper.scanPackageTracedLI(
+ codePath, parseFlags, scanFlags, 0 /*currentTime*/, null);
+
+ PackageSetting pkgSetting = mPm.mSettings.getPackageLPr(pkg.getPackageName());
+
+ try {
+ // update shared libraries for the newly re-installed system package
+ mPm.updateSharedLibrariesLocked(pkg, pkgSetting, null, null,
+ Collections.unmodifiableMap(mPm.mPackages));
+ } catch (PackageManagerException e) {
+ Slog.e(TAG, "updateAllSharedLibrariesLPw failed: " + e.getMessage());
+ }
+
+ mAppDataHelper.prepareAppDataAfterInstallLIF(pkg);
+
+ setPackageInstalledForSystemPackage(pkg, allUserHandles,
+ origUserHandles, writeSettings);
+ }
+
+ private void setPackageInstalledForSystemPackage(@NonNull AndroidPackage pkg,
+ @NonNull int[] allUserHandles, @Nullable int[] origUserHandles, boolean writeSettings) {
+ // writer
+ synchronized (mPm.mLock) {
+ PackageSetting ps = mPm.mSettings.getPackageLPr(pkg.getPackageName());
+
+ final boolean applyUserRestrictions = origUserHandles != null;
+ if (applyUserRestrictions) {
+ boolean installedStateChanged = false;
+ if (DEBUG_REMOVE) {
+ Slog.d(TAG, "Propagating install state across reinstall");
+ }
+ for (int userId : allUserHandles) {
+ final boolean installed = ArrayUtils.contains(origUserHandles, userId);
+ if (DEBUG_REMOVE) {
+ Slog.d(TAG, " user " + userId + " => " + installed);
+ }
+ if (installed != ps.getInstalled(userId)) {
+ installedStateChanged = true;
+ }
+ ps.setInstalled(installed, userId);
+ if (installed) {
+ ps.setUninstallReason(UNINSTALL_REASON_UNKNOWN, userId);
+ }
+ }
+ // Regardless of writeSettings we need to ensure that this restriction
+ // state propagation is persisted
+ mPm.mSettings.writeAllUsersPackageRestrictionsLPr();
+ if (installedStateChanged) {
+ mPm.mSettings.writeKernelMappingLPr(ps);
+ }
+ }
+
+ // The method below will take care of removing obsolete permissions and granting
+ // install permissions.
+ mPm.mPermissionManager.onPackageInstalled(pkg,
+ Process.INVALID_UID /* previousAppId */,
+ PermissionManagerServiceInternal.PackageInstalledParams.DEFAULT,
+ UserHandle.USER_ALL);
+ for (final int userId : allUserHandles) {
+ if (applyUserRestrictions) {
+ mPm.mSettings.writePermissionStateForUserLPr(userId, false);
+ }
+ }
+
+ // can downgrade to reader here
+ if (writeSettings) {
+ mPm.writeSettingsLPrTEMP();
+ }
+ }
+ }
+
}
diff --git a/services/core/java/com/android/server/pm/InstallParams.java b/services/core/java/com/android/server/pm/InstallParams.java
index 56cb4ad..0dff7d1 100644
--- a/services/core/java/com/android/server/pm/InstallParams.java
+++ b/services/core/java/com/android/server/pm/InstallParams.java
@@ -17,131 +17,42 @@
package com.android.server.pm;
import static android.app.AppOpsManager.MODE_DEFAULT;
-import static android.content.pm.PackageManager.COMPONENT_ENABLED_STATE_DEFAULT;
-import static android.content.pm.PackageManager.INSTALL_FAILED_ALREADY_EXISTS;
-import static android.content.pm.PackageManager.INSTALL_FAILED_BAD_PERMISSION_GROUP;
-import static android.content.pm.PackageManager.INSTALL_FAILED_DUPLICATE_PACKAGE;
-import static android.content.pm.PackageManager.INSTALL_FAILED_DUPLICATE_PERMISSION;
-import static android.content.pm.PackageManager.INSTALL_FAILED_DUPLICATE_PERMISSION_GROUP;
-import static android.content.pm.PackageManager.INSTALL_FAILED_INSUFFICIENT_STORAGE;
-import static android.content.pm.PackageManager.INSTALL_FAILED_INTERNAL_ERROR;
-import static android.content.pm.PackageManager.INSTALL_FAILED_INVALID_APK;
-import static android.content.pm.PackageManager.INSTALL_FAILED_INVALID_INSTALL_LOCATION;
-import static android.content.pm.PackageManager.INSTALL_FAILED_SESSION_INVALID;
-import static android.content.pm.PackageManager.INSTALL_FAILED_TEST_ONLY;
-import static android.content.pm.PackageManager.INSTALL_FAILED_UID_CHANGED;
-import static android.content.pm.PackageManager.INSTALL_FAILED_UPDATE_INCOMPATIBLE;
-import static android.content.pm.PackageManager.INSTALL_REASON_DEVICE_RESTORE;
-import static android.content.pm.PackageManager.INSTALL_REASON_DEVICE_SETUP;
import static android.content.pm.PackageManager.INSTALL_STAGED;
import static android.content.pm.PackageManager.INSTALL_SUCCEEDED;
import static android.content.pm.PackageManager.PERMISSION_GRANTED;
-import static android.content.pm.PackageManager.UNINSTALL_REASON_UNKNOWN;
-import static android.content.pm.SigningDetails.SignatureSchemeVersion.SIGNING_BLOCK_V4;
import static android.os.Trace.TRACE_TAG_PACKAGE_MANAGER;
-import static android.os.incremental.IncrementalManager.isIncrementalPath;
-import static android.os.storage.StorageManager.FLAG_STORAGE_CE;
-import static android.os.storage.StorageManager.FLAG_STORAGE_DE;
-import static android.os.storage.StorageManager.FLAG_STORAGE_EXTERNAL;
-import static com.android.server.pm.InstructionSets.getAppDexInstructionSets;
-import static com.android.server.pm.PackageManagerService.DEBUG_INSTALL;
import static com.android.server.pm.PackageManagerService.DEBUG_INSTANT;
import static com.android.server.pm.PackageManagerService.INIT_COPY;
-import static com.android.server.pm.PackageManagerService.PLATFORM_PACKAGE_NAME;
-import static com.android.server.pm.PackageManagerService.PRECOMPILE_LAYOUTS;
-import static com.android.server.pm.PackageManagerService.SCAN_AS_FULL_APP;
-import static com.android.server.pm.PackageManagerService.SCAN_AS_INSTANT_APP;
-import static com.android.server.pm.PackageManagerService.SCAN_AS_ODM;
-import static com.android.server.pm.PackageManagerService.SCAN_AS_OEM;
-import static com.android.server.pm.PackageManagerService.SCAN_AS_PRIVILEGED;
-import static com.android.server.pm.PackageManagerService.SCAN_AS_PRODUCT;
-import static com.android.server.pm.PackageManagerService.SCAN_AS_SYSTEM;
-import static com.android.server.pm.PackageManagerService.SCAN_AS_SYSTEM_EXT;
-import static com.android.server.pm.PackageManagerService.SCAN_AS_VENDOR;
-import static com.android.server.pm.PackageManagerService.SCAN_AS_VIRTUAL_PRELOAD;
-import static com.android.server.pm.PackageManagerService.SCAN_DONT_KILL_APP;
-import static com.android.server.pm.PackageManagerService.SCAN_INITIAL;
-import static com.android.server.pm.PackageManagerService.SCAN_MOVE;
-import static com.android.server.pm.PackageManagerService.SCAN_NEW_INSTALL;
-import static com.android.server.pm.PackageManagerService.SCAN_NO_DEX;
-import static com.android.server.pm.PackageManagerService.SCAN_UPDATE_SIGNATURE;
import static com.android.server.pm.PackageManagerService.TAG;
-import static com.android.server.pm.PackageManagerServiceUtils.deriveAbiOverride;
-import static com.android.server.pm.PackageManagerServiceUtils.verifySignatures;
import android.annotation.NonNull;
import android.annotation.Nullable;
-import android.app.ApplicationPackageManager;
import android.content.pm.DataLoaderType;
import android.content.pm.IPackageInstallObserver2;
-import android.content.pm.PackageChangeEvent;
-import android.content.pm.PackageInfo;
import android.content.pm.PackageInfoLite;
import android.content.pm.PackageInstaller;
import android.content.pm.PackageManager;
-import android.content.pm.PermissionGroupInfo;
-import android.content.pm.PermissionInfo;
-import android.content.pm.SharedLibraryInfo;
import android.content.pm.SigningDetails;
-import android.content.pm.dex.DexMetadataHelper;
import android.content.pm.parsing.PackageLite;
-import android.content.pm.parsing.ParsingPackageUtils;
-import android.content.pm.parsing.component.ComponentMutateUtils;
-import android.content.pm.parsing.component.ParsedPermission;
-import android.content.pm.parsing.component.ParsedPermissionGroup;
-import android.content.pm.parsing.result.ParseResult;
-import android.content.pm.parsing.result.ParseTypeImpl;
-import android.net.Uri;
-import android.os.Build;
import android.os.Environment;
import android.os.Message;
-import android.os.SystemProperties;
import android.os.Trace;
import android.os.UserHandle;
-import android.os.incremental.IncrementalManager;
-import android.os.incremental.IncrementalStorage;
import android.os.storage.StorageManager;
import android.util.ArrayMap;
-import android.util.ArraySet;
-import android.util.EventLog;
import android.util.Pair;
import android.util.Slog;
-import android.util.SparseArray;
-import com.android.internal.annotations.GuardedBy;
import com.android.internal.content.F2fsUtils;
import com.android.internal.content.PackageHelper;
-import com.android.internal.security.VerityUtils;
-import com.android.internal.util.ArrayUtils;
import com.android.internal.util.Preconditions;
-import com.android.server.Watchdog;
-import com.android.server.pm.dex.DexoptOptions;
-import com.android.server.pm.parsing.PackageParser2;
-import com.android.server.pm.parsing.pkg.AndroidPackage;
-import com.android.server.pm.parsing.pkg.AndroidPackageUtils;
-import com.android.server.pm.parsing.pkg.ParsedPackage;
-import com.android.server.pm.permission.Permission;
-import com.android.server.pm.permission.PermissionManagerServiceInternal;
-import com.android.server.pm.pkg.PackageStateInternal;
-
-import libcore.io.IoUtils;
import java.io.File;
-import java.io.FileDescriptor;
-import java.io.FileInputStream;
-import java.io.IOException;
-import java.security.DigestException;
-import java.security.DigestInputStream;
-import java.security.MessageDigest;
-import java.security.NoSuchAlgorithmException;
import java.util.ArrayList;
-import java.util.Arrays;
import java.util.Collections;
import java.util.List;
import java.util.Map;
-import java.util.Objects;
-import java.util.Set;
final class InstallParams extends HandlerParams {
final OriginInfo mOriginInfo;
@@ -165,7 +76,6 @@
final int mDataLoaderType;
final long mRequiredInstalledVersionCode;
final PackageLite mPackageLite;
- final InstallPackageHelper mInstallPackageHelper;
InstallParams(OriginInfo originInfo, MoveInfo moveInfo, IPackageInstallObserver2 observer,
int installFlags, InstallSource installSource, String volumeUuid,
@@ -190,7 +100,6 @@
mDataLoaderType = DataLoaderType.NONE;
mRequiredInstalledVersionCode = PackageManager.VERSION_CODE_HIGHEST;
mPackageLite = packageLite;
- mInstallPackageHelper = new InstallPackageHelper(mPm);
}
InstallParams(File stagedDir, IPackageInstallObserver2 observer,
@@ -217,7 +126,6 @@
? sessionParams.dataLoaderParams.getType() : DataLoaderType.NONE;
mRequiredInstalledVersionCode = sessionParams.requiredInstalledVersionCode;
mPackageLite = packageLite;
- mInstallPackageHelper = new InstallPackageHelper(mPm);
}
@Override
@@ -226,45 +134,6 @@
+ " file=" + mOriginInfo.mFile + "}";
}
- private int installLocationPolicy(PackageInfoLite pkgLite) {
- String packageName = pkgLite.packageName;
- int installLocation = pkgLite.installLocation;
- // reader
- synchronized (mPm.mLock) {
- // Currently installed package which the new package is attempting to replace or
- // null if no such package is installed.
- AndroidPackage installedPkg = mPm.mPackages.get(packageName);
-
- if (installedPkg != null) {
- if ((mInstallFlags & PackageManager.INSTALL_REPLACE_EXISTING) != 0) {
- // Check for updated system application.
- if (installedPkg.isSystem()) {
- return PackageHelper.RECOMMEND_INSTALL_INTERNAL;
- } else {
- // If current upgrade specifies particular preference
- if (installLocation == PackageInfo.INSTALL_LOCATION_INTERNAL_ONLY) {
- // Application explicitly specified internal.
- return PackageHelper.RECOMMEND_INSTALL_INTERNAL;
- } else if (
- installLocation == PackageInfo.INSTALL_LOCATION_PREFER_EXTERNAL) {
- // App explicitly prefers external. Let policy decide
- } else {
- // Prefer previous location
- if (installedPkg.isExternalStorage()) {
- return PackageHelper.RECOMMEND_INSTALL_EXTERNAL;
- }
- return PackageHelper.RECOMMEND_INSTALL_INTERNAL;
- }
- }
- } else {
- // Invalid install. Return error code
- return PackageHelper.RECOMMEND_FAILED_ALREADY_EXISTS;
- }
- }
- }
- return pkgLite.recommendedInstallLocation;
- }
-
/**
* Override install location based on default policy if needed.
*
@@ -338,7 +207,7 @@
ret = PackageManager.INSTALL_FAILED_MEDIA_UNAVAILABLE;
} else {
// Override with defaults if needed.
- loc = installLocationPolicy(pkgLite);
+ loc = mInstallPackageHelper.installLocationPolicy(pkgLite, mInstallFlags);
final boolean onInt = (mInstallFlags & PackageManager.INSTALL_INTERNAL) != 0;
@@ -375,7 +244,7 @@
// state can change within this delay and hence we need to re-verify certain conditions.
boolean isStaged = (mInstallFlags & INSTALL_STAGED) != 0;
if (isStaged) {
- Pair<Integer, String> ret = verifyReplacingVersionCode(
+ Pair<Integer, String> ret = mInstallPackageHelper.verifyReplacingVersionCode(
pkgLite, mRequiredInstalledVersionCode, mInstallFlags);
mRet = ret.first;
if (mRet != INSTALL_SUCCEEDED) {
@@ -422,1609 +291,10 @@
private void processInstallRequestsAsync(boolean success,
List<InstallRequest> installRequests) {
mPm.mHandler.post(() -> {
- List<InstallRequest> apexInstallRequests = new ArrayList<>();
- List<InstallRequest> apkInstallRequests = new ArrayList<>();
- for (InstallRequest request : installRequests) {
- if ((request.mArgs.mInstallFlags & PackageManager.INSTALL_APEX) != 0) {
- apexInstallRequests.add(request);
- } else {
- apkInstallRequests.add(request);
- }
- }
- // Note: supporting multi package install of both APEXes and APKs might requir some
- // thinking to ensure atomicity of the install.
- if (!apexInstallRequests.isEmpty() && !apkInstallRequests.isEmpty()) {
- // This should've been caught at the validation step, but for some reason wasn't.
- throw new IllegalStateException(
- "Attempted to do a multi package install of both APEXes and APKs");
- }
- if (!apexInstallRequests.isEmpty()) {
- if (success) {
- // Since installApexPackages requires talking to external service (apexd), we
- // schedule to run it async. Once it finishes, it will resume the install.
- Thread t = new Thread(() -> installApexPackagesTraced(apexInstallRequests),
- "installApexPackages");
- t.start();
- } else {
- // Non-staged APEX installation failed somewhere before
- // processInstallRequestAsync. In that case just notify the observer about the
- // failure.
- InstallRequest request = apexInstallRequests.get(0);
- mPm.notifyInstallObserver(request.mInstallResult,
- request.mArgs.mObserver);
- }
- return;
- }
- if (success) {
- for (InstallRequest request : apkInstallRequests) {
- request.mArgs.doPreInstall(request.mInstallResult.mReturnCode);
- }
- synchronized (mPm.mInstallLock) {
- installPackagesTracedLI(apkInstallRequests);
- }
- for (InstallRequest request : apkInstallRequests) {
- request.mArgs.doPostInstall(
- request.mInstallResult.mReturnCode, request.mInstallResult.mUid);
- }
- }
- for (InstallRequest request : apkInstallRequests) {
- mInstallPackageHelper.restoreAndPostInstall(request.mArgs.mUser.getIdentifier(),
- request.mInstallResult,
- new PostInstallData(request.mArgs,
- request.mInstallResult, null));
- }
+ mInstallPackageHelper.processInstallRequests(success, installRequests);
});
}
- private void installApexPackagesTraced(List<InstallRequest> requests) {
- try {
- Trace.traceBegin(TRACE_TAG_PACKAGE_MANAGER, "installApexPackages");
- installApexPackages(requests);
- } finally {
- Trace.traceEnd(TRACE_TAG_PACKAGE_MANAGER);
- }
- }
-
- private void installApexPackages(List<InstallRequest> requests) {
- if (requests.isEmpty()) {
- return;
- }
- if (requests.size() != 1) {
- throw new IllegalStateException(
- "Only a non-staged install of a single APEX is supported");
- }
- InstallRequest request = requests.get(0);
- try {
- // Should directory scanning logic be moved to ApexManager for better test coverage?
- final File dir = request.mArgs.mOriginInfo.mResolvedFile;
- final File[] apexes = dir.listFiles();
- if (apexes == null) {
- throw new PackageManagerException(INSTALL_FAILED_INTERNAL_ERROR,
- dir.getAbsolutePath() + " is not a directory");
- }
- if (apexes.length != 1) {
- throw new PackageManagerException(INSTALL_FAILED_INTERNAL_ERROR,
- "Expected exactly one .apex file under " + dir.getAbsolutePath()
- + " got: " + apexes.length);
- }
- try (PackageParser2 packageParser = mPm.mInjector.getScanningPackageParser()) {
- mPm.mApexManager.installPackage(apexes[0], packageParser);
- }
- } catch (PackageManagerException e) {
- request.mInstallResult.setError("APEX installation failed", e);
- }
- PackageManagerService.invalidatePackageInfoCache();
- mPm.notifyInstallObserver(request.mInstallResult, request.mArgs.mObserver);
- }
-
- @GuardedBy("mInstallLock")
- private void installPackagesTracedLI(List<InstallRequest> requests) {
- try {
- Trace.traceBegin(TRACE_TAG_PACKAGE_MANAGER, "installPackages");
- installPackagesLI(requests);
- } finally {
- Trace.traceEnd(TRACE_TAG_PACKAGE_MANAGER);
- }
- }
-
- /**
- * Installs one or more packages atomically. This operation is broken up into four phases:
- * <ul>
- * <li><b>Prepare</b>
- * <br/>Analyzes any current install state, parses the package and does initial
- * validation on it.</li>
- * <li><b>Scan</b>
- * <br/>Interrogates the parsed packages given the context collected in prepare.</li>
- * <li><b>Reconcile</b>
- * <br/>Validates scanned packages in the context of each other and the current system
- * state to ensure that the install will be successful.
- * <li><b>Commit</b>
- * <br/>Commits all scanned packages and updates system state. This is the only place
- * that system state may be modified in the install flow and all predictable errors
- * must be determined before this phase.</li>
- * </ul>
- *
- * Failure at any phase will result in a full failure to install all packages.
- */
- @GuardedBy("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());
- final Map<String, PackageInstalledInfo> installResults = new ArrayMap<>(requests.size());
- final Map<String, PrepareResult> prepareResults = new ArrayMap<>(requests.size());
- final Map<String, Settings.VersionInfo> versionInfos = new ArrayMap<>(requests.size());
- final Map<String, PackageSetting> lastStaticSharedLibSettings =
- new ArrayMap<>(requests.size());
- final Map<String, Boolean> createdAppId = new ArrayMap<>(requests.size());
- boolean success = false;
- final ScanPackageHelper scanPackageHelper = new ScanPackageHelper(mPm);
- try {
- Trace.traceBegin(TRACE_TAG_PACKAGE_MANAGER, "installPackagesLI");
- for (InstallRequest request : requests) {
- // TODO(b/109941548): remove this once we've pulled everything from it and into
- // scan, reconcile or commit.
- final PrepareResult prepareResult;
- try {
- Trace.traceBegin(TRACE_TAG_PACKAGE_MANAGER, "preparePackage");
- prepareResult =
- preparePackageLI(request.mArgs, request.mInstallResult);
- } catch (PrepareFailure prepareFailure) {
- request.mInstallResult.setError(prepareFailure.error,
- prepareFailure.getMessage());
- request.mInstallResult.mOrigPackage = prepareFailure.mConflictingPackage;
- request.mInstallResult.mOrigPermission = prepareFailure.mConflictingPermission;
- return;
- } finally {
- Trace.traceEnd(TRACE_TAG_PACKAGE_MANAGER);
- }
- request.mInstallResult.setReturnCode(PackageManager.INSTALL_SUCCEEDED);
- request.mInstallResult.mInstallerPackageName =
- request.mArgs.mInstallSource.installerPackageName;
-
- final String packageName = prepareResult.mPackageToScan.getPackageName();
- prepareResults.put(packageName, prepareResult);
- installResults.put(packageName, request.mInstallResult);
- installArgs.put(packageName, request.mArgs);
- try {
- final ScanResult result = scanPackageHelper.scanPackageTracedLI(
- prepareResult.mPackageToScan, prepareResult.mParseFlags,
- prepareResult.mScanFlags, System.currentTimeMillis(),
- request.mArgs.mUser, request.mArgs.mAbiOverride);
- if (null != preparedScans.put(result.mPkgSetting.getPkg().getPackageName(),
- result)) {
- request.mInstallResult.setError(
- PackageManager.INSTALL_FAILED_DUPLICATE_PACKAGE,
- "Duplicate package "
- + result.mPkgSetting.getPkg().getPackageName()
- + " in multi-package install request.");
- return;
- }
- createdAppId.put(packageName,
- scanPackageHelper.optimisticallyRegisterAppId(result));
- versionInfos.put(result.mPkgSetting.getPkg().getPackageName(),
- mPm.getSettingsVersionForPackage(result.mPkgSetting.getPkg()));
- if (result.mStaticSharedLibraryInfo != null) {
- final PackageSetting sharedLibLatestVersionSetting =
- mPm.getSharedLibLatestVersionSetting(result);
- if (sharedLibLatestVersionSetting != null) {
- lastStaticSharedLibSettings.put(
- result.mPkgSetting.getPkg().getPackageName(),
- sharedLibLatestVersionSetting);
- }
- }
- } catch (PackageManagerException e) {
- request.mInstallResult.setError("Scanning Failed.", e);
- return;
- }
- }
- ReconcileRequest
- reconcileRequest = new ReconcileRequest(preparedScans, installArgs,
- installResults,
- prepareResults,
- mPm.mSharedLibraries,
- Collections.unmodifiableMap(mPm.mPackages), versionInfos,
- lastStaticSharedLibSettings);
- CommitRequest commitRequest = null;
- synchronized (mPm.mLock) {
- Map<String, ReconciledPackage> reconciledPackages;
- try {
- Trace.traceBegin(TRACE_TAG_PACKAGE_MANAGER, "reconcilePackages");
- reconciledPackages = mInstallPackageHelper.reconcilePackagesLocked(
- reconcileRequest, mPm.mSettings.getKeySetManagerService(),
- mPm.mInjector);
- } catch (ReconcileFailure e) {
- for (InstallRequest request : requests) {
- request.mInstallResult.setError("Reconciliation failed...", e);
- }
- return;
- } finally {
- Trace.traceEnd(TRACE_TAG_PACKAGE_MANAGER);
- }
- try {
- Trace.traceBegin(TRACE_TAG_PACKAGE_MANAGER, "commitPackages");
- commitRequest = new CommitRequest(reconciledPackages,
- mPm.mUserManager.getUserIds());
- commitPackagesLocked(commitRequest);
- success = true;
- } finally {
- Trace.traceEnd(TRACE_TAG_PACKAGE_MANAGER);
- }
- }
- executePostCommitSteps(commitRequest);
- } finally {
- if (success) {
- for (InstallRequest request : requests) {
- final InstallArgs args = request.mArgs;
- if (args.mDataLoaderType != DataLoaderType.INCREMENTAL) {
- continue;
- }
- if (args.mSigningDetails.getSignatureSchemeVersion() != SIGNING_BLOCK_V4) {
- continue;
- }
- // For incremental installs, we bypass the verifier prior to install. Now
- // that we know the package is valid, send a notice to the verifier with
- // the root hash of the base.apk.
- final String baseCodePath = request.mInstallResult.mPkg.getBaseApkPath();
- final String[] splitCodePaths = request.mInstallResult.mPkg.getSplitCodePaths();
- final Uri originUri = Uri.fromFile(args.mOriginInfo.mResolvedFile);
- final int verificationId = mPm.mPendingVerificationToken++;
- final String rootHashString = PackageManagerServiceUtils
- .buildVerificationRootHashString(baseCodePath, splitCodePaths);
- mVerificationHelper.broadcastPackageVerified(verificationId, originUri,
- PackageManager.VERIFICATION_ALLOW, rootHashString,
- args.mDataLoaderType, args.getUser());
- }
- } else {
- for (ScanResult result : preparedScans.values()) {
- if (createdAppId.getOrDefault(result.mRequest.mParsedPackage.getPackageName(),
- false)) {
- scanPackageHelper.cleanUpAppIdCreation(result);
- }
- }
- // TODO(b/194319951): create a more descriptive reason than unknown
- // mark all non-failure installs as UNKNOWN so we do not treat them as success
- for (InstallRequest request : requests) {
- if (request.mInstallResult.mFreezer != null) {
- request.mInstallResult.mFreezer.close();
- }
- if (request.mInstallResult.mReturnCode == PackageManager.INSTALL_SUCCEEDED) {
- request.mInstallResult.mReturnCode = PackageManager.INSTALL_UNKNOWN;
- }
- }
- }
- Trace.traceEnd(TRACE_TAG_PACKAGE_MANAGER);
- }
- }
-
- @GuardedBy("mInstallLock")
- private PrepareResult preparePackageLI(InstallArgs args, PackageInstalledInfo res)
- throws PrepareFailure {
- final int installFlags = args.mInstallFlags;
- final File tmpPackageFile = new File(args.getCodePath());
- final boolean onExternal = args.mVolumeUuid != null;
- final boolean instantApp = ((installFlags & PackageManager.INSTALL_INSTANT_APP) != 0);
- final boolean fullApp = ((installFlags & PackageManager.INSTALL_FULL_APP) != 0);
- final boolean virtualPreload =
- ((installFlags & PackageManager.INSTALL_VIRTUAL_PRELOAD) != 0);
- final boolean isRollback = args.mInstallReason == PackageManager.INSTALL_REASON_ROLLBACK;
- @PackageManagerService.ScanFlags int scanFlags = SCAN_NEW_INSTALL | SCAN_UPDATE_SIGNATURE;
- if (args.mMoveInfo != null) {
- // moving a complete application; perform an initial scan on the new install location
- scanFlags |= SCAN_INITIAL;
- }
- if ((installFlags & PackageManager.INSTALL_DONT_KILL_APP) != 0) {
- scanFlags |= SCAN_DONT_KILL_APP;
- }
- if (instantApp) {
- scanFlags |= SCAN_AS_INSTANT_APP;
- }
- if (fullApp) {
- scanFlags |= SCAN_AS_FULL_APP;
- }
- if (virtualPreload) {
- scanFlags |= SCAN_AS_VIRTUAL_PRELOAD;
- }
-
- if (DEBUG_INSTALL) Slog.d(TAG, "installPackageLI: path=" + tmpPackageFile);
-
- // Validity check
- if (instantApp && onExternal) {
- Slog.i(TAG, "Incompatible ephemeral install; external=" + onExternal);
- throw new PrepareFailure(PackageManager.INSTALL_FAILED_SESSION_INVALID);
- }
-
- // Retrieve PackageSettings and parse package
- @ParsingPackageUtils.ParseFlags final int parseFlags =
- mPm.getDefParseFlags() | ParsingPackageUtils.PARSE_CHATTY
- | ParsingPackageUtils.PARSE_ENFORCE_CODE
- | (onExternal ? ParsingPackageUtils.PARSE_EXTERNAL_STORAGE : 0);
-
- Trace.traceBegin(TRACE_TAG_PACKAGE_MANAGER, "parsePackage");
- final ParsedPackage parsedPackage;
- try (PackageParser2 pp = mPm.mInjector.getPreparingPackageParser()) {
- parsedPackage = pp.parsePackage(tmpPackageFile, parseFlags, false);
- AndroidPackageUtils.validatePackageDexMetadata(parsedPackage);
- } catch (PackageManagerException e) {
- throw new PrepareFailure("Failed parse during installPackageLI", e);
- } finally {
- Trace.traceEnd(TRACE_TAG_PACKAGE_MANAGER);
- }
-
- // Instant apps have several additional install-time checks.
- if (instantApp) {
- if (parsedPackage.getTargetSdkVersion() < Build.VERSION_CODES.O) {
- Slog.w(TAG, "Instant app package " + parsedPackage.getPackageName()
- + " does not target at least O");
- throw new PrepareFailure(INSTALL_FAILED_SESSION_INVALID,
- "Instant app package must target at least O");
- }
- if (parsedPackage.getSharedUserId() != null) {
- Slog.w(TAG, "Instant app package " + parsedPackage.getPackageName()
- + " may not declare sharedUserId.");
- throw new PrepareFailure(INSTALL_FAILED_SESSION_INVALID,
- "Instant app package may not declare a sharedUserId");
- }
- }
-
- if (parsedPackage.isStaticSharedLibrary()) {
- // Static shared libraries have synthetic package names
- PackageManagerService.renameStaticSharedLibraryPackage(parsedPackage);
-
- // No static shared libs on external storage
- if (onExternal) {
- Slog.i(TAG, "Static shared libs can only be installed on internal storage.");
- throw new PrepareFailure(INSTALL_FAILED_INVALID_INSTALL_LOCATION,
- "Packages declaring static-shared libs cannot be updated");
- }
- }
-
- String pkgName = res.mName = parsedPackage.getPackageName();
- if (parsedPackage.isTestOnly()) {
- if ((installFlags & PackageManager.INSTALL_ALLOW_TEST) == 0) {
- throw new PrepareFailure(INSTALL_FAILED_TEST_ONLY, "installPackageLI");
- }
- }
-
- // either use what we've been given or parse directly from the APK
- if (args.mSigningDetails != SigningDetails.UNKNOWN) {
- parsedPackage.setSigningDetails(args.mSigningDetails);
- } else {
- final ParseTypeImpl input = ParseTypeImpl.forDefaultParsing();
- final ParseResult<SigningDetails> result = ParsingPackageUtils.getSigningDetails(
- input, parsedPackage, false /*skipVerify*/);
- if (result.isError()) {
- throw new PrepareFailure("Failed collect during installPackageLI",
- result.getException());
- }
- parsedPackage.setSigningDetails(result.getResult());
- }
-
- if (instantApp && parsedPackage.getSigningDetails().getSignatureSchemeVersion()
- < SigningDetails.SignatureSchemeVersion.SIGNING_BLOCK_V2) {
- Slog.w(TAG, "Instant app package " + parsedPackage.getPackageName()
- + " is not signed with at least APK Signature Scheme v2");
- throw new PrepareFailure(INSTALL_FAILED_SESSION_INVALID,
- "Instant app package must be signed with APK Signature Scheme v2 or greater");
- }
-
- boolean systemApp = false;
- boolean replace = false;
- synchronized (mPm.mLock) {
- // Check if installing already existing package
- if ((installFlags & PackageManager.INSTALL_REPLACE_EXISTING) != 0) {
- String oldName = mPm.mSettings.getRenamedPackageLPr(pkgName);
- if (parsedPackage.getOriginalPackages().contains(oldName)
- && mPm.mPackages.containsKey(oldName)) {
- // This package is derived from an original package,
- // and this device has been updating from that original
- // name. We must continue using the original name, so
- // rename the new package here.
- parsedPackage.setPackageName(oldName);
- pkgName = parsedPackage.getPackageName();
- replace = true;
- if (DEBUG_INSTALL) {
- Slog.d(TAG, "Replacing existing renamed package: oldName="
- + oldName + " pkgName=" + pkgName);
- }
- } else if (mPm.mPackages.containsKey(pkgName)) {
- // This package, under its official name, already exists
- // on the device; we should replace it.
- replace = true;
- if (DEBUG_INSTALL) Slog.d(TAG, "Replace existing package: " + pkgName);
- }
-
- if (replace) {
- // Prevent apps opting out from runtime permissions
- AndroidPackage oldPackage = mPm.mPackages.get(pkgName);
- final int oldTargetSdk = oldPackage.getTargetSdkVersion();
- final int newTargetSdk = parsedPackage.getTargetSdkVersion();
- if (oldTargetSdk > Build.VERSION_CODES.LOLLIPOP_MR1
- && newTargetSdk <= Build.VERSION_CODES.LOLLIPOP_MR1) {
- throw new PrepareFailure(
- PackageManager.INSTALL_FAILED_PERMISSION_MODEL_DOWNGRADE,
- "Package " + parsedPackage.getPackageName()
- + " new target SDK " + newTargetSdk
- + " doesn't support runtime permissions but the old"
- + " target SDK " + oldTargetSdk + " does.");
- }
- // Prevent persistent apps from being updated
- if (oldPackage.isPersistent()
- && ((installFlags & PackageManager.INSTALL_STAGED) == 0)) {
- throw new PrepareFailure(PackageManager.INSTALL_FAILED_INVALID_APK,
- "Package " + oldPackage.getPackageName() + " is a persistent app. "
- + "Persistent apps are not updateable.");
- }
- }
- }
-
- PackageSetting ps = mPm.mSettings.getPackageLPr(pkgName);
- if (ps != null) {
- if (DEBUG_INSTALL) Slog.d(TAG, "Existing package: " + ps);
-
- // Static shared libs have same package with different versions where
- // we internally use a synthetic package name to allow multiple versions
- // of the same package, therefore we need to compare signatures against
- // the package setting for the latest library version.
- PackageSetting signatureCheckPs = ps;
- if (parsedPackage.isStaticSharedLibrary()) {
- SharedLibraryInfo libraryInfo = mPm.getLatestSharedLibraVersionLPr(
- parsedPackage);
- if (libraryInfo != null) {
- signatureCheckPs = mPm.mSettings.getPackageLPr(
- libraryInfo.getPackageName());
- }
- }
-
- // Quick validity check that we're signed correctly if updating;
- // we'll check this again later when scanning, but we want to
- // bail early here before tripping over redefined permissions.
- final KeySetManagerService ksms = mPm.mSettings.getKeySetManagerService();
- if (ksms.shouldCheckUpgradeKeySetLocked(signatureCheckPs, scanFlags)) {
- if (!ksms.checkUpgradeKeySetLocked(signatureCheckPs, parsedPackage)) {
- throw new PrepareFailure(INSTALL_FAILED_UPDATE_INCOMPATIBLE, "Package "
- + parsedPackage.getPackageName() + " upgrade keys do not match the "
- + "previously installed version");
- }
- } else {
- try {
- final boolean compareCompat =
- mInstallPackageHelper.isCompatSignatureUpdateNeeded(parsedPackage);
- final boolean compareRecover =
- mInstallPackageHelper.isRecoverSignatureUpdateNeeded(parsedPackage);
- // We don't care about disabledPkgSetting on install for now.
- final boolean compatMatch = verifySignatures(signatureCheckPs, null,
- parsedPackage.getSigningDetails(), compareCompat, compareRecover,
- isRollback);
- // The new KeySets will be re-added later in the scanning process.
- if (compatMatch) {
- synchronized (mPm.mLock) {
- ksms.removeAppKeySetDataLPw(parsedPackage.getPackageName());
- }
- }
- } catch (PackageManagerException e) {
- throw new PrepareFailure(e.error, e.getMessage());
- }
- }
-
- if (ps.getPkg() != null) {
- systemApp = ps.getPkg().isSystem();
- }
- res.mOrigUsers = ps.queryInstalledUsers(mPm.mUserManager.getUserIds(), true);
- }
-
- final int numGroups = ArrayUtils.size(parsedPackage.getPermissionGroups());
- for (int groupNum = 0; groupNum < numGroups; groupNum++) {
- final ParsedPermissionGroup group =
- parsedPackage.getPermissionGroups().get(groupNum);
- final PermissionGroupInfo sourceGroup = mPm.getPermissionGroupInfo(group.getName(),
- 0);
-
- if (sourceGroup != null && cannotInstallWithBadPermissionGroups(parsedPackage)) {
- final String sourcePackageName = sourceGroup.packageName;
-
- if ((replace || !parsedPackage.getPackageName().equals(sourcePackageName))
- && !doesSignatureMatchForPermissions(sourcePackageName, parsedPackage,
- scanFlags)) {
- EventLog.writeEvent(0x534e4554, "146211400", -1,
- parsedPackage.getPackageName());
-
- throw new PrepareFailure(INSTALL_FAILED_DUPLICATE_PERMISSION_GROUP,
- "Package "
- + parsedPackage.getPackageName()
- + " attempting to redeclare permission group "
- + group.getName() + " already owned by "
- + sourcePackageName);
- }
- }
- }
-
- // TODO: Move logic for checking permission compatibility into PermissionManagerService
- final int n = ArrayUtils.size(parsedPackage.getPermissions());
- for (int i = n - 1; i >= 0; i--) {
- final ParsedPermission perm = parsedPackage.getPermissions().get(i);
- final Permission bp = mPm.mPermissionManager.getPermissionTEMP(perm.getName());
-
- // Don't allow anyone but the system to define ephemeral permissions.
- if ((perm.getProtectionLevel() & PermissionInfo.PROTECTION_FLAG_INSTANT) != 0
- && !systemApp) {
- Slog.w(TAG, "Non-System package " + parsedPackage.getPackageName()
- + " attempting to delcare ephemeral permission "
- + perm.getName() + "; Removing ephemeral.");
- ComponentMutateUtils.setProtectionLevel(perm,
- perm.getProtectionLevel() & ~PermissionInfo.PROTECTION_FLAG_INSTANT);
- }
-
- // Check whether the newly-scanned package wants to define an already-defined perm
- if (bp != null) {
- final String sourcePackageName = bp.getPackageName();
-
- if (!doesSignatureMatchForPermissions(sourcePackageName, parsedPackage,
- scanFlags)) {
- // If the owning package is the system itself, we log but allow
- // install to proceed; we fail the install on all other permission
- // redefinitions.
- if (!sourcePackageName.equals("android")) {
- throw new PrepareFailure(INSTALL_FAILED_DUPLICATE_PERMISSION,
- "Package "
- + parsedPackage.getPackageName()
- + " attempting to redeclare permission "
- + perm.getName() + " already owned by "
- + sourcePackageName)
- .conflictsWithExistingPermission(perm.getName(),
- sourcePackageName);
- } else {
- Slog.w(TAG, "Package " + parsedPackage.getPackageName()
- + " attempting to redeclare system permission "
- + perm.getName() + "; ignoring new declaration");
- parsedPackage.removePermission(i);
- }
- } else if (!PLATFORM_PACKAGE_NAME.equals(parsedPackage.getPackageName())) {
- // Prevent apps to change protection level to dangerous from any other
- // type as this would allow a privilege escalation where an app adds a
- // normal/signature permission in other app's group and later redefines
- // it as dangerous leading to the group auto-grant.
- if ((perm.getProtectionLevel() & PermissionInfo.PROTECTION_MASK_BASE)
- == PermissionInfo.PROTECTION_DANGEROUS) {
- if (bp != null && !bp.isRuntime()) {
- Slog.w(TAG, "Package " + parsedPackage.getPackageName()
- + " trying to change a non-runtime permission "
- + perm.getName()
- + " to runtime; keeping old protection level");
- ComponentMutateUtils.setProtectionLevel(perm,
- bp.getProtectionLevel());
- }
- }
- }
- }
-
- if (perm.getGroup() != null
- && cannotInstallWithBadPermissionGroups(parsedPackage)) {
- boolean isPermGroupDefinedByPackage = false;
- for (int groupNum = 0; groupNum < numGroups; groupNum++) {
- if (parsedPackage.getPermissionGroups().get(groupNum).getName()
- .equals(perm.getGroup())) {
- isPermGroupDefinedByPackage = true;
- break;
- }
- }
-
- if (!isPermGroupDefinedByPackage) {
- final PermissionGroupInfo sourceGroup =
- mPm.getPermissionGroupInfo(perm.getGroup(), 0);
-
- if (sourceGroup == null) {
- EventLog.writeEvent(0x534e4554, "146211400", -1,
- parsedPackage.getPackageName());
-
- throw new PrepareFailure(INSTALL_FAILED_BAD_PERMISSION_GROUP,
- "Package "
- + parsedPackage.getPackageName()
- + " attempting to declare permission "
- + perm.getName() + " in non-existing group "
- + perm.getGroup());
- } else {
- String groupSourcePackageName = sourceGroup.packageName;
-
- if (!PLATFORM_PACKAGE_NAME.equals(groupSourcePackageName)
- && !doesSignatureMatchForPermissions(groupSourcePackageName,
- parsedPackage, scanFlags)) {
- EventLog.writeEvent(0x534e4554, "146211400", -1,
- parsedPackage.getPackageName());
-
- throw new PrepareFailure(INSTALL_FAILED_BAD_PERMISSION_GROUP,
- "Package "
- + parsedPackage.getPackageName()
- + " attempting to declare permission "
- + perm.getName() + " in group "
- + perm.getGroup() + " owned by package "
- + groupSourcePackageName
- + " with incompatible certificate");
- }
- }
- }
- }
- }
- }
-
- if (systemApp) {
- if (onExternal) {
- // Abort update; system app can't be replaced with app on sdcard
- throw new PrepareFailure(INSTALL_FAILED_INVALID_INSTALL_LOCATION,
- "Cannot install updates to system apps on sdcard");
- } else if (instantApp) {
- // Abort update; system app can't be replaced with an instant app
- throw new PrepareFailure(INSTALL_FAILED_SESSION_INVALID,
- "Cannot update a system app with an instant app");
- }
- }
-
- if (args.mMoveInfo != null) {
- // We did an in-place move, so dex is ready to roll
- scanFlags |= SCAN_NO_DEX;
- scanFlags |= SCAN_MOVE;
-
- synchronized (mPm.mLock) {
- final PackageSetting ps = mPm.mSettings.getPackageLPr(pkgName);
- if (ps == null) {
- res.setError(INSTALL_FAILED_INTERNAL_ERROR,
- "Missing settings for moved package " + pkgName);
- }
-
- // We moved the entire application as-is, so bring over the
- // previously derived ABI information.
- parsedPackage.setPrimaryCpuAbi(ps.getPrimaryCpuAbi())
- .setSecondaryCpuAbi(ps.getSecondaryCpuAbi());
- }
-
- } else {
- // Enable SCAN_NO_DEX flag to skip dexopt at a later stage
- scanFlags |= SCAN_NO_DEX;
-
- try {
- PackageSetting pkgSetting;
- AndroidPackage oldPackage;
- synchronized (mPm.mLock) {
- pkgSetting = mPm.mSettings.getPackageLPr(pkgName);
- oldPackage = mPm.mPackages.get(pkgName);
- }
- boolean isUpdatedSystemAppFromExistingSetting = pkgSetting != null
- && pkgSetting.getPkgState().isUpdatedSystemApp();
- final String abiOverride = deriveAbiOverride(args.mAbiOverride);
- boolean isUpdatedSystemAppInferred = oldPackage != null && oldPackage.isSystem();
- final Pair<PackageAbiHelper.Abis, PackageAbiHelper.NativeLibraryPaths>
- derivedAbi = mPm.mInjector.getAbiHelper().derivePackageAbi(parsedPackage,
- isUpdatedSystemAppFromExistingSetting || isUpdatedSystemAppInferred,
- abiOverride, mPm.mAppLib32InstallDir);
- derivedAbi.first.applyTo(parsedPackage);
- derivedAbi.second.applyTo(parsedPackage);
- } catch (PackageManagerException pme) {
- Slog.e(TAG, "Error deriving application ABI", pme);
- throw new PrepareFailure(INSTALL_FAILED_INTERNAL_ERROR,
- "Error deriving application ABI: " + pme.getMessage());
- }
- }
-
- if (!args.doRename(res.mReturnCode, parsedPackage)) {
- throw new PrepareFailure(INSTALL_FAILED_INSUFFICIENT_STORAGE, "Failed rename");
- }
-
- try {
- setUpFsVerityIfPossible(parsedPackage);
- } catch (Installer.InstallerException | IOException | DigestException
- | NoSuchAlgorithmException e) {
- throw new PrepareFailure(INSTALL_FAILED_INTERNAL_ERROR,
- "Failed to set up verity: " + e);
- }
-
- final PackageFreezer freezer =
- freezePackageForInstall(pkgName, installFlags, "installPackageLI");
- boolean shouldCloseFreezerBeforeReturn = true;
- try {
- final AndroidPackage oldPackage;
- String renamedPackage;
- boolean sysPkg = false;
- int targetScanFlags = scanFlags;
- int targetParseFlags = parseFlags;
- final PackageSetting ps;
- final PackageSetting disabledPs;
- if (replace) {
- final String pkgName11 = parsedPackage.getPackageName();
- synchronized (mPm.mLock) {
- oldPackage = mPm.mPackages.get(pkgName11);
- }
- if (parsedPackage.isStaticSharedLibrary()) {
- // Static libs have a synthetic package name containing the version
- // and cannot be updated as an update would get a new package name,
- // unless this is installed from adb which is useful for development.
- if (oldPackage != null
- && (installFlags & PackageManager.INSTALL_FROM_ADB) == 0) {
- throw new PrepareFailure(INSTALL_FAILED_DUPLICATE_PACKAGE,
- "Packages declaring "
- + "static-shared libs cannot be updated");
- }
- }
-
- final boolean isInstantApp = (scanFlags & SCAN_AS_INSTANT_APP) != 0;
-
- final int[] allUsers;
- final int[] installedUsers;
- final int[] uninstalledUsers;
-
- synchronized (mPm.mLock) {
- if (DEBUG_INSTALL) {
- Slog.d(TAG,
- "replacePackageLI: new=" + parsedPackage + ", old=" + oldPackage);
- }
-
- ps = mPm.mSettings.getPackageLPr(pkgName11);
- disabledPs = mPm.mSettings.getDisabledSystemPkgLPr(ps);
-
- // verify signatures are valid
- final KeySetManagerService ksms = mPm.mSettings.getKeySetManagerService();
- if (ksms.shouldCheckUpgradeKeySetLocked(ps, scanFlags)) {
- if (!ksms.checkUpgradeKeySetLocked(ps, parsedPackage)) {
- throw new PrepareFailure(INSTALL_FAILED_UPDATE_INCOMPATIBLE,
- "New package not signed by keys specified by upgrade-keysets: "
- + pkgName11);
- }
- } else {
- SigningDetails parsedPkgSigningDetails = parsedPackage.getSigningDetails();
- SigningDetails oldPkgSigningDetails = oldPackage.getSigningDetails();
- // default to original signature matching
- if (!parsedPkgSigningDetails.checkCapability(oldPkgSigningDetails,
- SigningDetails.CertCapabilities.INSTALLED_DATA)
- && !oldPkgSigningDetails.checkCapability(parsedPkgSigningDetails,
- SigningDetails.CertCapabilities.ROLLBACK)) {
- // Allow the update to proceed if this is a rollback and the parsed
- // package's current signing key is the current signer or in the lineage
- // of the old package; this allows a rollback to a previously installed
- // version after an app's signing key has been rotated without requiring
- // the rollback capability on the previous signing key.
- if (!isRollback || !oldPkgSigningDetails.hasAncestorOrSelf(
- parsedPkgSigningDetails)) {
- throw new PrepareFailure(INSTALL_FAILED_UPDATE_INCOMPATIBLE,
- "New package has a different signature: " + pkgName11);
- }
- }
- }
-
- // don't allow a system upgrade unless the upgrade hash matches
- if (oldPackage.getRestrictUpdateHash() != null && oldPackage.isSystem()) {
- final byte[] digestBytes;
- try {
- final MessageDigest digest = MessageDigest.getInstance("SHA-512");
- updateDigest(digest, new File(parsedPackage.getBaseApkPath()));
- if (!ArrayUtils.isEmpty(parsedPackage.getSplitCodePaths())) {
- for (String path : parsedPackage.getSplitCodePaths()) {
- updateDigest(digest, new File(path));
- }
- }
- digestBytes = digest.digest();
- } catch (NoSuchAlgorithmException | IOException e) {
- throw new PrepareFailure(INSTALL_FAILED_INVALID_APK,
- "Could not compute hash: " + pkgName11);
- }
- if (!Arrays.equals(oldPackage.getRestrictUpdateHash(), digestBytes)) {
- throw new PrepareFailure(INSTALL_FAILED_INVALID_APK,
- "New package fails restrict-update check: " + pkgName11);
- }
- // retain upgrade restriction
- parsedPackage.setRestrictUpdateHash(oldPackage.getRestrictUpdateHash());
- }
-
- // Check for shared user id changes
- if (!Objects.equals(oldPackage.getSharedUserId(),
- parsedPackage.getSharedUserId())
- // Don't mark as invalid if the app is trying to
- // leave a sharedUserId
- && parsedPackage.getSharedUserId() != null) {
- throw new PrepareFailure(INSTALL_FAILED_UID_CHANGED,
- "Package " + parsedPackage.getPackageName()
- + " shared user changed from "
- + (oldPackage.getSharedUserId() != null
- ? oldPackage.getSharedUserId() : "<nothing>")
- + " to " + parsedPackage.getSharedUserId());
- }
-
- // In case of rollback, remember per-user/profile install state
- allUsers = mPm.mUserManager.getUserIds();
- installedUsers = ps.queryInstalledUsers(allUsers, true);
- uninstalledUsers = ps.queryInstalledUsers(allUsers, false);
-
-
- // don't allow an upgrade from full to ephemeral
- if (isInstantApp) {
- if (args.mUser == null
- || args.mUser.getIdentifier() == UserHandle.USER_ALL) {
- for (int currentUser : allUsers) {
- if (!ps.getInstantApp(currentUser)) {
- // can't downgrade from full to instant
- Slog.w(TAG,
- "Can't replace full app with instant app: " + pkgName11
- + " for user: " + currentUser);
- throw new PrepareFailure(
- PackageManager.INSTALL_FAILED_SESSION_INVALID);
- }
- }
- } else if (!ps.getInstantApp(args.mUser.getIdentifier())) {
- // can't downgrade from full to instant
- Slog.w(TAG, "Can't replace full app with instant app: " + pkgName11
- + " for user: " + args.mUser.getIdentifier());
- throw new PrepareFailure(
- PackageManager.INSTALL_FAILED_SESSION_INVALID);
- }
- }
- }
-
- // Update what is removed
- res.mRemovedInfo = new PackageRemovedInfo(mPm);
- res.mRemovedInfo.mUid = oldPackage.getUid();
- res.mRemovedInfo.mRemovedPackage = oldPackage.getPackageName();
- res.mRemovedInfo.mInstallerPackageName = ps.getInstallSource().installerPackageName;
- res.mRemovedInfo.mIsStaticSharedLib =
- parsedPackage.getStaticSharedLibName() != null;
- res.mRemovedInfo.mIsUpdate = true;
- res.mRemovedInfo.mOrigUsers = installedUsers;
- res.mRemovedInfo.mInstallReasons = new SparseArray<>(installedUsers.length);
- for (int i = 0; i < installedUsers.length; i++) {
- final int userId = installedUsers[i];
- res.mRemovedInfo.mInstallReasons.put(userId, ps.getInstallReason(userId));
- }
- res.mRemovedInfo.mUninstallReasons = new SparseArray<>(uninstalledUsers.length);
- for (int i = 0; i < uninstalledUsers.length; i++) {
- final int userId = uninstalledUsers[i];
- res.mRemovedInfo.mUninstallReasons.put(userId, ps.getUninstallReason(userId));
- }
-
- sysPkg = oldPackage.isSystem();
- if (sysPkg) {
- // Set the system/privileged/oem/vendor/product flags as needed
- final boolean privileged = oldPackage.isPrivileged();
- final boolean oem = oldPackage.isOem();
- final boolean vendor = oldPackage.isVendor();
- final boolean product = oldPackage.isProduct();
- final boolean odm = oldPackage.isOdm();
- final boolean systemExt = oldPackage.isSystemExt();
- final @ParsingPackageUtils.ParseFlags int systemParseFlags = parseFlags;
- final @PackageManagerService.ScanFlags int systemScanFlags = scanFlags
- | SCAN_AS_SYSTEM
- | (privileged ? SCAN_AS_PRIVILEGED : 0)
- | (oem ? SCAN_AS_OEM : 0)
- | (vendor ? SCAN_AS_VENDOR : 0)
- | (product ? SCAN_AS_PRODUCT : 0)
- | (odm ? SCAN_AS_ODM : 0)
- | (systemExt ? SCAN_AS_SYSTEM_EXT : 0);
-
- if (DEBUG_INSTALL) {
- Slog.d(TAG, "replaceSystemPackageLI: new=" + parsedPackage
- + ", old=" + oldPackage);
- }
- res.setReturnCode(PackageManager.INSTALL_SUCCEEDED);
- targetParseFlags = systemParseFlags;
- targetScanFlags = systemScanFlags;
- } else { // non system replace
- replace = true;
- if (DEBUG_INSTALL) {
- Slog.d(TAG,
- "replaceNonSystemPackageLI: new=" + parsedPackage + ", old="
- + oldPackage);
- }
- }
- } else { // new package install
- ps = null;
- disabledPs = null;
- replace = false;
- oldPackage = null;
- // Remember this for later, in case we need to rollback this install
- String pkgName1 = parsedPackage.getPackageName();
-
- if (DEBUG_INSTALL) Slog.d(TAG, "installNewPackageLI: " + parsedPackage);
-
- // TODO(b/194319951): MOVE TO RECONCILE
- synchronized (mPm.mLock) {
- renamedPackage = mPm.mSettings.getRenamedPackageLPr(pkgName1);
- if (renamedPackage != null) {
- // A package with the same name is already installed, though
- // it has been renamed to an older name. The package we
- // are trying to install should be installed as an update to
- // the existing one, but that has not been requested, so bail.
- throw new PrepareFailure(INSTALL_FAILED_ALREADY_EXISTS,
- "Attempt to re-install " + pkgName1
- + " without first uninstalling package running as "
- + renamedPackage);
- }
- if (mPm.mPackages.containsKey(pkgName1)) {
- // Don't allow installation over an existing package with the same name.
- throw new PrepareFailure(INSTALL_FAILED_ALREADY_EXISTS,
- "Attempt to re-install " + pkgName1
- + " without first uninstalling.");
- }
- }
- }
- // we're passing the freezer back to be closed in a later phase of install
- shouldCloseFreezerBeforeReturn = false;
-
- return new PrepareResult(replace, targetScanFlags, targetParseFlags,
- oldPackage, parsedPackage, replace /* clearCodeCache */, sysPkg,
- ps, disabledPs);
- } finally {
- res.mFreezer = freezer;
- if (shouldCloseFreezerBeforeReturn) {
- freezer.close();
- }
- }
- }
-
- /*
- * Cannot properly check CANNOT_INSTALL_WITH_BAD_PERMISSION_GROUPS using CompatChanges
- * as this only works for packages that are installed
- *
- * TODO: Move logic for permission group compatibility into PermissionManagerService
- */
- @SuppressWarnings("AndroidFrameworkCompatChange")
- private static boolean cannotInstallWithBadPermissionGroups(ParsedPackage parsedPackage) {
- return parsedPackage.getTargetSdkVersion() >= Build.VERSION_CODES.S;
- }
-
- private boolean doesSignatureMatchForPermissions(@NonNull String sourcePackageName,
- @NonNull ParsedPackage parsedPackage, int scanFlags) {
- // If the defining package is signed with our cert, it's okay. This
- // also includes the "updating the same package" case, of course.
- // "updating same package" could also involve key-rotation.
-
- final PackageSetting sourcePackageSetting;
- final KeySetManagerService ksms;
- synchronized (mPm.mLock) {
- sourcePackageSetting = mPm.mSettings.getPackageLPr(sourcePackageName);
- ksms = mPm.mSettings.getKeySetManagerService();
- }
-
- final SigningDetails sourceSigningDetails = (sourcePackageSetting == null
- ? SigningDetails.UNKNOWN : sourcePackageSetting.getSigningDetails());
- if (sourcePackageName.equals(parsedPackage.getPackageName())
- && (ksms.shouldCheckUpgradeKeySetLocked(
- sourcePackageSetting, scanFlags))) {
- return ksms.checkUpgradeKeySetLocked(sourcePackageSetting, parsedPackage);
- } else {
-
- // in the event of signing certificate rotation, we need to see if the
- // package's certificate has rotated from the current one, or if it is an
- // older certificate with which the current is ok with sharing permissions
- if (sourceSigningDetails.checkCapability(
- parsedPackage.getSigningDetails(),
- SigningDetails.CertCapabilities.PERMISSION)) {
- return true;
- } else if (parsedPackage.getSigningDetails().checkCapability(
- sourceSigningDetails,
- SigningDetails.CertCapabilities.PERMISSION)) {
- // the scanned package checks out, has signing certificate rotation
- // history, and is newer; bring it over
- synchronized (mPm.mLock) {
- sourcePackageSetting.setSigningDetails(parsedPackage.getSigningDetails());
- }
- return true;
- } else {
- return false;
- }
- }
- }
-
- /**
- * Set up fs-verity for the given package if possible. This requires a feature flag of system
- * property to be enabled only if the kernel supports fs-verity.
- *
- * <p>When the feature flag is set to legacy mode, only APK is supported (with some experimental
- * kernel patches). In normal mode, all file format can be supported.
- */
- private void setUpFsVerityIfPossible(AndroidPackage pkg) throws Installer.InstallerException,
- PrepareFailure, IOException, DigestException, NoSuchAlgorithmException {
- final boolean standardMode = PackageManagerServiceUtils.isApkVerityEnabled();
- final boolean legacyMode = PackageManagerServiceUtils.isLegacyApkVerityEnabled();
- if (!standardMode && !legacyMode) {
- return;
- }
-
- if (isIncrementalPath(pkg.getPath()) && IncrementalManager.getVersion()
- < IncrementalManager.MIN_VERSION_TO_SUPPORT_FSVERITY) {
- return;
- }
-
- // Collect files we care for fs-verity setup.
- ArrayMap<String, String> fsverityCandidates = new ArrayMap<>();
- if (legacyMode) {
- synchronized (mPm.mLock) {
- final PackageSetting ps = mPm.mSettings.getPackageLPr(pkg.getPackageName());
- if (ps != null && ps.isPrivileged()) {
- fsverityCandidates.put(pkg.getBaseApkPath(), null);
- if (pkg.getSplitCodePaths() != null) {
- for (String splitPath : pkg.getSplitCodePaths()) {
- fsverityCandidates.put(splitPath, null);
- }
- }
- }
- }
- } else {
- // NB: These files will become only accessible if the signing key is loaded in kernel's
- // .fs-verity keyring.
- fsverityCandidates.put(pkg.getBaseApkPath(),
- VerityUtils.getFsveritySignatureFilePath(pkg.getBaseApkPath()));
-
- final String dmPath = DexMetadataHelper.buildDexMetadataPathForApk(
- pkg.getBaseApkPath());
- if (new File(dmPath).exists()) {
- fsverityCandidates.put(dmPath, VerityUtils.getFsveritySignatureFilePath(dmPath));
- }
-
- if (pkg.getSplitCodePaths() != null) {
- for (String path : pkg.getSplitCodePaths()) {
- fsverityCandidates.put(path, VerityUtils.getFsveritySignatureFilePath(path));
-
- final String splitDmPath = DexMetadataHelper.buildDexMetadataPathForApk(path);
- if (new File(splitDmPath).exists()) {
- fsverityCandidates.put(splitDmPath,
- VerityUtils.getFsveritySignatureFilePath(splitDmPath));
- }
- }
- }
- }
-
- for (Map.Entry<String, String> entry : fsverityCandidates.entrySet()) {
- final String filePath = entry.getKey();
- final String signaturePath = entry.getValue();
-
- if (!legacyMode) {
- // fs-verity is optional for now. Only set up if signature is provided.
- if (new File(signaturePath).exists() && !VerityUtils.hasFsverity(filePath)) {
- try {
- VerityUtils.setUpFsverity(filePath, signaturePath);
- } catch (IOException e) {
- throw new PrepareFailure(PackageManager.INSTALL_FAILED_BAD_SIGNATURE,
- "Failed to enable fs-verity: " + e);
- }
- }
- continue;
- }
-
- // In legacy mode, fs-verity can only be enabled by process with CAP_SYS_ADMIN.
- final VerityUtils.SetupResult result = VerityUtils.generateApkVeritySetupData(filePath);
- if (result.isOk()) {
- if (Build.IS_DEBUGGABLE) Slog.i(TAG, "Enabling verity to " + filePath);
- final FileDescriptor fd = result.getUnownedFileDescriptor();
- try {
- final byte[] rootHash = VerityUtils.generateApkVerityRootHash(filePath);
- try {
- // A file may already have fs-verity, e.g. when reused during a split
- // install. If the measurement succeeds, no need to attempt to set up.
- mPm.mInstaller.assertFsverityRootHashMatches(filePath, rootHash);
- } catch (Installer.InstallerException e) {
- mPm.mInstaller.installApkVerity(filePath, fd, result.getContentSize());
- mPm.mInstaller.assertFsverityRootHashMatches(filePath, rootHash);
- }
- } finally {
- IoUtils.closeQuietly(fd);
- }
- } else if (result.isFailed()) {
- throw new PrepareFailure(PackageManager.INSTALL_FAILED_BAD_SIGNATURE,
- "Failed to generate verity");
- }
- }
- }
-
- private PackageFreezer freezePackageForInstall(String packageName, int installFlags,
- String killReason) {
- return freezePackageForInstall(packageName, UserHandle.USER_ALL, installFlags, killReason);
- }
-
- private PackageFreezer freezePackageForInstall(String packageName, int userId, int installFlags,
- String killReason) {
- if ((installFlags & PackageManager.INSTALL_DONT_KILL_APP) != 0) {
- return new PackageFreezer(mPm);
- } else {
- return mPm.freezePackage(packageName, userId, killReason);
- }
- }
-
- private static void updateDigest(MessageDigest digest, File file) throws IOException {
- try (DigestInputStream digestStream =
- new DigestInputStream(new FileInputStream(file), digest)) {
- int length, total = 0;
- while ((length = digestStream.read()) != -1) {
- total += length;
- } // just plow through the file
- }
- }
-
- @GuardedBy("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.)
- for (ReconciledPackage reconciledPkg : request.mReconciledPackages.values()) {
- final ScanResult scanResult = reconciledPkg.mScanResult;
- final ScanRequest scanRequest = scanResult.mRequest;
- final ParsedPackage parsedPackage = scanRequest.mParsedPackage;
- final String packageName = parsedPackage.getPackageName();
- final PackageInstalledInfo res = reconciledPkg.mInstallResult;
- final RemovePackageHelper removePackageHelper = new RemovePackageHelper(mPm);
- final DeletePackageHelper deletePackageHelper = new DeletePackageHelper(mPm);
-
- if (reconciledPkg.mPrepareResult.mReplace) {
- AndroidPackage oldPackage = mPm.mPackages.get(packageName);
-
- // Set the update and install times
- PackageStateInternal deletedPkgSetting = mPm.getPackageStateInternal(
- oldPackage.getPackageName());
- reconciledPkg.mPkgSetting
- .setFirstInstallTime(deletedPkgSetting.getFirstInstallTime())
- .setLastUpdateTime(System.currentTimeMillis());
-
- res.mRemovedInfo.mBroadcastAllowList = mPm.mAppsFilter.getVisibilityAllowList(
- reconciledPkg.mPkgSetting, request.mAllUsers,
- mPm.mSettings.getPackagesLocked());
- if (reconciledPkg.mPrepareResult.mSystem) {
- // Remove existing system package
- removePackageHelper.removePackageLI(oldPackage, true);
- if (!disableSystemPackageLPw(oldPackage)) {
- // We didn't need to disable the .apk as a current system package,
- // which means we are replacing another update that is already
- // installed. We need to make sure to delete the older one's .apk.
- res.mRemovedInfo.mArgs = new FileInstallArgs(
- oldPackage.getPath(),
- getAppDexInstructionSets(
- AndroidPackageUtils.getPrimaryCpuAbi(oldPackage,
- deletedPkgSetting),
- AndroidPackageUtils.getSecondaryCpuAbi(oldPackage,
- deletedPkgSetting)), mPm);
- } else {
- res.mRemovedInfo.mArgs = null;
- }
- } else {
- try {
- // Settings will be written during the call to updateSettingsLI().
- deletePackageHelper.executeDeletePackageLIF(
- reconciledPkg.mDeletePackageAction, packageName,
- true, request.mAllUsers, false);
- } catch (SystemDeleteException e) {
- if (mPm.mIsEngBuild) {
- throw new RuntimeException("Unexpected failure", e);
- // ignore; not possible for non-system app
- }
- }
- // Successfully deleted the old package; proceed with replace.
-
- // If deleted package lived in a container, give users a chance to
- // relinquish resources before killing.
- if (oldPackage.isExternalStorage()) {
- if (DEBUG_INSTALL) {
- Slog.i(TAG, "upgrading pkg " + oldPackage
- + " is ASEC-hosted -> UNAVAILABLE");
- }
- final int[] uidArray = new int[]{oldPackage.getUid()};
- final ArrayList<String> pkgList = new ArrayList<>(1);
- pkgList.add(oldPackage.getPackageName());
- mBroadcastHelper.sendResourcesChangedBroadcast(
- false, true, pkgList, uidArray, null);
- }
-
- // Update the in-memory copy of the previous code paths.
- PackageSetting ps1 = mPm.mSettings.getPackageLPr(
- reconciledPkg.mPrepareResult.mExistingPackage.getPackageName());
- if ((reconciledPkg.mInstallArgs.mInstallFlags & PackageManager.DONT_KILL_APP)
- == 0) {
- if (ps1.mOldCodePaths == null) {
- ps1.mOldCodePaths = new ArraySet<>();
- }
- Collections.addAll(ps1.mOldCodePaths, oldPackage.getBaseApkPath());
- if (oldPackage.getSplitCodePaths() != null) {
- Collections.addAll(ps1.mOldCodePaths, oldPackage.getSplitCodePaths());
- }
- } else {
- ps1.mOldCodePaths = null;
- }
-
- if (reconciledPkg.mInstallResult.mReturnCode
- == PackageManager.INSTALL_SUCCEEDED) {
- PackageSetting ps2 = mPm.mSettings.getPackageLPr(
- parsedPackage.getPackageName());
- if (ps2 != null) {
- res.mRemovedInfo.mRemovedForAllUsers =
- mPm.mPackages.get(ps2.getPackageName()) == null;
- }
- }
- }
- }
-
- AndroidPackage pkg = mInstallPackageHelper.commitReconciledScanResultLocked(
- reconciledPkg, request.mAllUsers);
- updateSettingsLI(pkg, reconciledPkg, request.mAllUsers, res);
-
- final PackageSetting ps = mPm.mSettings.getPackageLPr(packageName);
- if (ps != null) {
- res.mNewUsers = ps.queryInstalledUsers(mPm.mUserManager.getUserIds(), true);
- ps.setUpdateAvailable(false /*updateAvailable*/);
- }
- if (res.mReturnCode == PackageManager.INSTALL_SUCCEEDED) {
- mPm.updateSequenceNumberLP(ps, res.mNewUsers);
- mPm.updateInstantAppInstallerLocked(packageName);
- }
- }
- ApplicationPackageManager.invalidateGetPackagesForUidCache();
- }
-
- @GuardedBy("mLock")
- private boolean disableSystemPackageLPw(AndroidPackage oldPkg) {
- return mPm.mSettings.disableSystemPackageLPw(oldPkg.getPackageName(), true);
- }
-
- private void updateSettingsLI(AndroidPackage newPackage, ReconciledPackage reconciledPkg,
- int[] allUsers, PackageInstalledInfo res) {
- updateSettingsInternalLI(newPackage, reconciledPkg, allUsers, res);
- }
-
- private void updateSettingsInternalLI(AndroidPackage pkg, ReconciledPackage reconciledPkg,
- int[] allUsers, PackageInstalledInfo res) {
- Trace.traceBegin(TRACE_TAG_PACKAGE_MANAGER, "updateSettings");
-
- final String pkgName = pkg.getPackageName();
- final int[] installedForUsers = res.mOrigUsers;
- final InstallArgs installArgs = reconciledPkg.mInstallArgs;
- final int installReason = installArgs.mInstallReason;
- InstallSource installSource = installArgs.mInstallSource;
- final String installerPackageName = installSource.installerPackageName;
-
- if (DEBUG_INSTALL) Slog.d(TAG, "New package installed in " + pkg.getPath());
- synchronized (mPm.mLock) {
- // For system-bundled packages, we assume that installing an upgraded version
- // of the package implies that the user actually wants to run that new code,
- // so we enable the package.
- final PackageSetting ps = mPm.mSettings.getPackageLPr(pkgName);
- final int userId = installArgs.mUser.getIdentifier();
- if (ps != null) {
- if (pkg.isSystem()) {
- if (DEBUG_INSTALL) {
- Slog.d(TAG, "Implicitly enabling system package on upgrade: " + pkgName);
- }
- // Enable system package for requested users
- if (res.mOrigUsers != null) {
- for (int origUserId : res.mOrigUsers) {
- if (userId == UserHandle.USER_ALL || userId == origUserId) {
- ps.setEnabled(COMPONENT_ENABLED_STATE_DEFAULT,
- origUserId, installerPackageName);
- }
- }
- }
- // Also convey the prior install/uninstall state
- if (allUsers != null && installedForUsers != null) {
- for (int currentUserId : allUsers) {
- final boolean installed = ArrayUtils.contains(
- installedForUsers, currentUserId);
- if (DEBUG_INSTALL) {
- Slog.d(TAG, " user " + currentUserId + " => " + installed);
- }
- ps.setInstalled(installed, currentUserId);
- }
- // these install state changes will be persisted in the
- // upcoming call to mSettings.writeLPr().
- }
-
- if (allUsers != null) {
- for (int currentUserId : allUsers) {
- ps.resetOverrideComponentLabelIcon(currentUserId);
- }
- }
- }
-
- // Retrieve the overlays for shared libraries of the package.
- if (!ps.getPkgState().getUsesLibraryInfos().isEmpty()) {
- for (SharedLibraryInfo sharedLib : ps.getPkgState().getUsesLibraryInfos()) {
- for (int currentUserId : UserManagerService.getInstance().getUserIds()) {
- if (!sharedLib.isDynamic()) {
- // TODO(146804378): Support overlaying static shared libraries
- continue;
- }
- final PackageSetting libPs = mPm.mSettings.getPackageLPr(
- sharedLib.getPackageName());
- if (libPs == null) {
- continue;
- }
- ps.setOverlayPathsForLibrary(sharedLib.getName(),
- libPs.getOverlayPaths(currentUserId), currentUserId);
- }
- }
- }
-
- // It's implied that when a user requests installation, they want the app to be
- // installed and enabled. (This does not apply to USER_ALL, which here means only
- // install on users for which the app is already installed).
- if (userId != UserHandle.USER_ALL) {
- ps.setInstalled(true, userId);
- ps.setEnabled(COMPONENT_ENABLED_STATE_DEFAULT, userId, installerPackageName);
- }
-
- mPm.mSettings.addInstallerPackageNames(ps.getInstallSource());
-
- // When replacing an existing package, preserve the original install reason for all
- // users that had the package installed before. Similarly for uninstall reasons.
- final Set<Integer> previousUserIds = new ArraySet<>();
- if (res.mRemovedInfo != null && res.mRemovedInfo.mInstallReasons != null) {
- final int installReasonCount = res.mRemovedInfo.mInstallReasons.size();
- for (int i = 0; i < installReasonCount; i++) {
- final int previousUserId = res.mRemovedInfo.mInstallReasons.keyAt(i);
- final int previousInstallReason =
- res.mRemovedInfo.mInstallReasons.valueAt(i);
- ps.setInstallReason(previousInstallReason, previousUserId);
- previousUserIds.add(previousUserId);
- }
- }
- if (res.mRemovedInfo != null && res.mRemovedInfo.mUninstallReasons != null) {
- for (int i = 0; i < res.mRemovedInfo.mUninstallReasons.size(); i++) {
- final int previousUserId = res.mRemovedInfo.mUninstallReasons.keyAt(i);
- final int previousReason = res.mRemovedInfo.mUninstallReasons.valueAt(i);
- ps.setUninstallReason(previousReason, previousUserId);
- }
- }
-
- // Set install reason for users that are having the package newly installed.
- final int[] allUsersList = mPm.mUserManager.getUserIds();
- if (userId == UserHandle.USER_ALL) {
- // TODO(b/152629990): It appears that the package doesn't actually get newly
- // installed in this case, so the installReason shouldn't get modified?
- for (int currentUserId : allUsersList) {
- if (!previousUserIds.contains(currentUserId)) {
- ps.setInstallReason(installReason, currentUserId);
- }
- }
- } else if (!previousUserIds.contains(userId)) {
- ps.setInstallReason(installReason, userId);
- }
-
- // TODO(b/169721400): generalize Incremental States and create a Callback object
- // that can be used for all the packages.
- final String codePath = ps.getPathString();
- if (IncrementalManager.isIncrementalPath(codePath)
- && mPm.mIncrementalManager != null) {
- mPm.mIncrementalManager.registerLoadingProgressCallback(codePath,
- new IncrementalProgressListener(ps.getPackageName(), mPm));
- }
-
- // Ensure that the uninstall reason is UNKNOWN for users with the package installed.
- for (int currentUserId : allUsersList) {
- if (ps.getInstalled(currentUserId)) {
- ps.setUninstallReason(UNINSTALL_REASON_UNKNOWN, currentUserId);
- }
- }
-
- mPm.mSettings.writeKernelMappingLPr(ps);
-
- final PermissionManagerServiceInternal.PackageInstalledParams.Builder
- permissionParamsBuilder =
- new PermissionManagerServiceInternal.PackageInstalledParams.Builder();
- final boolean grantPermissions = (installArgs.mInstallFlags
- & PackageManager.INSTALL_GRANT_RUNTIME_PERMISSIONS) != 0;
- if (grantPermissions) {
- final List<String> grantedPermissions =
- installArgs.mInstallGrantPermissions != null
- ? Arrays.asList(installArgs.mInstallGrantPermissions)
- : pkg.getRequestedPermissions();
- permissionParamsBuilder.setGrantedPermissions(grantedPermissions);
- }
- final boolean allowlistAllRestrictedPermissions =
- (installArgs.mInstallFlags
- & PackageManager.INSTALL_ALL_WHITELIST_RESTRICTED_PERMISSIONS) != 0;
- final List<String> allowlistedRestrictedPermissions =
- allowlistAllRestrictedPermissions ? pkg.getRequestedPermissions()
- : installArgs.mAllowlistedRestrictedPermissions;
- if (allowlistedRestrictedPermissions != null) {
- permissionParamsBuilder.setAllowlistedRestrictedPermissions(
- allowlistedRestrictedPermissions);
- }
- final int autoRevokePermissionsMode = installArgs.mAutoRevokePermissionsMode;
- permissionParamsBuilder.setAutoRevokePermissionsMode(autoRevokePermissionsMode);
- final ScanResult scanResult = reconciledPkg.mScanResult;
- mPm.mPermissionManager.onPackageInstalled(pkg, scanResult.mPreviousAppId,
- permissionParamsBuilder.build(), userId);
- }
- res.mName = pkgName;
- res.mUid = pkg.getUid();
- res.mPkg = pkg;
- res.setReturnCode(PackageManager.INSTALL_SUCCEEDED);
- //to update install status
- Trace.traceBegin(TRACE_TAG_PACKAGE_MANAGER, "writeSettings");
- mPm.writeSettingsLPrTEMP();
- Trace.traceEnd(TRACE_TAG_PACKAGE_MANAGER);
- }
-
- Trace.traceEnd(TRACE_TAG_PACKAGE_MANAGER);
- }
-
- /**
- * On successful install, executes remaining steps after commit completes and the package lock
- * is released. These are typically more expensive or require calls to installd, which often
- * locks on {@link com.android.server.pm.PackageManagerService.mLock}.
- */
- private void executePostCommitSteps(CommitRequest commitRequest) {
- final ArraySet<IncrementalStorage> incrementalStorages = new ArraySet<>();
- final AppDataHelper appDataHelper = new AppDataHelper(mPm);
- for (ReconciledPackage reconciledPkg : commitRequest.mReconciledPackages.values()) {
- final boolean instantApp = ((reconciledPkg.mScanResult.mRequest.mScanFlags
- & SCAN_AS_INSTANT_APP) != 0);
- final AndroidPackage pkg = reconciledPkg.mPkgSetting.getPkg();
- final String packageName = pkg.getPackageName();
- final String codePath = pkg.getPath();
- final boolean onIncremental = mPm.mIncrementalManager != null
- && isIncrementalPath(codePath);
- if (onIncremental) {
- IncrementalStorage storage = mPm.mIncrementalManager.openStorage(codePath);
- if (storage == null) {
- throw new IllegalArgumentException(
- "Install: null storage for incremental package " + packageName);
- }
- incrementalStorages.add(storage);
- }
- int previousAppId = 0;
- if (reconciledPkg.mScanResult.needsNewAppId()) {
- // Only set previousAppId if the app is migrating out of shared UID
- previousAppId = reconciledPkg.mScanResult.mPreviousAppId;
- }
- appDataHelper.prepareAppDataPostCommitLIF(pkg, previousAppId);
- if (reconciledPkg.mPrepareResult.mClearCodeCache) {
- appDataHelper.clearAppDataLIF(pkg, UserHandle.USER_ALL,
- FLAG_STORAGE_DE | FLAG_STORAGE_CE | FLAG_STORAGE_EXTERNAL
- | Installer.FLAG_CLEAR_CODE_CACHE_ONLY);
- }
- if (reconciledPkg.mPrepareResult.mReplace) {
- mPm.getDexManager().notifyPackageUpdated(pkg.getPackageName(),
- pkg.getBaseApkPath(), pkg.getSplitCodePaths());
- }
-
- // Prepare the application profiles for the new code paths.
- // This needs to be done before invoking dexopt so that any install-time profile
- // can be used for optimizations.
- mPm.mArtManagerService.prepareAppProfiles(
- pkg,
- mPm.resolveUserIds(reconciledPkg.mInstallArgs.mUser.getIdentifier()),
- /* updateReferenceProfileContent= */ true);
-
- // Compute the compilation reason from the installation scenario.
- final int compilationReason =
- mPm.getDexManager().getCompilationReasonForInstallScenario(
- reconciledPkg.mInstallArgs.mInstallScenario);
-
- // Construct the DexoptOptions early to see if we should skip running dexopt.
- //
- // Do not run PackageDexOptimizer through the local performDexOpt
- // method because `pkg` may not be in `mPackages` yet.
- //
- // Also, don't fail application installs if the dexopt step fails.
- final boolean isBackupOrRestore =
- reconciledPkg.mInstallArgs.mInstallReason == INSTALL_REASON_DEVICE_RESTORE
- || reconciledPkg.mInstallArgs.mInstallReason
- == INSTALL_REASON_DEVICE_SETUP;
-
- final int dexoptFlags = DexoptOptions.DEXOPT_BOOT_COMPLETE
- | DexoptOptions.DEXOPT_INSTALL_WITH_DEX_METADATA_FILE
- | (isBackupOrRestore ? DexoptOptions.DEXOPT_FOR_RESTORE : 0);
- DexoptOptions dexoptOptions =
- new DexoptOptions(packageName, compilationReason, dexoptFlags);
-
- // Check whether we need to dexopt the app.
- //
- // NOTE: it is IMPORTANT to call dexopt:
- // - after doRename which will sync the package data from AndroidPackage and
- // its corresponding ApplicationInfo.
- // - after installNewPackageLIF or replacePackageLIF which will update result with the
- // uid of the application (pkg.applicationInfo.uid).
- // This update happens in place!
- //
- // We only need to dexopt if the package meets ALL of the following conditions:
- // 1) it is not an instant app or if it is then dexopt is enabled via gservices.
- // 2) it is not debuggable.
- // 3) it is not on Incremental File System.
- //
- // Note that we do not dexopt instant apps by default. dexopt can take some time to
- // complete, so we skip this step during installation. Instead, we'll take extra time
- // the first time the instant app starts. It's preferred to do it this way to provide
- // continuous progress to the useur instead of mysteriously blocking somewhere in the
- // middle of running an instant app. The default behaviour can be overridden
- // via gservices.
- //
- // Furthermore, dexopt may be skipped, depending on the install scenario and current
- // state of the device.
- //
- // TODO(b/174695087): instantApp and onIncremental should be removed and their install
- // path moved to SCENARIO_FAST.
- final boolean performDexopt =
- (!instantApp || android.provider.Settings.Global.getInt(
- mPm.mContext.getContentResolver(),
- android.provider.Settings.Global.INSTANT_APP_DEXOPT_ENABLED, 0) != 0)
- && !pkg.isDebuggable()
- && (!onIncremental)
- && dexoptOptions.isCompilationEnabled();
-
- if (performDexopt) {
- // Compile the layout resources.
- if (SystemProperties.getBoolean(PRECOMPILE_LAYOUTS, false)) {
- Trace.traceBegin(TRACE_TAG_PACKAGE_MANAGER, "compileLayouts");
- mPm.mViewCompiler.compileLayouts(pkg);
- Trace.traceEnd(TRACE_TAG_PACKAGE_MANAGER);
- }
-
- Trace.traceBegin(TRACE_TAG_PACKAGE_MANAGER, "dexopt");
- ScanResult result = reconciledPkg.mScanResult;
-
- // This mirrors logic from commitReconciledScanResultLocked, where the library files
- // needed for dexopt are assigned.
- // TODO: Fix this to have 1 mutable PackageSetting for scan/install. If the previous
- // setting needs to be passed to have a comparison, hide it behind an immutable
- // interface. There's no good reason to have 3 different ways to access the real
- // PackageSetting object, only one of which is actually correct.
- PackageSetting realPkgSetting = result.mExistingSettingCopied
- ? result.mRequest.mPkgSetting : result.mPkgSetting;
- if (realPkgSetting == null) {
- realPkgSetting = reconciledPkg.mPkgSetting;
- }
-
- // Unfortunately, the updated system app flag is only tracked on this PackageSetting
- boolean isUpdatedSystemApp = reconciledPkg.mPkgSetting.getPkgState()
- .isUpdatedSystemApp();
-
- realPkgSetting.getPkgState().setUpdatedSystemApp(isUpdatedSystemApp);
-
- mPm.mPackageDexOptimizer.performDexOpt(pkg, realPkgSetting,
- null /* instructionSets */,
- mPm.getOrCreateCompilerPackageStats(pkg),
- mPm.getDexManager().getPackageUseInfoOrDefault(packageName),
- dexoptOptions);
- Trace.traceEnd(TRACE_TAG_PACKAGE_MANAGER);
- }
-
- // Notify BackgroundDexOptService that the package has been changed.
- // If this is an update of a package which used to fail to compile,
- // BackgroundDexOptService will remove it from its denylist.
- // TODO: Layering violation
- BackgroundDexOptService.getService().notifyPackageChanged(packageName);
-
- notifyPackageChangeObserversOnUpdate(reconciledPkg);
- }
- waitForNativeBinariesExtraction(incrementalStorages);
- }
-
- private void notifyPackageChangeObserversOnUpdate(ReconciledPackage reconciledPkg) {
- final PackageSetting pkgSetting = reconciledPkg.mPkgSetting;
- final PackageInstalledInfo pkgInstalledInfo = reconciledPkg.mInstallResult;
- final PackageRemovedInfo pkgRemovedInfo = pkgInstalledInfo.mRemovedInfo;
-
- PackageChangeEvent pkgChangeEvent = new PackageChangeEvent();
- pkgChangeEvent.packageName = pkgSetting.getPkg().getPackageName();
- pkgChangeEvent.version = pkgSetting.getVersionCode();
- pkgChangeEvent.lastUpdateTimeMillis = pkgSetting.getLastUpdateTime();
- pkgChangeEvent.newInstalled = (pkgRemovedInfo == null || !pkgRemovedInfo.mIsUpdate);
- pkgChangeEvent.dataRemoved = (pkgRemovedInfo != null && pkgRemovedInfo.mDataRemoved);
- pkgChangeEvent.isDeleted = false;
-
- mPm.notifyPackageChangeObservers(pkgChangeEvent);
- }
-
- static void waitForNativeBinariesExtraction(
- ArraySet<IncrementalStorage> incrementalStorages) {
- if (incrementalStorages.isEmpty()) {
- return;
- }
- try {
- // Native library extraction may take very long time: each page could potentially
- // wait for either 10s or 100ms (adb vs non-adb data loader), and that easily adds
- // up to a full watchdog timeout of 1 min, killing the system after that. It doesn't
- // make much sense as blocking here doesn't lock up the framework, but only blocks
- // the installation session and the following ones.
- Watchdog.getInstance().pauseWatchingCurrentThread("native_lib_extract");
- for (int i = 0; i < incrementalStorages.size(); ++i) {
- IncrementalStorage storage = incrementalStorages.valueAtUnchecked(i);
- storage.waitForNativeBinariesExtraction();
- }
- } finally {
- Watchdog.getInstance().resumeWatchingCurrentThread("native_lib_extract");
- }
- }
-
/**
* Ensure that the install reason matches what we know about the package installer (e.g. whether
* it is acting on behalf on an enterprise or the user).
diff --git a/services/core/java/com/android/server/pm/PackageHandler.java b/services/core/java/com/android/server/pm/PackageHandler.java
index e3a772c..1a9c7a9 100644
--- a/services/core/java/com/android/server/pm/PackageHandler.java
+++ b/services/core/java/com/android/server/pm/PackageHandler.java
@@ -16,21 +16,16 @@
package com.android.server.pm;
-import static android.content.pm.PackageManager.INSTALL_FAILED_PACKAGE_CHANGED;
-import static android.os.PowerExemptionManager.REASON_PACKAGE_REPLACED;
import static android.os.Trace.TRACE_TAG_PACKAGE_MANAGER;
import static com.android.server.pm.PackageManagerService.CHECK_PENDING_INTEGRITY_VERIFICATION;
import static com.android.server.pm.PackageManagerService.CHECK_PENDING_VERIFICATION;
-import static com.android.server.pm.PackageManagerService.DEBUG_BACKUP;
import static com.android.server.pm.PackageManagerService.DEBUG_INSTALL;
import static com.android.server.pm.PackageManagerService.DEFAULT_UNUSED_STATIC_SHARED_LIB_MIN_CACHE_PERIOD;
import static com.android.server.pm.PackageManagerService.DEFAULT_VERIFICATION_RESPONSE;
import static com.android.server.pm.PackageManagerService.DEFERRED_NO_KILL_INSTALL_OBSERVER;
import static com.android.server.pm.PackageManagerService.DEFERRED_NO_KILL_POST_DELETE;
-import static com.android.server.pm.PackageManagerService.DEFERRED_NO_KILL_POST_DELETE_DELAY_MS;
import static com.android.server.pm.PackageManagerService.DOMAIN_VERIFICATION;
-import static com.android.server.pm.PackageManagerService.EMPTY_INT_ARRAY;
import static com.android.server.pm.PackageManagerService.ENABLE_ROLLBACK_STATUS;
import static com.android.server.pm.PackageManagerService.ENABLE_ROLLBACK_TIMEOUT;
import static com.android.server.pm.PackageManagerService.INIT_COPY;
@@ -48,15 +43,11 @@
import static com.android.server.pm.PackageManagerService.WRITE_SETTINGS;
import android.content.Intent;
-import android.content.pm.IPackageInstallObserver2;
import android.content.pm.InstantAppRequest;
-import android.content.pm.PackageInfo;
-import android.content.pm.PackageInstaller;
import android.content.pm.PackageManager;
import android.content.pm.PackageManagerInternal;
import android.net.Uri;
import android.os.Binder;
-import android.os.Bundle;
import android.os.Handler;
import android.os.Looper;
import android.os.Message;
@@ -65,42 +56,22 @@
import android.os.UserHandle;
import android.os.UserManager;
import android.provider.Settings;
-import android.os.storage.StorageManager;
-import android.os.storage.VolumeInfo;
-import android.stats.storage.StorageEnums;
-import android.util.ArrayMap;
-import android.util.EventLog;
import android.util.Log;
import android.util.Slog;
-import android.util.SparseArray;
-
-import com.android.internal.util.ArrayUtils;
-import com.android.internal.util.FrameworkStatsLog;
-import com.android.server.EventLogTags;
-import com.android.server.pm.parsing.pkg.AndroidPackage;
-import com.android.server.pm.pkg.PackageStateInternal;
-
-import dalvik.system.VMRuntime;
-
-import java.util.ArrayList;
-import java.util.Arrays;
-import java.util.Collections;
import java.io.IOException;
/**
* Part of PackageManagerService that handles events.
*/
-public final class PackageHandler extends Handler {
+final class PackageHandler extends Handler {
private final PackageManagerService mPm;
- private final VerificationHelper mVerificationHelper;
- private final BroadcastHelper mBroadcastHelper;
+ private final InstallPackageHelper mInstallPackageHelper;
PackageHandler(Looper looper, PackageManagerService pm) {
super(looper);
mPm = pm;
- mVerificationHelper = new VerificationHelper(mPm.mContext);
- mBroadcastHelper = new BroadcastHelper(mPm.mInjector);
+ mInstallPackageHelper = new InstallPackageHelper(mPm);
}
@Override
@@ -127,46 +98,7 @@
break;
}
case SEND_PENDING_BROADCAST: {
- String[] packages;
- ArrayList<String>[] components;
- int size = 0;
- int[] uids;
- Process.setThreadPriority(Process.THREAD_PRIORITY_DEFAULT);
- synchronized (mPm.mLock) {
- size = mPm.mPendingBroadcasts.size();
- if (size <= 0) {
- // Nothing to be done. Just return
- return;
- }
- packages = new String[size];
- components = new ArrayList[size];
- uids = new int[size];
- int i = 0; // filling out the above arrays
-
- for (int n = 0; n < mPm.mPendingBroadcasts.userIdCount(); n++) {
- final int packageUserId = mPm.mPendingBroadcasts.userIdAt(n);
- final ArrayMap<String, ArrayList<String>> componentsToBroadcast =
- mPm.mPendingBroadcasts.packagesForUserId(packageUserId);
- final int numComponents = componentsToBroadcast.size();
- for (int index = 0; i < size && index < numComponents; index++) {
- packages[i] = componentsToBroadcast.keyAt(index);
- components[i] = componentsToBroadcast.valueAt(index);
- final PackageSetting ps = mPm.mSettings.getPackageLPr(packages[i]);
- uids[i] = (ps != null)
- ? UserHandle.getUid(packageUserId, ps.getAppId())
- : -1;
- i++;
- }
- }
- size = i;
- mPm.mPendingBroadcasts.clear();
- }
- // Send broadcasts
- for (int i = 0; i < size; i++) {
- mPm.sendPackageChangedBroadcast(packages[i], true /* dontKillApp */,
- components[i], uids[i], null /* reason */);
- }
- Process.setThreadPriority(Process.THREAD_PRIORITY_BACKGROUND);
+ mInstallPackageHelper.sendPendingBroadcasts();
break;
}
case POST_INSTALL: {
@@ -183,23 +115,7 @@
if (data != null && data.mPostInstallRunnable != null) {
data.mPostInstallRunnable.run();
} else if (data != null && data.args != null) {
- InstallArgs args = data.args;
- PackageInstalledInfo parentRes = data.res;
-
- final boolean killApp = (args.mInstallFlags
- & PackageManager.INSTALL_DONT_KILL_APP) == 0;
- final boolean virtualPreload = ((args.mInstallFlags
- & PackageManager.INSTALL_VIRTUAL_PRELOAD) != 0);
-
- handlePackagePostInstall(parentRes, killApp, virtualPreload,
- didRestore, args.mInstallSource.installerPackageName,
- args.mObserver, args.mDataLoaderType);
-
- // Log tracing if needed
- if (args.mTraceMethod != null) {
- Trace.asyncTraceEnd(TRACE_TAG_PACKAGE_MANAGER, args.mTraceMethod,
- args.mTraceCookie);
- }
+ mInstallPackageHelper.handlePackagePostInstall(data.res, data.args, didRestore);
} else if (DEBUG_INSTALL) {
// No post-install when we run restore from installExistingPackageForUser
Slog.i(TAG, "Nothing to do for post-install token " + msg.arg1);
@@ -223,31 +139,17 @@
} break;
case WRITE_SETTINGS: {
Process.setThreadPriority(Process.THREAD_PRIORITY_DEFAULT);
- synchronized (mPm.mLock) {
- removeMessages(WRITE_SETTINGS);
- removeMessages(WRITE_PACKAGE_RESTRICTIONS);
- mPm.writeSettingsLPrTEMP();
- mPm.mDirtyUsers.clear();
- }
+ mPm.writeSettings();
Process.setThreadPriority(Process.THREAD_PRIORITY_BACKGROUND);
} break;
case WRITE_PACKAGE_RESTRICTIONS: {
Process.setThreadPriority(Process.THREAD_PRIORITY_DEFAULT);
- synchronized (mPm.mLock) {
- removeMessages(WRITE_PACKAGE_RESTRICTIONS);
- for (int userId : mPm.mDirtyUsers) {
- mPm.mSettings.writePackageRestrictionsLPr(userId);
- }
- mPm.mDirtyUsers.clear();
- }
+ mPm.writePendingRestrictions();
Process.setThreadPriority(Process.THREAD_PRIORITY_BACKGROUND);
} break;
case WRITE_PACKAGE_LIST: {
Process.setThreadPriority(Process.THREAD_PRIORITY_DEFAULT);
- synchronized (mPm.mLock) {
- removeMessages(WRITE_PACKAGE_LIST);
- mPm.mSettings.writePackageListLPr(msg.arg1);
- }
+ mPm.writePackageList(msg.arg1);
Process.setThreadPriority(Process.THREAD_PRIORITY_BACKGROUND);
} break;
case CHECK_PENDING_VERIFICATION: {
@@ -268,13 +170,13 @@
Slog.i(TAG, "Continuing with installation of " + originUri);
state.setVerifierResponse(Binder.getCallingUid(),
PackageManager.VERIFICATION_ALLOW_WITHOUT_SUFFICIENT);
- mVerificationHelper.broadcastPackageVerified(verificationId, originUri,
+ VerificationUtils.broadcastPackageVerified(verificationId, originUri,
PackageManager.VERIFICATION_ALLOW, null, params.mDataLoaderType,
- user);
+ user, mPm.mContext);
} else {
- mVerificationHelper.broadcastPackageVerified(verificationId, originUri,
+ VerificationUtils.broadcastPackageVerified(verificationId, originUri,
PackageManager.VERIFICATION_REJECT, null,
- params.mDataLoaderType, user);
+ params.mDataLoaderType, user, mPm.mContext);
params.setReturnCode(
PackageManager.INSTALL_FAILED_VERIFICATION_FAILURE, errorMsg);
state.setVerifierResponse(Binder.getCallingUid(),
@@ -349,8 +251,9 @@
final Uri originUri = Uri.fromFile(params.mOriginInfo.mResolvedFile);
if (state.isInstallAllowed()) {
- mVerificationHelper.broadcastPackageVerified(verificationId, originUri,
- response.code, null, params.mDataLoaderType, params.getUser());
+ VerificationUtils.broadcastPackageVerified(verificationId, originUri,
+ response.code, null, params.mDataLoaderType, params.getUser(),
+ mPm.mContext);
} else {
params.setReturnCode(
PackageManager.INSTALL_FAILED_VERIFICATION_FAILURE,
@@ -493,290 +396,6 @@
}
}
- private void handlePackagePostInstall(PackageInstalledInfo res, boolean killApp,
- boolean virtualPreload, boolean launchedForRestore, String installerPackage,
- IPackageInstallObserver2 installObserver, int dataLoaderType) {
- boolean succeeded = res.mReturnCode == PackageManager.INSTALL_SUCCEEDED;
- final boolean update = res.mRemovedInfo != null && res.mRemovedInfo.mRemovedPackage != null;
- final String packageName = res.mName;
- final PackageStateInternal pkgSetting =
- succeeded ? mPm.getPackageStateInternal(packageName) : null;
- final boolean removedBeforeUpdate = (pkgSetting == null)
- || (pkgSetting.isSystem() && !pkgSetting.getPath().getPath().equals(
- res.mPkg.getPath()));
- if (succeeded && removedBeforeUpdate) {
- Slog.e(TAG, packageName + " was removed before handlePackagePostInstall "
- + "could be executed");
- res.mReturnCode = INSTALL_FAILED_PACKAGE_CHANGED;
- res.mReturnMsg = "Package was removed before install could complete.";
-
- // Remove the update failed package's older resources safely now
- InstallArgs args = res.mRemovedInfo != null ? res.mRemovedInfo.mArgs : null;
- if (args != null) {
- synchronized (mPm.mInstallLock) {
- args.doPostDeleteLI(true);
- }
- }
- mPm.notifyInstallObserver(res, installObserver);
- return;
- }
-
- if (succeeded) {
- // Clear the uid cache after we installed a new package.
- mPm.mPerUidReadTimeoutsCache = null;
-
- // Send the removed broadcasts
- if (res.mRemovedInfo != null) {
- res.mRemovedInfo.sendPackageRemovedBroadcasts(killApp, false /*removedBySystem*/);
- }
-
- final String installerPackageName =
- res.mInstallerPackageName != null
- ? res.mInstallerPackageName
- : res.mRemovedInfo != null
- ? res.mRemovedInfo.mInstallerPackageName
- : null;
-
- synchronized (mPm.mLock) {
- mPm.mInstantAppRegistry.onPackageInstalledLPw(res.mPkg, res.mNewUsers);
- }
-
- // Determine the set of users who are adding this package for
- // the first time vs. those who are seeing an update.
- int[] firstUserIds = EMPTY_INT_ARRAY;
- int[] firstInstantUserIds = EMPTY_INT_ARRAY;
- int[] updateUserIds = EMPTY_INT_ARRAY;
- int[] instantUserIds = EMPTY_INT_ARRAY;
- final boolean allNewUsers = res.mOrigUsers == null || res.mOrigUsers.length == 0;
- for (int newUser : res.mNewUsers) {
- final boolean isInstantApp = pkgSetting.getUserStateOrDefault(newUser)
- .isInstantApp();
- if (allNewUsers) {
- if (isInstantApp) {
- firstInstantUserIds = ArrayUtils.appendInt(firstInstantUserIds, newUser);
- } else {
- firstUserIds = ArrayUtils.appendInt(firstUserIds, newUser);
- }
- continue;
- }
- boolean isNew = true;
- for (int origUser : res.mOrigUsers) {
- if (origUser == newUser) {
- isNew = false;
- break;
- }
- }
- if (isNew) {
- if (isInstantApp) {
- firstInstantUserIds = ArrayUtils.appendInt(firstInstantUserIds, newUser);
- } else {
- firstUserIds = ArrayUtils.appendInt(firstUserIds, newUser);
- }
- } else {
- if (isInstantApp) {
- instantUserIds = ArrayUtils.appendInt(instantUserIds, newUser);
- } else {
- updateUserIds = ArrayUtils.appendInt(updateUserIds, newUser);
- }
- }
- }
-
- // Send installed broadcasts if the package is not a static shared lib.
- if (res.mPkg.getStaticSharedLibName() == null) {
- mPm.mProcessLoggingHandler.invalidateBaseApkHash(res.mPkg.getBaseApkPath());
-
- // Send added for users that see the package for the first time
- // sendPackageAddedForNewUsers also deals with system apps
- int appId = UserHandle.getAppId(res.mUid);
- boolean isSystem = res.mPkg.isSystem();
- mPm.sendPackageAddedForNewUsers(packageName, isSystem || virtualPreload,
- virtualPreload /*startReceiver*/, appId, firstUserIds, firstInstantUserIds,
- dataLoaderType);
-
- // Send added for users that don't see the package for the first time
- Bundle extras = new Bundle(1);
- extras.putInt(Intent.EXTRA_UID, res.mUid);
- if (update) {
- extras.putBoolean(Intent.EXTRA_REPLACING, true);
- }
- extras.putInt(PackageInstaller.EXTRA_DATA_LOADER_TYPE, dataLoaderType);
- // Send to all running apps.
- final SparseArray<int[]> newBroadcastAllowList;
-
- synchronized (mPm.mLock) {
- newBroadcastAllowList = mPm.mAppsFilter.getVisibilityAllowList(
- mPm.getPackageStateInternal(res.mName, Process.SYSTEM_UID),
- updateUserIds, mPm.mSettings.getPackagesLocked());
- }
- mPm.sendPackageBroadcast(Intent.ACTION_PACKAGE_ADDED, packageName,
- extras, 0 /*flags*/,
- null /*targetPackage*/, null /*finishedReceiver*/,
- updateUserIds, instantUserIds, newBroadcastAllowList, null);
- if (installerPackageName != null) {
- // Send to the installer, even if it's not running.
- mPm.sendPackageBroadcast(Intent.ACTION_PACKAGE_ADDED, packageName,
- extras, 0 /*flags*/,
- installerPackageName, null /*finishedReceiver*/,
- updateUserIds, instantUserIds, null /* broadcastAllowList */, null);
- }
- // if the required verifier is defined, but, is not the installer of record
- // for the package, it gets notified
- final boolean notifyVerifier = mPm.mRequiredVerifierPackage != null
- && !mPm.mRequiredVerifierPackage.equals(installerPackageName);
- if (notifyVerifier) {
- mPm.sendPackageBroadcast(Intent.ACTION_PACKAGE_ADDED, packageName,
- extras, 0 /*flags*/,
- mPm.mRequiredVerifierPackage, null /*finishedReceiver*/,
- updateUserIds, instantUserIds, null /* broadcastAllowList */, null);
- }
- // If package installer is defined, notify package installer about new
- // app installed
- if (mPm.mRequiredInstallerPackage != null) {
- mPm.sendPackageBroadcast(Intent.ACTION_PACKAGE_ADDED, packageName,
- extras, Intent.FLAG_RECEIVER_INCLUDE_BACKGROUND /*flags*/,
- mPm.mRequiredInstallerPackage, null /*finishedReceiver*/,
- firstUserIds, instantUserIds, null /* broadcastAllowList */, null);
- }
-
- // Send replaced for users that don't see the package for the first time
- if (update) {
- mPm.sendPackageBroadcast(Intent.ACTION_PACKAGE_REPLACED,
- packageName, extras, 0 /*flags*/,
- null /*targetPackage*/, null /*finishedReceiver*/,
- updateUserIds, instantUserIds, res.mRemovedInfo.mBroadcastAllowList,
- null);
- if (installerPackageName != null) {
- mPm.sendPackageBroadcast(Intent.ACTION_PACKAGE_REPLACED, packageName,
- extras, 0 /*flags*/,
- installerPackageName, null /*finishedReceiver*/,
- updateUserIds, instantUserIds, null /*broadcastAllowList*/, null);
- }
- if (notifyVerifier) {
- mPm.sendPackageBroadcast(Intent.ACTION_PACKAGE_REPLACED, packageName,
- extras, 0 /*flags*/,
- mPm.mRequiredVerifierPackage, null /*finishedReceiver*/,
- updateUserIds, instantUserIds, null /*broadcastAllowList*/, null);
- }
- mPm.sendPackageBroadcast(Intent.ACTION_MY_PACKAGE_REPLACED,
- null /*package*/, null /*extras*/, 0 /*flags*/,
- packageName /*targetPackage*/,
- null /*finishedReceiver*/, updateUserIds, instantUserIds,
- null /*broadcastAllowList*/,
- mBroadcastHelper.getTemporaryAppAllowlistBroadcastOptions(
- REASON_PACKAGE_REPLACED).toBundle());
- } else if (launchedForRestore && !res.mPkg.isSystem()) {
- // First-install and we did a restore, so we're responsible for the
- // first-launch broadcast.
- if (DEBUG_BACKUP) {
- Slog.i(TAG, "Post-restore of " + packageName
- + " sending FIRST_LAUNCH in " + Arrays.toString(firstUserIds));
- }
- mBroadcastHelper.sendFirstLaunchBroadcast(packageName, installerPackage,
- firstUserIds, firstInstantUserIds);
- }
-
- // Send broadcast package appeared if external for all users
- if (res.mPkg.isExternalStorage()) {
- if (!update) {
- final StorageManager storage = mPm.mInjector.getSystemService(
- StorageManager.class);
- VolumeInfo volume =
- storage.findVolumeByUuid(
- StorageManager.convert(
- res.mPkg.getVolumeUuid()).toString());
- int packageExternalStorageType =
- PackageManagerServiceUtils.getPackageExternalStorageType(volume,
- res.mPkg.isExternalStorage());
- // If the package was installed externally, log it.
- if (packageExternalStorageType != StorageEnums.UNKNOWN) {
- FrameworkStatsLog.write(
- FrameworkStatsLog.APP_INSTALL_ON_EXTERNAL_STORAGE_REPORTED,
- packageExternalStorageType, packageName);
- }
- }
- if (DEBUG_INSTALL) {
- Slog.i(TAG, "upgrading pkg " + res.mPkg + " is external");
- }
- final int[] uidArray = new int[]{res.mPkg.getUid()};
- ArrayList<String> pkgList = new ArrayList<>(1);
- pkgList.add(packageName);
- mBroadcastHelper.sendResourcesChangedBroadcast(
- true, true, pkgList, uidArray, null);
- }
- } else if (!ArrayUtils.isEmpty(res.mLibraryConsumers)) { // if static shared lib
- for (int i = 0; i < res.mLibraryConsumers.size(); i++) {
- AndroidPackage pkg = res.mLibraryConsumers.get(i);
- // send broadcast that all consumers of the static shared library have changed
- mPm.sendPackageChangedBroadcast(pkg.getPackageName(), false /* dontKillApp */,
- new ArrayList<>(Collections.singletonList(pkg.getPackageName())),
- pkg.getUid(), null);
- }
- }
-
- // Work that needs to happen on first install within each user
- if (firstUserIds != null && firstUserIds.length > 0) {
- for (int userId : firstUserIds) {
- mPm.restorePermissionsAndUpdateRolesForNewUserInstall(packageName,
- userId);
- }
- }
-
- if (allNewUsers && !update) {
- mPm.notifyPackageAdded(packageName, res.mUid);
- } else {
- mPm.notifyPackageChanged(packageName, res.mUid);
- }
-
- // Log current value of "unknown sources" setting
- EventLog.writeEvent(EventLogTags.UNKNOWN_SOURCES_ENABLED,
- getUnknownSourcesSettings());
-
- // Remove the replaced package's older resources safely now
- InstallArgs args = res.mRemovedInfo != null ? res.mRemovedInfo.mArgs : null;
- if (args != null) {
- if (!killApp) {
- // If we didn't kill the app, defer the deletion of code/resource files, since
- // they may still be in use by the running application. This mitigates problems
- // in cases where resources or code is loaded by a new Activity before
- // ApplicationInfo changes have propagated to all application threads.
- scheduleDeferredNoKillPostDelete(args);
- } else {
- synchronized (mPm.mInstallLock) {
- args.doPostDeleteLI(true);
- }
- }
- } else {
- // Force a gc to clear up things. Ask for a background one, it's fine to go on
- // and not block here.
- VMRuntime.getRuntime().requestConcurrentGC();
- }
-
- // Notify DexManager that the package was installed for new users.
- // The updated users should already be indexed and the package code paths
- // should not change.
- // Don't notify the manager for ephemeral apps as they are not expected to
- // survive long enough to benefit of background optimizations.
- for (int userId : firstUserIds) {
- PackageInfo info = mPm.getPackageInfo(packageName, /*flags*/ 0, userId);
- // There's a race currently where some install events may interleave with an
- // uninstall. This can lead to package info being null (b/36642664).
- if (info != null) {
- mPm.getDexManager().notifyPackageInstalled(info, userId);
- }
- }
- }
-
- final boolean deferInstallObserver = succeeded && update && !killApp;
- if (deferInstallObserver) {
- mPm.scheduleDeferredNoKillInstallObserver(res, installObserver);
- } else {
- mPm.notifyInstallObserver(res, installObserver);
- }
-
- // Prune unused static shared libraries which have been cached a period of time
- mPm.schedulePruneUnusedStaticSharedLibraries(true /* delay */);
- }
-
/**
* Get the default verification agent response code.
*
@@ -800,20 +419,4 @@
// an easy way to get around the integrity check.
return PackageManager.VERIFICATION_REJECT;
}
-
- /**
- * Get the "allow unknown sources" setting.
- *
- * @return the current "allow unknown sources" setting
- */
- private int getUnknownSourcesSettings() {
- return android.provider.Settings.Secure.getIntForUser(mPm.mContext.getContentResolver(),
- android.provider.Settings.Secure.INSTALL_NON_MARKET_APPS,
- -1, UserHandle.USER_SYSTEM);
- }
-
- private void scheduleDeferredNoKillPostDelete(InstallArgs args) {
- Message message = obtainMessage(DEFERRED_NO_KILL_POST_DELETE, args);
- sendMessageDelayed(message, DEFERRED_NO_KILL_POST_DELETE_DELAY_MS);
- }
}
diff --git a/services/core/java/com/android/server/pm/PackageManagerService.java b/services/core/java/com/android/server/pm/PackageManagerService.java
index 8b26419..e71ac1a 100644
--- a/services/core/java/com/android/server/pm/PackageManagerService.java
+++ b/services/core/java/com/android/server/pm/PackageManagerService.java
@@ -1273,6 +1273,11 @@
mHandler.sendMessageDelayed(message, DEFERRED_NO_KILL_INSTALL_OBSERVER_DELAY_MS);
}
+ void scheduleDeferredNoKillPostDelete(InstallArgs args) {
+ Message message = mHandler.obtainMessage(DEFERRED_NO_KILL_POST_DELETE, args);
+ mHandler.sendMessageDelayed(message, DEFERRED_NO_KILL_POST_DELETE_DELAY_MS);
+ }
+
void schedulePruneUnusedStaticSharedLibraries(boolean delay) {
mHandler.removeMessages(PRUNE_UNUSED_STATIC_SHARED_LIBRARIES);
mHandler.sendEmptyMessageDelayed(PRUNE_UNUSED_STATIC_SHARED_LIBRARIES,
@@ -1409,6 +1414,32 @@
}
}
+ void writePendingRestrictions() {
+ synchronized (mLock) {
+ mHandler.removeMessages(WRITE_PACKAGE_RESTRICTIONS);
+ for (int userId : mDirtyUsers) {
+ mSettings.writePackageRestrictionsLPr(userId);
+ }
+ mDirtyUsers.clear();
+ }
+ }
+
+ void writeSettings() {
+ synchronized (mLock) {
+ mHandler.removeMessages(WRITE_SETTINGS);
+ mHandler.removeMessages(WRITE_PACKAGE_RESTRICTIONS);
+ writeSettingsLPrTEMP();
+ mDirtyUsers.clear();
+ }
+ }
+
+ void writePackageList(int userId) {
+ synchronized (mLock) {
+ mHandler.removeMessages(WRITE_PACKAGE_LIST);
+ mSettings.writePackageListLPr(userId);
+ }
+ }
+
public static PackageManagerService main(Context context, Installer installer,
@NonNull DomainVerificationService domainVerificationService, boolean factoryTest,
boolean onlyCore) {
@@ -1801,7 +1832,7 @@
mInitAndSystemPackageHelper = new InitAndSystemPackageHelper(this, mRemovePackageHelper,
mAppDataHelper);
mDeletePackageHelper = new DeletePackageHelper(this, mRemovePackageHelper,
- mInitAndSystemPackageHelper, mAppDataHelper);
+ mAppDataHelper);
mPreferredActivityHelper = new PreferredActivityHelper(this);
mResolveIntentHelper = new ResolveIntentHelper(this, mPreferredActivityHelper);
mDexOptHelper = new DexOptHelper(this);
@@ -7845,7 +7876,8 @@
if (isSystemStub
&& (newState == PackageManager.COMPONENT_ENABLED_STATE_DEFAULT
|| newState == PackageManager.COMPONENT_ENABLED_STATE_ENABLED)) {
- if (!mInitAndSystemPackageHelper.enableCompressedPackage(deletedPkg, pkgSetting)) {
+ if (!new InstallPackageHelper(this).enableCompressedPackage(deletedPkg,
+ pkgSetting)) {
Slog.w(TAG, "Failed setApplicationEnabledSetting: failed to enable "
+ "commpressed package " + setting.getPackageName());
updateAllowed[i] = false;
@@ -11134,11 +11166,24 @@
return mIsPreNMR1Upgrade;
}
- InitAndSystemPackageHelper getInitAndSystemPackageHelper() {
- return mInitAndSystemPackageHelper;
- }
-
boolean isOverlayMutable(String packageName) {
return mOverlayConfig.isMutable(packageName);
}
+
+ @ScanFlags int getSystemPackageScanFlags(File codePath) {
+ List<ScanPartition> dirsToScanAsSystem =
+ mInitAndSystemPackageHelper.getDirsToScanAsSystem();
+ @PackageManagerService.ScanFlags int scanFlags = SCAN_AS_SYSTEM;
+ for (int i = dirsToScanAsSystem.size() - 1; i >= 0; i--) {
+ ScanPartition partition = dirsToScanAsSystem.get(i);
+ if (partition.containsFile(codePath)) {
+ scanFlags |= partition.scanFlag;
+ if (partition.containsPrivApp(codePath)) {
+ scanFlags |= SCAN_AS_PRIVILEGED;
+ }
+ break;
+ }
+ }
+ return scanFlags;
+ }
}
diff --git a/services/core/java/com/android/server/pm/PackageManagerServiceUtils.java b/services/core/java/com/android/server/pm/PackageManagerServiceUtils.java
index 9c41ccb..098c42e 100644
--- a/services/core/java/com/android/server/pm/PackageManagerServiceUtils.java
+++ b/services/core/java/com/android/server/pm/PackageManagerServiceUtils.java
@@ -644,6 +644,35 @@
return compatMatch;
}
+ /**
+ * Decompress files stored in codePath to dstCodePath for a certain package.
+ */
+ public static int decompressFiles(String codePath, File dstCodePath, String packageName) {
+ final File[] compressedFiles = getCompressedFiles(codePath);
+ int ret = PackageManager.INSTALL_SUCCEEDED;
+ try {
+ makeDirRecursive(dstCodePath, 0755);
+ for (File srcFile : compressedFiles) {
+ final String srcFileName = srcFile.getName();
+ final String dstFileName = srcFileName.substring(
+ 0, srcFileName.length() - COMPRESSED_EXTENSION.length());
+ final File dstFile = new File(dstCodePath, dstFileName);
+ ret = decompressFile(srcFile, dstFile);
+ if (ret != PackageManager.INSTALL_SUCCEEDED) {
+ logCriticalInfo(Log.ERROR, "Failed to decompress"
+ + "; pkg: " + packageName
+ + ", file: " + dstFileName);
+ break;
+ }
+ }
+ } catch (ErrnoException e) {
+ logCriticalInfo(Log.ERROR, "Failed to decompress"
+ + "; pkg: " + packageName
+ + ", err: " + e.errno);
+ }
+ return ret;
+ }
+
public static int decompressFile(File srcFile, File dstFile) throws ErrnoException {
if (DEBUG_COMPRESSION) {
Slog.i(TAG, "Decompress file"
diff --git a/services/core/java/com/android/server/pm/VerificationParams.java b/services/core/java/com/android/server/pm/VerificationParams.java
index 7dec756..4dbc5d5 100644
--- a/services/core/java/com/android/server/pm/VerificationParams.java
+++ b/services/core/java/com/android/server/pm/VerificationParams.java
@@ -51,7 +51,6 @@
import android.content.pm.PackageManagerInternal;
import android.content.pm.ParceledListSlice;
import android.content.pm.ResolveInfo;
-import android.content.pm.Signature;
import android.content.pm.SigningDetails;
import android.content.pm.VerifierInfo;
import android.content.pm.parsing.PackageLite;
@@ -62,7 +61,6 @@
import android.os.RemoteException;
import android.os.Trace;
import android.os.UserHandle;
-import android.os.UserManager;
import android.provider.DeviceConfig;
import android.provider.Settings;
import android.util.ArraySet;
@@ -70,13 +68,9 @@
import android.util.Slog;
import com.android.server.DeviceIdleInternal;
-import com.android.server.pm.parsing.pkg.AndroidPackage;
import java.io.File;
-import java.security.PublicKey;
-import java.security.cert.CertificateException;
import java.util.ArrayList;
-import java.util.Arrays;
import java.util.List;
import java.util.Set;
@@ -91,10 +85,6 @@
*/
private static final long DEFAULT_INTEGRITY_VERIFICATION_TIMEOUT = 30 * 1000;
/**
- * Whether verification is enabled by default.
- */
- private static final boolean DEFAULT_VERIFY_ENABLE = true;
- /**
* Timeout duration in milliseconds for enabling package rollback. If we fail to enable
* rollback within that period, the install will proceed without rollback enabled.
*
@@ -166,7 +156,7 @@
PackageInfoLite pkgLite = PackageManagerServiceUtils.getMinimalPackageInfo(mPm.mContext,
mPackageLite, mOriginInfo.mResolvedPath, mInstallFlags, mPackageAbiOverride);
- Pair<Integer, String> ret = verifyReplacingVersionCode(
+ Pair<Integer, String> ret = mInstallPackageHelper.verifyReplacingVersionCode(
pkgLite, mRequiredInstalledVersionCode, mInstallFlags);
setReturnCode(ret.first, ret.second);
if (mRet != INSTALL_SUCCEEDED) {
@@ -186,7 +176,7 @@
}
}
- void sendApkVerificationRequest(PackageInfoLite pkgLite) {
+ private void sendApkVerificationRequest(PackageInfoLite pkgLite) {
final int verificationId = mPm.mPendingVerificationToken++;
PackageVerificationState verificationState =
@@ -332,7 +322,7 @@
/**
* Send a request to verifier(s) to verify the package if necessary.
*/
- void sendPackageVerificationRequest(
+ private void sendPackageVerificationRequest(
int verificationId,
PackageInfoLite pkgLite,
PackageVerificationState verificationState) {
@@ -354,7 +344,7 @@
verificationState.setRequiredVerifierUid(requiredUid);
final int installerUid =
mVerificationInfo == null ? -1 : mVerificationInfo.mInstallerUid;
- final boolean isVerificationEnabled = isVerificationEnabled(
+ final boolean isVerificationEnabled = mInstallPackageHelper.isVerificationEnabled(
pkgLite, verifierUser.getIdentifier(), mInstallFlags, installerUid);
final boolean isV4Signed =
(mSigningDetails.getSignatureSchemeVersion() == SIGNING_BLOCK_V4);
@@ -416,7 +406,7 @@
DeviceIdleInternal idleController =
mPm.mInjector.getLocalService(DeviceIdleInternal.class);
- final long idleDuration = mVerificationHelper.getVerificationTimeout();
+ final long idleDuration = VerificationUtils.getVerificationTimeout(mPm.mContext);
final BroadcastOptions options = BroadcastOptions.makeBasic();
options.setTemporaryAppAllowlist(idleDuration,
TEMPORARY_ALLOWLIST_TYPE_FOREGROUND_SERVICE_ALLOWED,
@@ -473,7 +463,7 @@
.obtainMessage(CHECK_PENDING_VERIFICATION);
msg.arg1 = verificationId;
mPm.mHandler.sendMessageDelayed(msg,
- mVerificationHelper.getVerificationTimeout());
+ VerificationUtils.getVerificationTimeout(mPm.mContext));
}
}, null, 0, null, null);
@@ -510,7 +500,7 @@
continue;
}
- final int verifierUid = getUidForVerifier(verifierInfo);
+ final int verifierUid = mInstallPackageHelper.getUidForVerifier(verifierInfo);
if (verifierUid == -1) {
continue;
}
@@ -526,44 +516,6 @@
return sufficientVerifiers;
}
- private int getUidForVerifier(VerifierInfo verifierInfo) {
- synchronized (mPm.mLock) {
- final AndroidPackage pkg = mPm.mPackages.get(verifierInfo.packageName);
- if (pkg == null) {
- return -1;
- } else if (pkg.getSigningDetails().getSignatures().length != 1) {
- Slog.i(TAG, "Verifier package " + verifierInfo.packageName
- + " has more than one signature; ignoring");
- return -1;
- }
-
- /*
- * If the public key of the package's signature does not match
- * our expected public key, then this is a different package and
- * we should skip.
- */
-
- final byte[] expectedPublicKey;
- try {
- final Signature verifierSig = pkg.getSigningDetails().getSignatures()[0];
- final PublicKey publicKey = verifierSig.getPublicKey();
- expectedPublicKey = publicKey.getEncoded();
- } catch (CertificateException e) {
- return -1;
- }
-
- final byte[] actualPublicKey = verifierInfo.publicKey.getEncoded();
-
- if (!Arrays.equals(actualPublicKey, expectedPublicKey)) {
- Slog.i(TAG, "Verifier package " + verifierInfo.packageName
- + " does not have the expected public key; ignoring");
- return -1;
- }
-
- return pkg.getUid();
- }
- }
-
private static ComponentName matchComponentForVerifier(String packageName,
List<ResolveInfo> receivers) {
ActivityInfo targetReceiver = null;
@@ -588,56 +540,6 @@
return new ComponentName(targetReceiver.packageName, targetReceiver.name);
}
- /**
- * Check whether or not package verification has been enabled.
- *
- * @return true if verification should be performed
- */
- private boolean isVerificationEnabled(
- PackageInfoLite pkgInfoLite, int userId, int installFlags, int installerUid) {
- if (!DEFAULT_VERIFY_ENABLE) {
- return false;
- }
-
- // Check if installing from ADB
- if ((installFlags & PackageManager.INSTALL_FROM_ADB) != 0) {
- if (mPm.isUserRestricted(userId, UserManager.ENSURE_VERIFY_APPS)) {
- return true;
- }
- // Check if the developer wants to skip verification for ADB installs
- if ((installFlags & PackageManager.INSTALL_DISABLE_VERIFICATION) != 0) {
- synchronized (mPm.mLock) {
- if (mPm.mSettings.getPackageLPr(pkgInfoLite.packageName) == null) {
- // Always verify fresh install
- return true;
- }
- }
- // Only skip when apk is debuggable
- return !pkgInfoLite.debuggable;
- }
- return Settings.Global.getInt(mPm.mContext.getContentResolver(),
- Settings.Global.PACKAGE_VERIFIER_INCLUDE_ADB, 1) != 0;
- }
-
- // only when not installed from ADB, skip verification for instant apps when
- // the installer and verifier are the same.
- if ((installFlags & PackageManager.INSTALL_INSTANT_APP) != 0) {
- if (mPm.mInstantAppInstallerActivity != null
- && mPm.mInstantAppInstallerActivity.packageName.equals(
- mPm.mRequiredVerifierPackage)) {
- try {
- mPm.mInjector.getSystemService(AppOpsManager.class)
- .checkPackage(installerUid, mPm.mRequiredVerifierPackage);
- if (DEBUG_VERIFY) {
- Slog.i(TAG, "disable verification for instant app");
- }
- return false;
- } catch (SecurityException ignore) { }
- }
- }
- return true;
- }
-
void populateInstallerExtras(Intent intent) {
intent.putExtra(PackageManager.EXTRA_VERIFICATION_INSTALLER_PACKAGE,
mInstallSource.initiatingPackageName);
diff --git a/services/core/java/com/android/server/pm/VerificationHelper.java b/services/core/java/com/android/server/pm/VerificationUtils.java
similarity index 84%
rename from services/core/java/com/android/server/pm/VerificationHelper.java
rename to services/core/java/com/android/server/pm/VerificationUtils.java
index b04d6de..4392b47 100644
--- a/services/core/java/com/android/server/pm/VerificationHelper.java
+++ b/services/core/java/com/android/server/pm/VerificationUtils.java
@@ -27,36 +27,30 @@
import android.os.UserHandle;
import android.provider.Settings;
-public final class VerificationHelper {
+final class VerificationUtils {
/**
* The default maximum time to wait for the verification agent to return in
* milliseconds.
*/
private static final long DEFAULT_VERIFICATION_TIMEOUT = 10 * 1000;
- Context mContext;
-
- public VerificationHelper(Context context) {
- mContext = context;
- }
-
/**
* Get the verification agent timeout. Used for both the APK verifier and the
* intent filter verifier.
*
* @return verification timeout in milliseconds
*/
- public long getVerificationTimeout() {
- long timeout = Settings.Global.getLong(mContext.getContentResolver(),
+ public static long getVerificationTimeout(Context context) {
+ long timeout = Settings.Global.getLong(context.getContentResolver(),
Settings.Global.PACKAGE_VERIFIER_TIMEOUT, DEFAULT_VERIFICATION_TIMEOUT);
// The setting can be used to increase the timeout but not decrease it, since that is
// equivalent to disabling the verifier.
return Math.max(timeout, DEFAULT_VERIFICATION_TIMEOUT);
}
- public void broadcastPackageVerified(int verificationId, Uri packageUri,
+ public static void broadcastPackageVerified(int verificationId, Uri packageUri,
int verificationCode, @Nullable String rootHashString, int dataLoaderType,
- UserHandle user) {
+ UserHandle user, Context context) {
final Intent intent = new Intent(Intent.ACTION_PACKAGE_VERIFIED);
intent.setDataAndType(packageUri, PACKAGE_MIME_TYPE);
intent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION);
@@ -67,7 +61,7 @@
}
intent.putExtra(PackageInstaller.EXTRA_DATA_LOADER_TYPE, dataLoaderType);
- mContext.sendBroadcastAsUser(intent, user,
+ context.sendBroadcastAsUser(intent, user,
android.Manifest.permission.PACKAGE_VERIFICATION_AGENT);
}
}
diff --git a/services/core/java/com/android/server/pm/verify/domain/DomainVerificationService.java b/services/core/java/com/android/server/pm/verify/domain/DomainVerificationService.java
index 962816c..0fb8475 100644
--- a/services/core/java/com/android/server/pm/verify/domain/DomainVerificationService.java
+++ b/services/core/java/com/android/server/pm/verify/domain/DomainVerificationService.java
@@ -899,7 +899,7 @@
oldPkgState.getUserStates();
int oldUserStatesSize = oldUserStates.size();
if (oldUserStatesSize > 0) {
- ArraySet<String> newWebDomains = mCollector.collectValidAutoVerifyDomains(newPkg);
+ ArraySet<String> newWebDomains = mCollector.collectAllWebDomains(newPkg);
for (int oldUserStatesIndex = 0; oldUserStatesIndex < oldUserStatesSize;
oldUserStatesIndex++) {
int userId = oldUserStates.keyAt(oldUserStatesIndex);
diff --git a/services/core/java/com/android/server/stats/pull/StatsPullAtomService.java b/services/core/java/com/android/server/stats/pull/StatsPullAtomService.java
index 7826ddf..ab61ef4 100644
--- a/services/core/java/com/android/server/stats/pull/StatsPullAtomService.java
+++ b/services/core/java/com/android/server/stats/pull/StatsPullAtomService.java
@@ -95,7 +95,6 @@
import android.hardware.biometrics.BiometricsProtoEnums;
import android.hardware.face.FaceManager;
import android.hardware.fingerprint.FingerprintManager;
-import android.hardware.health.V2_0.IHealth;
import android.net.ConnectivityManager;
import android.net.INetworkStatsService;
import android.net.INetworkStatsSession;
@@ -194,13 +193,13 @@
import com.android.internal.util.CollectionUtils;
import com.android.internal.util.FrameworkStatsLog;
import com.android.role.RoleManagerLocal;
-import com.android.server.BatteryService;
import com.android.server.BinderCallsStatsService;
import com.android.server.LocalManagerRegistry;
import com.android.server.LocalServices;
import com.android.server.SystemService;
import com.android.server.SystemServiceManager;
import com.android.server.am.MemoryStatUtil.MemoryStat;
+import com.android.server.health.HealthServiceWrapper;
import com.android.server.notification.NotificationManagerService;
import com.android.server.stats.pull.IonMemoryUtil.IonAllocations;
import com.android.server.stats.pull.ProcfsMemoryUtil.MemorySnapshot;
@@ -230,6 +229,7 @@
import java.util.List;
import java.util.Map;
import java.util.MissingResourceException;
+import java.util.NoSuchElementException;
import java.util.Random;
import java.util.Set;
import java.util.UUID;
@@ -358,7 +358,7 @@
private File mBaseDir;
@GuardedBy("mHealthHalLock")
- private BatteryService.HealthServiceWrapper mHealthService;
+ private HealthServiceWrapper mHealthService;
@Nullable
@GuardedBy("mCpuTimePerThreadFreqLock")
@@ -807,10 +807,9 @@
KernelCpuThreadReaderSettingsObserver.getSettingsModifiedReader(mContext);
// Initialize HealthService
- mHealthService = new BatteryService.HealthServiceWrapper();
try {
- mHealthService.init();
- } catch (RemoteException e) {
+ mHealthService = HealthServiceWrapper.create(null);
+ } catch (RemoteException | NoSuchElementException e) {
Slog.e(TAG, "failed to initialize healthHalWrapper");
}
@@ -3990,38 +3989,40 @@
}
int pullHealthHalLocked(int atomTag, List<StatsEvent> pulledData) {
- IHealth healthService = mHealthService.getLastService();
- if (healthService == null) {
+ if (mHealthService == null) {
return StatsManager.PULL_SKIP;
}
+ android.hardware.health.V1_0.HealthInfo healthInfo;
try {
- healthService.getHealthInfo((result, value) -> {
- int pulledValue;
- switch(atomTag) {
- case FrameworkStatsLog.BATTERY_LEVEL:
- pulledValue = value.legacy.batteryLevel;
- break;
- case FrameworkStatsLog.REMAINING_BATTERY_CAPACITY:
- pulledValue = value.legacy.batteryChargeCounter;
- break;
- case FrameworkStatsLog.FULL_BATTERY_CAPACITY:
- pulledValue = value.legacy.batteryFullCharge;
- break;
- case FrameworkStatsLog.BATTERY_VOLTAGE:
- pulledValue = value.legacy.batteryVoltage;
- break;
- case FrameworkStatsLog.BATTERY_CYCLE_COUNT:
- pulledValue = value.legacy.batteryCycleCount;
- break;
- default:
- throw new IllegalStateException("Invalid atomTag in healthHal puller: "
- + atomTag);
- }
- pulledData.add(FrameworkStatsLog.buildStatsEvent(atomTag, pulledValue));
- });
+ healthInfo = mHealthService.getHealthInfo();
} catch (RemoteException | IllegalStateException e) {
return StatsManager.PULL_SKIP;
}
+ if (healthInfo == null) {
+ return StatsManager.PULL_SKIP;
+ }
+
+ int pulledValue;
+ switch (atomTag) {
+ case FrameworkStatsLog.BATTERY_LEVEL:
+ pulledValue = healthInfo.batteryLevel;
+ break;
+ case FrameworkStatsLog.REMAINING_BATTERY_CAPACITY:
+ pulledValue = healthInfo.batteryChargeCounter;
+ break;
+ case FrameworkStatsLog.FULL_BATTERY_CAPACITY:
+ pulledValue = healthInfo.batteryFullCharge;
+ break;
+ case FrameworkStatsLog.BATTERY_VOLTAGE:
+ pulledValue = healthInfo.batteryVoltage;
+ break;
+ case FrameworkStatsLog.BATTERY_CYCLE_COUNT:
+ pulledValue = healthInfo.batteryCycleCount;
+ break;
+ default:
+ return StatsManager.PULL_SKIP;
+ }
+ pulledData.add(FrameworkStatsLog.buildStatsEvent(atomTag, pulledValue));
return StatsManager.PULL_SUCCESS;
}
diff --git a/services/core/java/com/android/server/vcn/VcnGatewayConnection.java b/services/core/java/com/android/server/vcn/VcnGatewayConnection.java
index 239a916..1c46ac8 100644
--- a/services/core/java/com/android/server/vcn/VcnGatewayConnection.java
+++ b/services/core/java/com/android/server/vcn/VcnGatewayConnection.java
@@ -98,6 +98,7 @@
import java.net.Inet4Address;
import java.net.Inet6Address;
import java.net.InetAddress;
+import java.net.NetworkInterface;
import java.util.Arrays;
import java.util.Collections;
import java.util.List;
@@ -2048,7 +2049,8 @@
return builder.build();
}
- private static LinkProperties buildConnectedLinkProperties(
+ @VisibleForTesting(visibility = Visibility.PRIVATE)
+ LinkProperties buildConnectedLinkProperties(
@NonNull VcnGatewayConnectionConfig gatewayConnectionConfig,
@NonNull IpSecTunnelInterface tunnelIface,
@NonNull VcnChildSessionConfiguration childConfig,
@@ -2076,6 +2078,13 @@
lp.setTcpBufferSizes(underlyingLp.getTcpBufferSizes());
underlyingMtu = underlyingLp.getMtu();
+
+ // WiFi LinkProperties uses DHCP as the sole source of MTU information, and as a result
+ // often lists MTU as 0 (see b/184678973). Use the interface MTU as retrieved by
+ // NetworkInterface APIs.
+ if (underlyingMtu == 0 && underlyingLp.getInterfaceName() != null) {
+ underlyingMtu = mDeps.getUnderlyingIfaceMtu(underlyingLp.getInterfaceName());
+ }
} else {
Slog.wtf(
TAG,
@@ -2417,6 +2426,17 @@
public long getElapsedRealTime() {
return SystemClock.elapsedRealtime();
}
+
+ /** Gets the MTU for the given underlying interface. */
+ public int getUnderlyingIfaceMtu(String ifaceName) {
+ try {
+ final NetworkInterface underlyingIface = NetworkInterface.getByName(ifaceName);
+ return underlyingIface == null ? 0 : underlyingIface.getMTU();
+ } catch (IOException e) {
+ Slog.d(TAG, "Could not get MTU of underlying network", e);
+ return 0;
+ }
+ }
}
/**
diff --git a/services/core/java/com/android/server/wm/AccessibilityController.java b/services/core/java/com/android/server/wm/AccessibilityController.java
index 3d83995..ec0b5f0 100644
--- a/services/core/java/com/android/server/wm/AccessibilityController.java
+++ b/services/core/java/com/android/server/wm/AccessibilityController.java
@@ -20,11 +20,11 @@
import static android.accessibilityservice.AccessibilityTrace.FLAGS_WINDOWS_FOR_ACCESSIBILITY_CALLBACK;
import static android.os.Build.IS_USER;
import static android.view.InsetsState.ITYPE_NAVIGATION_BAR;
+import static android.view.WindowManager.LayoutParams.PRIVATE_FLAG_EXCLUDE_FROM_SCREEN_MAGNIFICATION;
import static android.view.WindowManager.LayoutParams.PRIVATE_FLAG_IS_ROUNDED_CORNERS_OVERLAY;
import static android.view.WindowManager.LayoutParams.TYPE_ACCESSIBILITY_MAGNIFICATION_OVERLAY;
import static android.view.WindowManager.LayoutParams.TYPE_DOCK_DIVIDER;
import static android.view.WindowManager.LayoutParams.TYPE_MAGNIFICATION_OVERLAY;
-import static android.view.WindowManager.LayoutParams.TYPE_NAVIGATION_BAR_PANEL;
import static com.android.server.accessibility.AccessibilityTraceFileProto.ENTRY;
import static com.android.server.accessibility.AccessibilityTraceFileProto.MAGIC_NUMBER;
@@ -926,6 +926,8 @@
final int windowType = windowState.mAttrs.type;
if (isExcludedWindowType(windowType)
|| ((windowState.mAttrs.privateFlags
+ & PRIVATE_FLAG_EXCLUDE_FROM_SCREEN_MAGNIFICATION) != 0)
+ || ((windowState.mAttrs.privateFlags
& PRIVATE_FLAG_IS_ROUNDED_CORNERS_OVERLAY) != 0)) {
continue;
}
@@ -990,7 +992,6 @@
}
}
}
-
visibleWindows.clear();
mMagnificationRegion.op(mDrawBorderInset, mDrawBorderInset,
@@ -1027,9 +1028,6 @@
private boolean isExcludedWindowType(int windowType) {
return windowType == TYPE_MAGNIFICATION_OVERLAY
- // Omit the touch region to avoid the cut out of the magnification
- // bounds because nav bar panel is unmagnifiable.
- || windowType == TYPE_NAVIGATION_BAR_PANEL
// Omit the touch region of window magnification to avoid the cut out of the
// magnification and the magnified center of window magnification could be
// in the bounds
diff --git a/services/core/java/com/android/server/wm/DisplayContent.java b/services/core/java/com/android/server/wm/DisplayContent.java
index 241e7e2..7a38554 100644
--- a/services/core/java/com/android/server/wm/DisplayContent.java
+++ b/services/core/java/com/android/server/wm/DisplayContent.java
@@ -5981,7 +5981,8 @@
// Reparent the SurfaceControl of this DisplayContent to null, to prevent content
// being added to it. This ensures that no app launched explicitly on the
// VirtualDisplay will show up as part of the mirrored content.
- .reparent(mWindowingLayer, null);
+ .reparent(mWindowingLayer, null)
+ .reparent(mOverlayLayer, null);
// Retrieve the size of the DisplayArea to mirror.
updateMirroredSurface(transaction, wc.getDisplayContent().getBounds(), surfaceSize);
mTokenToMirror = tokenToMirror;
@@ -6011,7 +6012,9 @@
// Reparent the SurfaceControl of this DisplayContent back to mSurfaceControl,
// to allow content to be added to it. This allows this DisplayContent to stop
// mirroring and show content normally.
- .reparent(mWindowingLayer, mSurfaceControl).apply();
+ .reparent(mWindowingLayer, mSurfaceControl)
+ .reparent(mOverlayLayer, mSurfaceControl)
+ .apply();
// Stop mirroring by destroying the reference to the mirrored layer.
mMirroredSurface = null;
// Do not un-set the token, in case content is removed and mirroring should begin again.
diff --git a/services/core/java/com/android/server/wm/TaskDisplayArea.java b/services/core/java/com/android/server/wm/TaskDisplayArea.java
index 4d986a2..3387e37 100644
--- a/services/core/java/com/android/server/wm/TaskDisplayArea.java
+++ b/services/core/java/com/android/server/wm/TaskDisplayArea.java
@@ -48,6 +48,7 @@
import android.app.WindowConfiguration;
import android.content.pm.ActivityInfo;
import android.content.res.Configuration;
+import android.graphics.Color;
import android.os.UserHandle;
import android.util.IntArray;
import android.util.Slog;
@@ -81,9 +82,9 @@
DisplayContent mDisplayContent;
/**
- * A color layer that serves as a solid color background to certain animations.
+ * Keeps track of the last set color layer so that it can be reset during surface migrations.
*/
- private SurfaceControl mColorBackgroundLayer;
+ private @ColorInt int mBackgroundColor = 0;
/**
* This counter is used to make sure we don't prematurely clear the background color in the
@@ -357,6 +358,14 @@
}
@Override
+ void setInitialSurfaceControlProperties(SurfaceControl.Builder b) {
+ // We want an effect layer instead of the default container layer so that we can set a
+ // background color on it for task animations.
+ b.setEffectLayer();
+ super.setInitialSurfaceControlProperties(b);
+ }
+
+ @Override
void addChild(WindowContainer child, int position) {
if (child.asTaskDisplayArea() != null) {
if (DEBUG_ROOT_TASK) {
@@ -875,11 +884,6 @@
void onParentChanged(ConfigurationContainer newParent, ConfigurationContainer oldParent) {
if (getParent() != null) {
super.onParentChanged(newParent, oldParent, () -> {
- mColorBackgroundLayer = makeChildSurface(null)
- .setColorLayer()
- .setName("colorBackgroundLayer")
- .setCallsite("TaskDisplayArea.onParentChanged")
- .build();
mSplitScreenDividerAnchor = makeChildSurface(null)
.setName("splitScreenDividerAnchor")
.setCallsite("TaskDisplayArea.onParentChanged")
@@ -891,28 +895,18 @@
} else {
super.onParentChanged(newParent, oldParent);
mWmService.mTransactionFactory.get()
- .remove(mColorBackgroundLayer)
.remove(mSplitScreenDividerAnchor)
.apply();
- mColorBackgroundLayer = null;
mSplitScreenDividerAnchor = null;
}
}
- void setBackgroundColor(@ColorInt int color) {
- if (mColorBackgroundLayer == null) {
- return;
- }
-
- float r = ((color >> 16) & 0xff) / 255.0f;
- float g = ((color >> 8) & 0xff) / 255.0f;
- float b = ((color >> 0) & 0xff) / 255.0f;
-
+ void setBackgroundColor(@ColorInt int colorInt) {
+ mBackgroundColor = colorInt;
+ Color color = Color.valueOf(colorInt);
mColorLayerCounter++;
-
getPendingTransaction()
- .setColor(mColorBackgroundLayer, new float[]{r, g, b})
- .show(mColorBackgroundLayer);
+ .setColor(mSurfaceControl, new float[]{color.red(), color.green(), color.blue()});
scheduleAnimation();
}
@@ -923,7 +917,7 @@
// Only clear the color layer if we have received the same amounts of clear as set
// requests.
if (mColorLayerCounter == 0) {
- getPendingTransaction().hide(mColorBackgroundLayer);
+ getPendingTransaction().unsetColor(mSurfaceControl);
scheduleAnimation();
}
}
@@ -931,12 +925,16 @@
@Override
void migrateToNewSurfaceControl(SurfaceControl.Transaction t) {
super.migrateToNewSurfaceControl(t);
- if (mColorBackgroundLayer == null) {
+
+ if (mColorLayerCounter > 0) {
+ setBackgroundColor(mBackgroundColor);
+ }
+
+ if (mSplitScreenDividerAnchor == null) {
return;
}
// As TaskDisplayArea is getting a new surface, reparent and reorder the child surfaces.
- t.reparent(mColorBackgroundLayer, mSurfaceControl);
t.reparent(mSplitScreenDividerAnchor, mSurfaceControl);
reassignLayer(t);
scheduleAnimation();
diff --git a/services/core/java/com/android/server/wm/TaskFragment.java b/services/core/java/com/android/server/wm/TaskFragment.java
index 49bbd8a..0eaa25b 100644
--- a/services/core/java/com/android/server/wm/TaskFragment.java
+++ b/services/core/java/com/android/server/wm/TaskFragment.java
@@ -31,6 +31,7 @@
import static android.content.res.Configuration.ORIENTATION_LANDSCAPE;
import static android.content.res.Configuration.ORIENTATION_PORTRAIT;
import static android.content.res.Configuration.ORIENTATION_UNDEFINED;
+import static android.os.Process.INVALID_UID;
import static android.os.UserHandle.USER_NULL;
import static android.view.Display.INVALID_DISPLAY;
import static android.view.WindowManager.TRANSIT_CLOSE;
@@ -220,6 +221,8 @@
/** Organizer that organizing this TaskFragment. */
@Nullable
private ITaskFragmentOrganizer mTaskFragmentOrganizer;
+ private int mTaskFragmentOrganizerUid = INVALID_UID;
+ private @Nullable String mTaskFragmentOrganizerProcessName;
/** Client assigned unique token for this TaskFragment if this is created by an organizer. */
@Nullable
@@ -232,13 +235,6 @@
*/
private boolean mDelayLastActivityRemoval;
- /**
- * The PID of the organizer that created this TaskFragment. It should be the same as the PID
- * of {@link android.window.TaskFragmentCreationParams#getOwnerToken()}.
- * {@link ActivityRecord#INVALID_PID} if this is not an organizer-created TaskFragment.
- */
- private int mTaskFragmentOrganizerPid = ActivityRecord.INVALID_PID;
-
final Point mLastSurfaceSize = new Point();
private final Rect mTmpInsets = new Rect();
@@ -334,9 +330,11 @@
mDelayLastActivityRemoval = false;
}
- void setTaskFragmentOrganizer(TaskFragmentOrganizerToken organizer, int pid) {
+ void setTaskFragmentOrganizer(@NonNull TaskFragmentOrganizerToken organizer, int uid,
+ @NonNull String processName) {
mTaskFragmentOrganizer = ITaskFragmentOrganizer.Stub.asInterface(organizer.asBinder());
- mTaskFragmentOrganizerPid = pid;
+ mTaskFragmentOrganizerUid = uid;
+ mTaskFragmentOrganizerProcessName = processName;
}
/** Whether this TaskFragment is organized by the given {@code organizer}. */
@@ -2176,9 +2174,11 @@
List<IBinder> childActivities = new ArrayList<>();
for (int i = 0; i < getChildCount(); i++) {
WindowContainer wc = getChildAt(i);
- if (mTaskFragmentOrganizerPid != ActivityRecord.INVALID_PID
+ if (mTaskFragmentOrganizerUid != INVALID_UID
&& wc.asActivityRecord() != null
- && wc.asActivityRecord().getPid() == mTaskFragmentOrganizerPid) {
+ && wc.asActivityRecord().info.processName.equals(
+ mTaskFragmentOrganizerProcessName)
+ && wc.asActivityRecord().getUid() == mTaskFragmentOrganizerUid) {
// Only includes Activities that belong to the organizer process for security.
childActivities.add(wc.asActivityRecord().token);
}
diff --git a/services/core/java/com/android/server/wm/WindowOrganizerController.java b/services/core/java/com/android/server/wm/WindowOrganizerController.java
index f6c8356..8ecf685 100644
--- a/services/core/java/com/android/server/wm/WindowOrganizerController.java
+++ b/services/core/java/com/android/server/wm/WindowOrganizerController.java
@@ -1204,8 +1204,8 @@
creationParams.getFragmentToken(), true /* createdByOrganizer */);
// Set task fragment organizer immediately, since it might have to be notified about further
// actions.
- taskFragment.setTaskFragmentOrganizer(
- creationParams.getOrganizer(), ownerActivity.getPid());
+ taskFragment.setTaskFragmentOrganizer(creationParams.getOrganizer(),
+ ownerActivity.getUid(), ownerActivity.info.processName);
ownerActivity.getTask().addChild(taskFragment, POSITION_TOP);
taskFragment.setWindowingMode(creationParams.getWindowingMode());
taskFragment.setBounds(creationParams.getInitialBounds());
diff --git a/services/tests/PackageManagerServiceTests/unit/src/com/android/server/pm/test/verify/domain/DomainVerificationPackageTest.kt b/services/tests/PackageManagerServiceTests/unit/src/com/android/server/pm/test/verify/domain/DomainVerificationPackageTest.kt
index 3ffce8c..ad79c65 100644
--- a/services/tests/PackageManagerServiceTests/unit/src/com/android/server/pm/test/verify/domain/DomainVerificationPackageTest.kt
+++ b/services/tests/PackageManagerServiceTests/unit/src/com/android/server/pm/test/verify/domain/DomainVerificationPackageTest.kt
@@ -624,6 +624,60 @@
}
@Test
+ fun migratePackageSelected() {
+ val pkgName = PKG_ONE
+ val pkgBefore = mockPkgState(pkgName, UUID_ONE, SIGNATURE_ONE,
+ listOf(DOMAIN_1), listOf(DOMAIN_2))
+ val pkgAfter = mockPkgState(pkgName, UUID_TWO, SIGNATURE_TWO,
+ listOf(DOMAIN_1), listOf(DOMAIN_2))
+
+ val map = mutableMapOf<String, PackageStateInternal>()
+ val service = makeService { map[it] }
+ service.addPackage(pkgBefore)
+
+ // Only insert the package after addPackage call to ensure the service doesn't access
+ // a live package inside the addPackage logic. It should only use the provided input.
+ map[pkgName] = pkgBefore
+
+ assertThat(service.setStatus(UUID_ONE, setOf(DOMAIN_1), STATE_SUCCESS))
+ .isEqualTo(DomainVerificationManager.STATUS_OK)
+
+ assertThat(service.setUserSelection(UUID_ONE, setOf(DOMAIN_2), true, USER_ID))
+ .isEqualTo(DomainVerificationManager.STATUS_OK)
+
+ service.getInfo(pkgName).run {
+ assertThat(identifier).isEqualTo(UUID_ONE)
+ assertThat(hostToStateMap).containsExactlyEntriesIn(mapOf(
+ DOMAIN_1 to STATE_SUCCESS,
+ ))
+ }
+ assertThat(service.getUserState(pkgName).hostToStateMap).containsExactlyEntriesIn(mapOf(
+ DOMAIN_1 to DOMAIN_STATE_VERIFIED,
+ DOMAIN_2 to DOMAIN_STATE_SELECTED,
+ ))
+ assertThat(service.queryValidVerificationPackageNames()).containsExactly(pkgName)
+
+ // Now remove the package because migrateState shouldn't use it either
+ map.remove(pkgName)
+
+ service.migrateState(pkgBefore, pkgAfter)
+
+ map[pkgName] = pkgAfter
+
+ service.getInfo(pkgName).run {
+ assertThat(identifier).isEqualTo(UUID_TWO)
+ assertThat(hostToStateMap).containsExactlyEntriesIn(mapOf(
+ DOMAIN_1 to STATE_SUCCESS,
+ ))
+ }
+ assertThat(service.getUserState(pkgName).hostToStateMap).containsExactlyEntriesIn(mapOf(
+ DOMAIN_1 to DOMAIN_STATE_VERIFIED,
+ DOMAIN_2 to DOMAIN_STATE_SELECTED,
+ ))
+ assertThat(service.queryValidVerificationPackageNames()).containsExactly(pkgName)
+ }
+
+ @Test
fun backupAndRestore() {
// This test acts as a proxy for true user restore through PackageManager,
// as that's much harder to test for real.
@@ -804,7 +858,8 @@
pkgName: String,
domainSetId: UUID,
signature: String,
- domains: List<String> = listOf(DOMAIN_1, DOMAIN_2),
+ autoVerifyDomains: List<String> = listOf(DOMAIN_1, DOMAIN_2),
+ otherDomains: List<String> = listOf(),
isSystemApp: Boolean = false
) = mockThrowOnUnmocked<PackageStateInternal> {
val pkg = mockThrowOnUnmocked<AndroidPackage> {
@@ -812,23 +867,25 @@
whenever(targetSdkVersion) { Build.VERSION_CODES.S }
whenever(isEnabled) { true }
+ fun baseIntent(domain: String) = ParsedIntentInfoImpl().apply {
+ intentFilter.apply {
+ addAction(Intent.ACTION_VIEW)
+ addCategory(Intent.CATEGORY_BROWSABLE)
+ addCategory(Intent.CATEGORY_DEFAULT)
+ addDataScheme("http")
+ addDataScheme("https")
+ addDataPath("/sub", PatternMatcher.PATTERN_LITERAL)
+ addDataAuthority(domain, null)
+ }
+ }
+
val activityList = listOf(
ParsedActivityImpl().apply {
- domains.forEach {
- addIntent(
- ParsedIntentInfoImpl().apply {
- intentFilter.apply {
- autoVerify = true
- addAction(Intent.ACTION_VIEW)
- addCategory(Intent.CATEGORY_BROWSABLE)
- addCategory(Intent.CATEGORY_DEFAULT)
- addDataScheme("http")
- addDataScheme("https")
- addDataPath("/sub", PatternMatcher.PATTERN_LITERAL)
- addDataAuthority(it, null)
- }
- }
- )
+ autoVerifyDomains.forEach {
+ addIntent(baseIntent(it).apply { intentFilter.autoVerify = true })
+ }
+ otherDomains.forEach {
+ addIntent(baseIntent(it).apply { intentFilter.autoVerify = false })
}
},
)
diff --git a/services/tests/mockingservicestests/src/com/android/server/compat/overrides/AppCompatOverridesParserTest.java b/services/tests/mockingservicestests/src/com/android/server/compat/overrides/AppCompatOverridesParserTest.java
index df19be4..a34db63 100644
--- a/services/tests/mockingservicestests/src/com/android/server/compat/overrides/AppCompatOverridesParserTest.java
+++ b/services/tests/mockingservicestests/src/com/android/server/compat/overrides/AppCompatOverridesParserTest.java
@@ -16,6 +16,8 @@
package com.android.server.compat.overrides;
+import static android.content.pm.PackageManager.CERT_INPUT_SHA256;
+
import static com.google.common.truth.Truth.assertThat;
import static org.mockito.ArgumentMatchers.anyInt;
@@ -31,6 +33,8 @@
import androidx.test.filters.SmallTest;
+import libcore.util.HexEncoding;
+
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
@@ -182,16 +186,18 @@
@Test
public void parsePackageOverrides_emptyConfigNoOwnedChangeIds_returnsEmpty() {
- Map<Long, PackageOverride> result = AppCompatOverridesParser.parsePackageOverrides(
- /* configStr= */ "", /* versionCode= */ 0, /* changeIdsToSkip= */ emptySet());
+ Map<Long, PackageOverride> result = mParser.parsePackageOverrides(
+ /* configStr= */ "", PACKAGE_1, /* versionCode= */ 0, /* changeIdsToSkip= */
+ emptySet());
assertThat(result).isEmpty();
}
@Test
public void parsePackageOverrides_configWithSingleOverride_returnsOverride() {
- Map<Long, PackageOverride> result = AppCompatOverridesParser.parsePackageOverrides(
- /* configStr= */ "123:::true", /* versionCode= */ 5, /* changeIdsToSkip= */
+ Map<Long, PackageOverride> result = mParser.parsePackageOverrides(
+ /* configStr= */ "123:::true", PACKAGE_1, /* versionCode= */
+ 5, /* changeIdsToSkip= */
emptySet());
assertThat(result).hasSize(1);
@@ -201,10 +207,10 @@
@Test
public void parsePackageOverrides_configWithMultipleOverrides_returnsOverrides() {
- Map<Long, PackageOverride> result = AppCompatOverridesParser.parsePackageOverrides(
+ Map<Long, PackageOverride> result = mParser.parsePackageOverrides(
/* configStr= */ "910:3:4:false,78:10::false,12:::false,34:1:2:true,34:10::true,"
+ "56::2:true,56:3:4:false,34:4:8:true,78:6:7:true,910:5::true,"
- + "1112::5:true,56:6::true,1112:6:7:false", /* versionCode= */
+ + "1112::5:true,56:6::true,1112:6:7:false", PACKAGE_1, /* versionCode= */
5, /* changeIdsToSkip= */ emptySet());
assertThat(result).hasSize(6);
@@ -228,8 +234,9 @@
@Test
public void parsePackageOverrides_changeIdsToSkipSpecified_returnsWithoutChangeIdsToSkip() {
ArraySet<Long> changeIdsToSkip = new ArraySet<>(Arrays.asList(34L, 56L));
- Map<Long, PackageOverride> result = AppCompatOverridesParser.parsePackageOverrides(
- /* configStr= */ "12:::true,56:3:7:true", /* versionCode= */ 5, changeIdsToSkip);
+ Map<Long, PackageOverride> result = mParser.parsePackageOverrides(
+ /* configStr= */ "12:::true,56:3:7:true", PACKAGE_1, /* versionCode= */ 5,
+ changeIdsToSkip);
assertThat(result).hasSize(1);
assertThat(result.get(12L)).isEqualTo(
@@ -239,8 +246,77 @@
@Test
public void parsePackageOverrides_changeIdsToSkipContainsAllIds_returnsEmpty() {
ArraySet<Long> changeIdsToSkip = new ArraySet<>(Arrays.asList(12L, 34L));
- Map<Long, PackageOverride> result = AppCompatOverridesParser.parsePackageOverrides(
- /* configStr= */ "12:::true", /* versionCode= */ 5, changeIdsToSkip);
+ Map<Long, PackageOverride> result = mParser.parsePackageOverrides(
+ /* configStr= */ "12:::true", PACKAGE_1, /* versionCode= */ 5, changeIdsToSkip);
+
+ assertThat(result).isEmpty();
+ }
+
+ @Test
+ public void parsePackageOverrides_signatureInvalid() {
+ when(mPackageManager.hasSigningCertificate(PACKAGE_1, HexEncoding.decode("aa"),
+ CERT_INPUT_SHA256)).thenReturn(false);
+
+ Map<Long, PackageOverride> result = mParser.parsePackageOverrides(
+ /* configStr= */ "aa~12:::true,56:::true", PACKAGE_1,
+ /* versionCode= */ 0, /* changeIdsToSkip= */ emptySet());
+
+ assertThat(result).isEmpty();
+ }
+
+ @Test
+ public void parsePackageOverrides_signatureInvalid_oddNumberOfCharacters() {
+ // Valid hex encoding should always be an even number of characters.
+ Map<Long, PackageOverride> result = mParser.parsePackageOverrides(
+ /* configStr= */ "a~12:::true,56:::true", PACKAGE_1,
+ /* versionCode= */ 0, /* changeIdsToSkip= */ emptySet());
+
+ assertThat(result).isEmpty();
+ }
+
+ @Test
+ public void parsePackageOverrides_signatureValid() {
+ when(mPackageManager.hasSigningCertificate(PACKAGE_1, HexEncoding.decode("bb"),
+ CERT_INPUT_SHA256)).thenReturn(true);
+
+ Map<Long, PackageOverride> result = mParser.parsePackageOverrides(
+ /* configStr= */ "bb~12:::true,56:::false", PACKAGE_1,
+ /* versionCode= */ 0, /* changeIdsToSkip= */ emptySet());
+
+ assertThat(result.keySet()).containsExactly(12L, 56L);
+ }
+
+ @Test
+ public void parsePackageOverrides_emptySignature() {
+ Map<Long, PackageOverride> result = mParser.parsePackageOverrides(
+ /* configStr= */ "~12:::true,56:::false", PACKAGE_1,
+ /* versionCode= */ 0, /* changeIdsToSkip= */ emptySet());
+
+ assertThat(result.keySet()).containsExactly(12L, 56L);
+ }
+
+ @Test
+ public void parsePackageOverrides_multipleSignatures() {
+ when(mPackageManager.hasSigningCertificate(PACKAGE_1, HexEncoding.decode("aa"),
+ CERT_INPUT_SHA256)).thenReturn(true);
+ when(mPackageManager.hasSigningCertificate(PACKAGE_1, HexEncoding.decode("bb"),
+ CERT_INPUT_SHA256)).thenReturn(true);
+
+ Map<Long, PackageOverride> result = mParser.parsePackageOverrides(
+ /* configStr= */ "aa~bb~12:::true,56:::false", PACKAGE_1,
+ /* versionCode= */0, /* changeIdsToSkip= */ emptySet());
+
+ assertThat(result).isEmpty();
+ }
+
+ @Test
+ public void parsePackageOverrides_signatureOnly() {
+ when(mPackageManager.hasSigningCertificate(PACKAGE_1, HexEncoding.decode("aa"),
+ CERT_INPUT_SHA256)).thenReturn(true);
+
+ Map<Long, PackageOverride> result = mParser.parsePackageOverrides(
+ /* configStr= */ "aa~", PACKAGE_1,
+ /* versionCode= */ 0, /* changeIdsToSkip= */ emptySet());
assertThat(result).isEmpty();
}
@@ -248,9 +324,9 @@
@Test
public void parsePackageOverrides_someOverridesAreInvalid_returnsWithoutInvalidOverrides() {
// We add a valid entry before and after the invalid ones to make sure they are applied.
- Map<Long, PackageOverride> result = AppCompatOverridesParser.parsePackageOverrides(
+ Map<Long, PackageOverride> result = mParser.parsePackageOverrides(
/* configStr= */ "12:::True,56:1:2:FALSE,56:3:true,78:4:8:true:,C1:::true,910:::no,"
- + "1112:1:ten:true,1112:one:10:true,,1314:7:3:false,34:::",
+ + "1112:1:ten:true,1112:one:10:true,,1314:7:3:false,34:::", PACKAGE_1,
/* versionCode= */ 5, /* changeIdsToSkip= */ emptySet());
assertThat(result).hasSize(2);
diff --git a/services/tests/servicestests/src/com/android/server/BatteryServiceTest.java b/services/tests/servicestests/src/com/android/server/BatteryServiceTest.java
deleted file mode 100644
index a2ecbc3..0000000
--- a/services/tests/servicestests/src/com/android/server/BatteryServiceTest.java
+++ /dev/null
@@ -1,137 +0,0 @@
-/*
- * Copyright (C) 2017 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;
-
-import static junit.framework.Assert.*;
-
-import static org.mockito.Mockito.*;
-
-import android.hardware.health.V2_0.IHealth;
-import android.hidl.manager.V1_0.IServiceManager;
-import android.hidl.manager.V1_0.IServiceNotification;
-import android.test.AndroidTestCase;
-
-import androidx.test.filters.SmallTest;
-
-import org.mockito.ArgumentMatcher;
-import org.mockito.Mock;
-import org.mockito.MockitoAnnotations;
-import org.mockito.invocation.InvocationOnMock;
-
-import java.util.Arrays;
-import java.util.Collection;
-import java.util.NoSuchElementException;
-
-public class BatteryServiceTest extends AndroidTestCase {
-
- @Mock IServiceManager mMockedManager;
- @Mock IHealth mMockedHal;
- @Mock IHealth mMockedHal2;
-
- @Mock BatteryService.HealthServiceWrapper.Callback mCallback;
- @Mock BatteryService.HealthServiceWrapper.IServiceManagerSupplier mManagerSupplier;
- @Mock BatteryService.HealthServiceWrapper.IHealthSupplier mHealthServiceSupplier;
- BatteryService.HealthServiceWrapper mWrapper;
-
- private static final String VENDOR = BatteryService.HealthServiceWrapper.INSTANCE_VENDOR;
-
- @Override
- public void setUp() {
- MockitoAnnotations.initMocks(this);
- }
-
- @Override
- public void tearDown() {
- if (mWrapper != null)
- mWrapper.getHandlerThread().quitSafely();
- }
-
- public static <T> ArgumentMatcher<T> isOneOf(Collection<T> collection) {
- return new ArgumentMatcher<T>() {
- @Override public boolean matches(T e) {
- return collection.contains(e);
- }
- @Override public String toString() {
- return collection.toString();
- }
- };
- }
-
- private void initForInstances(String... instanceNamesArr) throws Exception {
- final Collection<String> instanceNames = Arrays.asList(instanceNamesArr);
- doAnswer((invocation) -> {
- // technically, preexisting is ignored by
- // BatteryService.HealthServiceWrapper.Notification, but still call it correctly.
- sendNotification(invocation, true);
- sendNotification(invocation, true);
- sendNotification(invocation, false);
- return null;
- }).when(mMockedManager).registerForNotifications(
- eq(IHealth.kInterfaceName),
- argThat(isOneOf(instanceNames)),
- any(IServiceNotification.class));
-
- doReturn(mMockedManager).when(mManagerSupplier).get();
- doReturn(mMockedHal) // init calls this
- .doReturn(mMockedHal) // notification 1
- .doReturn(mMockedHal) // notification 2
- .doReturn(mMockedHal2) // notification 3
- .doThrow(new RuntimeException("Should not call getService for more than 4 times"))
- .when(mHealthServiceSupplier).get(argThat(isOneOf(instanceNames)));
-
- mWrapper = new BatteryService.HealthServiceWrapper();
- }
-
- private void waitHandlerThreadFinish() throws Exception {
- for (int i = 0; i < 5; i++) {
- if (!mWrapper.getHandlerThread().getThreadHandler().hasMessagesOrCallbacks()) {
- return;
- }
- Thread.sleep(300);
- }
- assertFalse(mWrapper.getHandlerThread().getThreadHandler().hasMessagesOrCallbacks());
- }
-
- private static void sendNotification(InvocationOnMock invocation, boolean preexisting)
- throws Exception {
- ((IServiceNotification)invocation.getArguments()[2]).onRegistration(
- IHealth.kInterfaceName,
- (String)invocation.getArguments()[1],
- preexisting);
- }
-
- @SmallTest
- public void testWrapPreferVendor() throws Exception {
- initForInstances(VENDOR);
- mWrapper.init(mCallback, mManagerSupplier, mHealthServiceSupplier);
- waitHandlerThreadFinish();
- verify(mCallback, times(1)).onRegistration(same(null), same(mMockedHal), eq(VENDOR));
- verify(mCallback, never()).onRegistration(same(mMockedHal), same(mMockedHal), anyString());
- verify(mCallback, times(1)).onRegistration(same(mMockedHal), same(mMockedHal2), eq(VENDOR));
- }
-
- @SmallTest
- public void testNoService() throws Exception {
- initForInstances("unrelated");
- try {
- mWrapper.init(mCallback, mManagerSupplier, mHealthServiceSupplier);
- fail("Expect NoSuchElementException");
- } catch (NoSuchElementException ex) {
- // expected
- }
- }
-}
diff --git a/services/tests/servicestests/src/com/android/server/accessibility/AccessibilityManagerServiceTest.java b/services/tests/servicestests/src/com/android/server/accessibility/AccessibilityManagerServiceTest.java
index b1da890..ae2b8dc 100644
--- a/services/tests/servicestests/src/com/android/server/accessibility/AccessibilityManagerServiceTest.java
+++ b/services/tests/servicestests/src/com/android/server/accessibility/AccessibilityManagerServiceTest.java
@@ -42,7 +42,6 @@
import android.graphics.drawable.Icon;
import android.os.IBinder;
import android.provider.Settings;
-import android.test.AndroidTestCase;
import android.test.suitebuilder.annotation.SmallTest;
import android.view.accessibility.AccessibilityNodeInfo.AccessibilityAction;
@@ -57,13 +56,16 @@
import com.android.server.wm.ActivityTaskManagerInternal;
import com.android.server.wm.WindowManagerInternal;
+import org.junit.Assert;
+import org.junit.Before;
+import org.junit.Test;
import org.mockito.Mock;
import org.mockito.MockitoAnnotations;
/**
* APCT tests for {@link AccessibilityManagerService}.
*/
-public class AccessibilityManagerServiceTest extends AndroidTestCase {
+public class AccessibilityManagerServiceTest {
private static final String TAG = "A11Y_MANAGER_SERVICE_TEST";
private static final int ACTION_ID = 20;
private static final String LABEL = "label";
@@ -108,8 +110,8 @@
private AccessibilityServiceConnection mAccessibilityServiceConnection;
private AccessibilityManagerService mA11yms;
- @Override
- protected void setUp() throws Exception {
+ @Before
+ public void setUp() throws Exception {
MockitoAnnotations.initMocks(this);
LocalServices.removeServiceForTest(WindowManagerInternal.class);
LocalServices.removeServiceForTest(ActivityTaskManagerInternal.class);
@@ -174,44 +176,48 @@
}
@SmallTest
+ @Test
public void testRegisterSystemActionWithoutPermission() throws Exception {
doThrow(SecurityException.class).when(mMockSecurityPolicy)
.enforceCallingOrSelfPermission(Manifest.permission.MANAGE_ACCESSIBILITY);
try {
mA11yms.registerSystemAction(TEST_ACTION, ACTION_ID);
- fail();
+ Assert.fail();
} catch (SecurityException expected) {
}
verify(mMockSystemActionPerformer, never()).registerSystemAction(ACTION_ID, TEST_ACTION);
}
@SmallTest
+ @Test
public void testRegisterSystemAction() throws Exception {
mA11yms.registerSystemAction(TEST_ACTION, ACTION_ID);
verify(mMockSystemActionPerformer).registerSystemAction(ACTION_ID, TEST_ACTION);
}
- @SmallTest
+ @Test
public void testUnregisterSystemActionWithoutPermission() throws Exception {
doThrow(SecurityException.class).when(mMockSecurityPolicy)
.enforceCallingOrSelfPermission(Manifest.permission.MANAGE_ACCESSIBILITY);
try {
mA11yms.unregisterSystemAction(ACTION_ID);
- fail();
+ Assert.fail();
} catch (SecurityException expected) {
}
verify(mMockSystemActionPerformer, never()).unregisterSystemAction(ACTION_ID);
}
@SmallTest
+ @Test
public void testUnregisterSystemAction() throws Exception {
mA11yms.unregisterSystemAction(ACTION_ID);
verify(mMockSystemActionPerformer).unregisterSystemAction(ACTION_ID);
}
@SmallTest
+ @Test
public void testOnSystemActionsChanged() throws Exception {
setupAccessibilityServiceConnection();
mA11yms.notifySystemActionsChangedLocked(mUserState);
@@ -220,6 +226,7 @@
}
@SmallTest
+ @Test
public void testOnMagnificationTransitionFailed_capabilitiesIsAll_fallBackToPreviousMode() {
final AccessibilityUserState userState = mA11yms.mUserStates.get(
mA11yms.getCurrentUserIdLocked());
@@ -230,11 +237,12 @@
mA11yms.onMagnificationTransitionEndedLocked(false);
- assertEquals(Settings.Secure.ACCESSIBILITY_MAGNIFICATION_MODE_WINDOW,
+ Assert.assertEquals(Settings.Secure.ACCESSIBILITY_MAGNIFICATION_MODE_WINDOW,
userState.getMagnificationModeLocked());
}
@SmallTest
+ @Test
public void testOnClientChange_magnificationEnabledAndCapabilityAll_requestConnection() {
mUserState.mAccessibilityShortcutKeyTargets.add(MAGNIFICATION_CONTROLLER_NAME);
mUserState.setMagnificationCapabilitiesLocked(
@@ -247,6 +255,7 @@
}
@SmallTest
+ @Test
public void testOnClientChange_boundServiceCanControlMagnification_requestConnection() {
setupAccessibilityServiceConnection();
when(mMockSecurityPolicy.canControlMagnification(any())).thenReturn(true);
diff --git a/services/tests/servicestests/src/com/android/server/health/HealthServiceWrapperTest.java b/services/tests/servicestests/src/com/android/server/health/HealthServiceWrapperTest.java
new file mode 100644
index 0000000..c97a67b
--- /dev/null
+++ b/services/tests/servicestests/src/com/android/server/health/HealthServiceWrapperTest.java
@@ -0,0 +1,153 @@
+/*
+ * Copyright (C) 2017 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.health;
+
+import static junit.framework.Assert.assertFalse;
+import static junit.framework.Assert.fail;
+
+import static org.mockito.Mockito.*;
+
+import android.hardware.health.V2_0.IHealth;
+import android.hidl.manager.V1_0.IServiceManager;
+import android.hidl.manager.V1_0.IServiceNotification;
+import android.os.RemoteException;
+
+import androidx.test.filters.SmallTest;
+import androidx.test.runner.AndroidJUnit4;
+
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.ArgumentMatcher;
+import org.mockito.Mock;
+import org.mockito.MockitoAnnotations;
+import org.mockito.invocation.InvocationOnMock;
+
+import java.util.Arrays;
+import java.util.Collection;
+import java.util.NoSuchElementException;
+
+@RunWith(AndroidJUnit4.class)
+public class HealthServiceWrapperTest {
+
+ @Mock IServiceManager mMockedManager;
+ @Mock IHealth mMockedHal;
+ @Mock IHealth mMockedHal2;
+
+ @Mock HealthServiceWrapperHidl.Callback mCallback;
+ @Mock HealthServiceWrapperHidl.IServiceManagerSupplier mManagerSupplier;
+ @Mock HealthServiceWrapperHidl.IHealthSupplier mHealthServiceSupplier;
+ HealthServiceWrapper mWrapper;
+
+ private static final String VENDOR = HealthServiceWrapperHidl.INSTANCE_VENDOR;
+
+ @Before
+ public void setUp() {
+ MockitoAnnotations.initMocks(this);
+ }
+
+ @After
+ public void tearDown() {
+ if (mWrapper != null) mWrapper.getHandlerThread().quitSafely();
+ }
+
+ public static <T> ArgumentMatcher<T> isOneOf(Collection<T> collection) {
+ return new ArgumentMatcher<T>() {
+ @Override
+ public boolean matches(T e) {
+ return collection.contains(e);
+ }
+
+ @Override
+ public String toString() {
+ return collection.toString();
+ }
+ };
+ }
+
+ private void initForInstances(String... instanceNamesArr) throws Exception {
+ final Collection<String> instanceNames = Arrays.asList(instanceNamesArr);
+ doAnswer(
+ (invocation) -> {
+ // technically, preexisting is ignored by
+ // HealthServiceWrapperHidl.Notification, but still call it correctly.
+ sendNotification(invocation, true);
+ sendNotification(invocation, true);
+ sendNotification(invocation, false);
+ return null;
+ })
+ .when(mMockedManager)
+ .registerForNotifications(
+ eq(IHealth.kInterfaceName),
+ argThat(isOneOf(instanceNames)),
+ any(IServiceNotification.class));
+
+ doReturn(mMockedManager).when(mManagerSupplier).get();
+ doReturn(mMockedHal) // init calls this
+ .doReturn(mMockedHal) // notification 1
+ .doReturn(mMockedHal) // notification 2
+ .doReturn(mMockedHal2) // notification 3
+ .doThrow(new RuntimeException("Should not call getService for more than 4 times"))
+ .when(mHealthServiceSupplier)
+ .get(argThat(isOneOf(instanceNames)));
+ }
+
+ private void waitHandlerThreadFinish() throws Exception {
+ for (int i = 0; i < 5; i++) {
+ if (!mWrapper.getHandlerThread().getThreadHandler().hasMessagesOrCallbacks()) {
+ return;
+ }
+ Thread.sleep(300);
+ }
+ assertFalse(mWrapper.getHandlerThread().getThreadHandler().hasMessagesOrCallbacks());
+ }
+
+ private static void sendNotification(InvocationOnMock invocation, boolean preexisting)
+ throws Exception {
+ ((IServiceNotification) invocation.getArguments()[2])
+ .onRegistration(
+ IHealth.kInterfaceName, (String) invocation.getArguments()[1], preexisting);
+ }
+
+ private void createWrapper() throws RemoteException {
+ mWrapper = HealthServiceWrapper.create(mCallback, mManagerSupplier, mHealthServiceSupplier);
+ }
+
+ @SmallTest
+ @Test
+ public void testWrapPreferVendor() throws Exception {
+ initForInstances(VENDOR);
+ createWrapper();
+ waitHandlerThreadFinish();
+ verify(mCallback, times(1)).onRegistration(same(null), same(mMockedHal), eq(VENDOR));
+ verify(mCallback, never()).onRegistration(same(mMockedHal), same(mMockedHal), anyString());
+ verify(mCallback, times(1)).onRegistration(same(mMockedHal), same(mMockedHal2), eq(VENDOR));
+ }
+
+ @SmallTest
+ @Test
+ public void testNoService() throws Exception {
+ initForInstances("unrelated");
+ try {
+ createWrapper();
+ fail("Expect NoSuchElementException");
+ } catch (NoSuchElementException ex) {
+ // expected
+ }
+ }
+}
diff --git a/services/tests/uiservicestests/src/com/android/server/notification/CountdownConditionProviderTest.java b/services/tests/uiservicestests/src/com/android/server/notification/CountdownConditionProviderTest.java
new file mode 100644
index 0000000..ddb9b43
--- /dev/null
+++ b/services/tests/uiservicestests/src/com/android/server/notification/CountdownConditionProviderTest.java
@@ -0,0 +1,73 @@
+/*
+ * 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.notification;
+
+import static org.junit.Assert.assertEquals;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.spy;
+
+import android.app.Application;
+import android.app.PendingIntent;
+import android.content.Intent;
+import android.net.Uri;
+import android.testing.AndroidTestingRunner;
+import android.testing.TestableLooper.RunWithLooper;
+
+import androidx.test.filters.SmallTest;
+
+import com.android.server.UiServiceTestCase;
+import com.android.server.pm.PackageManagerService;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.MockitoAnnotations;
+
+@RunWith(AndroidTestingRunner.class)
+@SmallTest
+@RunWithLooper
+public class CountdownConditionProviderTest extends UiServiceTestCase {
+
+ CountdownConditionProvider mService;
+
+ @Before
+ public void setUp() throws Exception {
+ MockitoAnnotations.initMocks(this);
+
+ Intent startIntent =
+ new Intent("com.android.server.notification.CountdownConditionProvider");
+ startIntent.setPackage("android");
+ CountdownConditionProvider service = new CountdownConditionProvider();
+ service.attach(
+ getContext(),
+ null, // ActivityThread not actually used in Service
+ CountdownConditionProvider.class.getName(),
+ null, // token not needed when not talking with the activity manager
+ mock(Application.class),
+ null // mocked services don't talk with the activity manager
+ );
+ service.onCreate();
+ service.onBind(startIntent);
+ mService = spy(service);
+ }
+
+ @Test
+ public void testGetPendingIntent() {
+ PendingIntent pi = mService.getPendingIntent(Uri.EMPTY);
+ assertEquals(PackageManagerService.PLATFORM_PACKAGE_NAME, pi.getIntent().getPackage());
+ }
+}
diff --git a/services/tests/uiservicestests/src/com/android/server/notification/EventConditionProviderTest.java b/services/tests/uiservicestests/src/com/android/server/notification/EventConditionProviderTest.java
new file mode 100644
index 0000000..4c440ca
--- /dev/null
+++ b/services/tests/uiservicestests/src/com/android/server/notification/EventConditionProviderTest.java
@@ -0,0 +1,73 @@
+/*
+ * 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.notification;
+
+import static org.junit.Assert.assertEquals;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.spy;
+
+import android.app.Application;
+import android.app.PendingIntent;
+import android.content.Intent;
+import android.net.Uri;
+import android.testing.AndroidTestingRunner;
+import android.testing.TestableLooper.RunWithLooper;
+
+import androidx.test.filters.SmallTest;
+
+import com.android.server.UiServiceTestCase;
+import com.android.server.pm.PackageManagerService;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.MockitoAnnotations;
+
+@RunWith(AndroidTestingRunner.class)
+@SmallTest
+@RunWithLooper
+public class EventConditionProviderTest extends UiServiceTestCase {
+
+ EventConditionProvider mService;
+
+ @Before
+ public void setUp() throws Exception {
+ MockitoAnnotations.initMocks(this);
+
+ Intent startIntent =
+ new Intent("com.android.server.notification.EventConditionProvider");
+ startIntent.setPackage("android");
+ EventConditionProvider service = new EventConditionProvider();
+ service.attach(
+ getContext(),
+ null, // ActivityThread not actually used in Service
+ CountdownConditionProvider.class.getName(),
+ null, // token not needed when not talking with the activity manager
+ mock(Application.class),
+ null // mocked services don't talk with the activity manager
+ );
+ service.onCreate();
+ service.onBind(startIntent);
+ mService = spy(service);
+ }
+
+ @Test
+ public void testGetPendingIntent() {
+ PendingIntent pi = mService.getPendingIntent(1000);
+ assertEquals(PackageManagerService.PLATFORM_PACKAGE_NAME, pi.getIntent().getPackage());
+ }
+}
diff --git a/services/tests/wmtests/src/com/android/server/wm/TaskFragmentOrganizerControllerTest.java b/services/tests/wmtests/src/com/android/server/wm/TaskFragmentOrganizerControllerTest.java
index cbdf4fe..d89d64a 100644
--- a/services/tests/wmtests/src/com/android/server/wm/TaskFragmentOrganizerControllerTest.java
+++ b/services/tests/wmtests/src/com/android/server/wm/TaskFragmentOrganizerControllerTest.java
@@ -254,7 +254,8 @@
});
// Allow transaction to change a TaskFragment created by the organizer.
- mTaskFragment.setTaskFragmentOrganizer(mOrganizerToken, 10 /* pid */);
+ mTaskFragment.setTaskFragmentOrganizer(mOrganizerToken, 10 /* uid */,
+ "Test:TaskFragmentOrganizer" /* processName */);
mAtm.getWindowOrganizerController().applyTransaction(mTransaction);
}
@@ -276,7 +277,8 @@
});
// Allow transaction to change a TaskFragment created by the organizer.
- mTaskFragment.setTaskFragmentOrganizer(mOrganizerToken, 10 /* pid */);
+ mTaskFragment.setTaskFragmentOrganizer(mOrganizerToken, 10 /* uid */,
+ "Test:TaskFragmentOrganizer" /* processName */);
mAtm.getWindowOrganizerController().applyTransaction(mTransaction);
}
@@ -301,7 +303,8 @@
});
// Allow transaction to change a TaskFragment created by the organizer.
- mTaskFragment.setTaskFragmentOrganizer(mOrganizerToken, 10 /* pid */);
+ mTaskFragment.setTaskFragmentOrganizer(mOrganizerToken, 10 /* uid */,
+ "Test:TaskFragmentOrganizer" /* processName */);
clearInvocations(mAtm.mRootWindowContainer);
mAtm.getWindowOrganizerController().applyTransaction(mTransaction);
@@ -337,8 +340,10 @@
});
// Allow transaction to change a TaskFragment created by the organizer.
- mTaskFragment.setTaskFragmentOrganizer(mOrganizerToken, 10 /* pid */);
- taskFragment2.setTaskFragmentOrganizer(mOrganizerToken, 10 /* pid */);
+ mTaskFragment.setTaskFragmentOrganizer(mOrganizerToken, 10 /* uid */,
+ "Test:TaskFragmentOrganizer" /* processName */);
+ taskFragment2.setTaskFragmentOrganizer(mOrganizerToken, 10 /* uid */,
+ "Test:TaskFragmentOrganizer" /* processName */);
clearInvocations(mAtm.mRootWindowContainer);
mAtm.getWindowOrganizerController().applyTransaction(mTransaction);
@@ -391,7 +396,8 @@
});
// Allow transaction to change a TaskFragment created by the organizer.
- mTaskFragment.setTaskFragmentOrganizer(mOrganizerToken, 10 /* pid */);
+ mTaskFragment.setTaskFragmentOrganizer(mOrganizerToken, 10 /* uid */,
+ "Test:TaskFragmentOrganizer" /* processName */);
clearInvocations(mAtm.mRootWindowContainer);
mAtm.getWindowOrganizerController().applyTransaction(mTransaction);
diff --git a/services/tests/wmtests/src/com/android/server/wm/TaskFragmentTest.java b/services/tests/wmtests/src/com/android/server/wm/TaskFragmentTest.java
index 42f4d58..6737b1a 100644
--- a/services/tests/wmtests/src/com/android/server/wm/TaskFragmentTest.java
+++ b/services/tests/wmtests/src/com/android/server/wm/TaskFragmentTest.java
@@ -20,12 +20,15 @@
import static com.android.dx.mockito.inline.extended.ExtendedMockito.spyOn;
import static com.android.dx.mockito.inline.extended.ExtendedMockito.verify;
+import static org.junit.Assert.assertEquals;
import static org.mockito.Mockito.clearInvocations;
import android.graphics.Rect;
+import android.os.Binder;
import android.platform.test.annotations.Presubmit;
import android.view.SurfaceControl;
import android.window.ITaskFragmentOrganizer;
+import android.window.TaskFragmentInfo;
import android.window.TaskFragmentOrganizer;
import androidx.test.filters.MediumTest;
@@ -64,6 +67,7 @@
mTaskFragment = new TaskFragmentBuilder(mAtm)
.setCreateParentTask()
.setOrganizer(mOrganizer)
+ .setFragmentToken(new Binder())
.build();
mLeash = mTaskFragment.getSurfaceControl();
spyOn(mTaskFragment);
@@ -103,4 +107,23 @@
verify(mTransaction).setPosition(mLeash, 500, 500);
verify(mTransaction).setWindowCrop(mLeash, 500, 500);
}
+
+ /**
+ * Tests that when a {@link TaskFragmentInfo} is generated from a {@link TaskFragment}, an
+ * activity that has not yet been attached to a process because it is being initialized but
+ * belongs to the TaskFragmentOrganizer process is still reported in the TaskFragmentInfo.
+ */
+ @Test
+ public void testActivityStillReported_NotYetAssignedToProcess() {
+ mTaskFragment.addChild(new ActivityBuilder(mAtm).setUid(DEFAULT_TASK_FRAGMENT_ORGANIZER_UID)
+ .setProcessName(DEFAULT_TASK_FRAGMENT_ORGANIZER_PROCESS_NAME).build());
+ final ActivityRecord activity = mTaskFragment.getTopMostActivity();
+ // Remove the process to simulate an activity that has not yet been attached to a process
+ activity.app = null;
+ final TaskFragmentInfo info = activity.getTaskFragment().getTaskFragmentInfo();
+ assertEquals(1, info.getRunningActivityCount());
+ assertEquals(1, info.getActivities().size());
+ assertEquals(false, info.isEmpty());
+ assertEquals(activity.token, info.getActivities().get(0));
+ }
}
diff --git a/services/tests/wmtests/src/com/android/server/wm/WindowTestsBase.java b/services/tests/wmtests/src/com/android/server/wm/WindowTestsBase.java
index 386ff4b..996b4b2 100644
--- a/services/tests/wmtests/src/com/android/server/wm/WindowTestsBase.java
+++ b/services/tests/wmtests/src/com/android/server/wm/WindowTestsBase.java
@@ -126,6 +126,9 @@
// Default package name
static final String DEFAULT_COMPONENT_PACKAGE_NAME = "com.foo";
+ static final int DEFAULT_TASK_FRAGMENT_ORGANIZER_UID = 10000;
+ static final String DEFAULT_TASK_FRAGMENT_ORGANIZER_PROCESS_NAME = "Test:TaskFragmentOrganizer";
+
// Default base activity name
private static final String DEFAULT_COMPONENT_CLASS_NAME = ".BarActivity";
@@ -1234,7 +1237,8 @@
}
if (mOrganizer != null) {
taskFragment.setTaskFragmentOrganizer(
- mOrganizer.getOrganizerToken(), 10000 /* pid */);
+ mOrganizer.getOrganizerToken(), DEFAULT_TASK_FRAGMENT_ORGANIZER_UID,
+ DEFAULT_TASK_FRAGMENT_ORGANIZER_PROCESS_NAME);
}
return taskFragment;
}
diff --git a/telephony/java/android/telephony/CarrierConfigManager.java b/telephony/java/android/telephony/CarrierConfigManager.java
index 2141e42..3e9ae38 100644
--- a/telephony/java/android/telephony/CarrierConfigManager.java
+++ b/telephony/java/android/telephony/CarrierConfigManager.java
@@ -21,6 +21,7 @@
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.annotation.RequiresPermission;
+import android.annotation.SuppressAutoDoc;
import android.annotation.SuppressLint;
import android.annotation.SystemApi;
import android.annotation.SystemService;
@@ -5978,12 +5979,15 @@
* any carrier specific configuration has been applied.
*
* <p>Requires Permission:
- * {@link android.Manifest.permission#READ_PHONE_STATE READ_PHONE_STATE}
+ * {@link android.Manifest.permission#READ_PHONE_STATE READ_PHONE_STATE}, or the calling app
+ * has carrier privileges (see {@link TelephonyManager#hasCarrierPrivileges()}).
*
* @param subId the subscription ID, normally obtained from {@link SubscriptionManager}.
* @return A {@link PersistableBundle} containing the config for the given subId, or default
* values for an invalid subId.
*/
+ @SuppressAutoDoc // Blocked by b/72967236 - no support for carrier privileges
+ @RequiresPermission(Manifest.permission.READ_PHONE_STATE)
@Nullable
public PersistableBundle getConfigForSubId(int subId) {
try {
@@ -6072,10 +6076,13 @@
* called to confirm whether any carrier specific configuration has been applied.
*
* <p>Requires Permission:
- * {@link android.Manifest.permission#READ_PHONE_STATE READ_PHONE_STATE}
+ * {@link android.Manifest.permission#READ_PHONE_STATE READ_PHONE_STATE}, or the calling app
+ * has carrier privileges (see {@link TelephonyManager#hasCarrierPrivileges()}).
*
* @see #getConfigForSubId
*/
+ @SuppressAutoDoc // Blocked by b/72967236 - no support for carrier privileges
+ @RequiresPermission(Manifest.permission.READ_PHONE_STATE)
@Nullable
public PersistableBundle getConfig() {
return getConfigForSubId(SubscriptionManager.getDefaultSubscriptionId());
@@ -6084,8 +6091,8 @@
/**
* Determines whether a configuration {@link PersistableBundle} obtained from
* {@link #getConfig()} or {@link #getConfigForSubId(int)} corresponds to an identified carrier.
- * <p>
- * When an app receives the {@link CarrierConfigManager#ACTION_CARRIER_CONFIG_CHANGED}
+ *
+ * <p>When an app receives the {@link CarrierConfigManager#ACTION_CARRIER_CONFIG_CHANGED}
* broadcast which informs it that the carrier configuration has changed, it is possible
* that another reload of the carrier configuration has begun since the intent was sent.
* In this case, the carrier configuration the app fetches (e.g. via {@link #getConfig()})
@@ -6094,14 +6101,12 @@
* return true because it may belong to another previous identified carrier. Users should
* always call {@link #getConfig()} or {@link #getConfigForSubId(int)} after receiving the
* broadcast {@link #ACTION_CARRIER_CONFIG_CHANGED}.
- * </p>
- * <p>
- * After using {@link #getConfig()} or {@link #getConfigForSubId(int)} an app should always
+ *
+ * <p>After using {@link #getConfig()} or {@link #getConfigForSubId(int)} an app should always
* use this method to confirm whether any carrier specific configuration has been applied.
* Especially when an app misses the broadcast {@link #ACTION_CARRIER_CONFIG_CHANGED} but it
* still needs to get the current configuration, it must use this method to verify whether the
* configuration is default or carrier overridden.
- * </p>
*
* @param bundle the configuration bundle to be checked.
* @return boolean true if any carrier specific configuration bundle has been applied, false
@@ -6113,19 +6118,20 @@
/**
* Calling this method triggers telephony services to fetch the current carrier configuration.
- * <p>
- * Normally this does not need to be called because the platform reloads config on its own.
+ *
+ * <p>Normally this does not need to be called because the platform reloads config on its own.
* This should be called by a carrier service app if it wants to update config at an arbitrary
* moment.
- * </p>
- * <p>Requires that the calling app has carrier privileges.
- * <p>
- * This method returns before the reload has completed, and
- * {@link android.service.carrier.CarrierService#onLoadConfig} will be called from an
- * arbitrary thread.
- * </p>
- * @see TelephonyManager#hasCarrierPrivileges
+ *
+ * <p>Requires Permission:
+ * {@link android.Manifest.permission#MODIFY_PHONE_STATE MODIFY_PHONE_STATE}, or the calling app
+ * has carrier privileges (see {@link TelephonyManager#hasCarrierPrivileges()}).
+ *
+ * <p>This method returns before the reload has completed, and {@link
+ * android.service.carrier.CarrierService#onLoadConfig} will be called from an arbitrary thread.
*/
+ @SuppressAutoDoc // Blocked by b/72967236 - no support for carrier privileges
+ @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE)
public void notifyConfigChangedForSubId(int subId) {
try {
ICarrierConfigLoader loader = getICarrierConfigLoader();
@@ -6141,11 +6147,10 @@
}
/**
- * Request the carrier config loader to update the cofig for phoneId.
- * <p>
- * Depending on simState, the config may be cleared or loaded from config app. This is only used
- * by SubscriptionInfoUpdater.
- * </p>
+ * Request the carrier config loader to update the config for phoneId.
+ *
+ * <p>Depending on simState, the config may be cleared or loaded from config app. This is only
+ * used by SubscriptionInfoUpdater.
*
* @hide
*/
@@ -6216,13 +6221,16 @@
* Gets the configuration values for a component using its prefix.
*
* <p>Requires Permission:
- * {@link android.Manifest.permission#READ_PHONE_STATE READ_PHONE_STATE}
+ * {@link android.Manifest.permission#READ_PHONE_STATE READ_PHONE_STATE}, or the calling app
+ * has carrier privileges (see {@link TelephonyManager#hasCarrierPrivileges()}).
*
* @param prefix prefix of the component.
* @param subId the subscription ID, normally obtained from {@link SubscriptionManager}.
*
* @see #getConfigForSubId
*/
+ @SuppressAutoDoc // Blocked by b/72967236 - no support for carrier privileges
+ @RequiresPermission(Manifest.permission.READ_PHONE_STATE)
@Nullable
public PersistableBundle getConfigByComponentForSubId(@NonNull String prefix, int subId) {
PersistableBundle configs = getConfigForSubId(subId);
diff --git a/telephony/java/com/android/internal/telephony/RILConstants.java b/telephony/java/com/android/internal/telephony/RILConstants.java
index 61c269c..866fd2c 100644
--- a/telephony/java/com/android/internal/telephony/RILConstants.java
+++ b/telephony/java/com/android/internal/telephony/RILConstants.java
@@ -529,7 +529,7 @@
int RIL_REQUEST_GET_ALLOWED_NETWORK_TYPES_BITMAP = 223;
int RIL_REQUEST_GET_SLICING_CONFIG = 224;
int RIL_REQUEST_ENABLE_VONR = 225;
- int RIL_REQUEST_IS_VONR_ENABLED = 225;
+ int RIL_REQUEST_IS_VONR_ENABLED = 226;
/* Responses begin */
int RIL_RESPONSE_ACKNOWLEDGEMENT = 800;
diff --git a/tests/FlickerTests/OWNERS b/tests/FlickerTests/OWNERS
index b556101..c1221e3 100644
--- a/tests/FlickerTests/OWNERS
+++ b/tests/FlickerTests/OWNERS
@@ -1,3 +1,4 @@
# Bug component: 909476
include /services/core/java/com/android/server/wm/OWNERS
-natanieljr@google.com
\ No newline at end of file
+natanieljr@google.com
+pablogamito@google.com
diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/close/CloseAppBackButtonTest.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/close/CloseAppBackButtonTest.kt
index 9f26c31..209d1aa 100644
--- a/tests/FlickerTests/src/com/android/server/wm/flicker/close/CloseAppBackButtonTest.kt
+++ b/tests/FlickerTests/src/com/android/server/wm/flicker/close/CloseAppBackButtonTest.kt
@@ -65,9 +65,9 @@
@FixMethodOrder(MethodSorters.NAME_ASCENDING)
@Group4
class CloseAppBackButtonTest(testSpec: FlickerTestParameter) : CloseAppTransition(testSpec) {
- override val transition: FlickerBuilder.(Map<String, Any?>) -> Unit
+ override val transition: FlickerBuilder.() -> Unit
get() = {
- super.transition(this, it)
+ super.transition(this)
transitions {
device.pressBack()
wmHelper.waitForHomeActivityVisible()
diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/close/CloseAppHomeButtonTest.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/close/CloseAppHomeButtonTest.kt
index 795766f..ac557cf 100644
--- a/tests/FlickerTests/src/com/android/server/wm/flicker/close/CloseAppHomeButtonTest.kt
+++ b/tests/FlickerTests/src/com/android/server/wm/flicker/close/CloseAppHomeButtonTest.kt
@@ -64,9 +64,9 @@
@FixMethodOrder(MethodSorters.NAME_ASCENDING)
@Group4
class CloseAppHomeButtonTest(testSpec: FlickerTestParameter) : CloseAppTransition(testSpec) {
- override val transition: FlickerBuilder.(Map<String, Any?>) -> Unit
+ override val transition: FlickerBuilder.() -> Unit
get() = {
- super.transition(this, it)
+ super.transition(this)
transitions {
device.pressHome()
wmHelper.waitForHomeActivityVisible()
diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/close/CloseAppTransition.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/close/CloseAppTransition.kt
index 339a5bd..8a2ddf1 100644
--- a/tests/FlickerTests/src/com/android/server/wm/flicker/close/CloseAppTransition.kt
+++ b/tests/FlickerTests/src/com/android/server/wm/flicker/close/CloseAppTransition.kt
@@ -32,7 +32,6 @@
import com.android.server.wm.flicker.navBarLayerRotatesAndScales
import com.android.server.wm.flicker.navBarWindowIsVisible
import com.android.server.wm.flicker.entireScreenCovered
-import com.android.server.wm.flicker.startRotation
import com.android.server.wm.flicker.statusBarLayerIsVisible
import com.android.server.wm.flicker.statusBarLayerRotatesScales
import com.android.server.wm.flicker.statusBarWindowIsVisible
@@ -54,11 +53,11 @@
/**
* Specification of the test transition to execute
*/
- protected open val transition: FlickerBuilder.(Map<String, Any?>) -> Unit = {
+ protected open val transition: FlickerBuilder.() -> Unit = {
setup {
eachRun {
testApp.launchViaIntent(wmHelper)
- this.setRotation(testSpec.config.startRotation)
+ this.setRotation(testSpec.startRotation)
}
}
teardown {
@@ -75,7 +74,7 @@
@FlickerBuilderProvider
fun buildFlicker(): FlickerBuilder {
return FlickerBuilder(instrumentation).apply {
- transition(testSpec.config)
+ transition()
}
}
diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/ime/CloseImeAutoOpenWindowToAppTest.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/ime/CloseImeAutoOpenWindowToAppTest.kt
index 5e21aff..c7dfbf8 100644
--- a/tests/FlickerTests/src/com/android/server/wm/flicker/ime/CloseImeAutoOpenWindowToAppTest.kt
+++ b/tests/FlickerTests/src/com/android/server/wm/flicker/ime/CloseImeAutoOpenWindowToAppTest.kt
@@ -33,7 +33,6 @@
import com.android.server.wm.flicker.navBarLayerIsVisible
import com.android.server.wm.flicker.navBarLayerRotatesAndScales
import com.android.server.wm.flicker.navBarWindowIsVisible
-import com.android.server.wm.flicker.startRotation
import com.android.server.wm.flicker.statusBarLayerIsVisible
import com.android.server.wm.flicker.statusBarLayerRotatesScales
import com.android.server.wm.flicker.statusBarWindowIsVisible
@@ -63,7 +62,7 @@
@Group2
class CloseImeAutoOpenWindowToAppTest(private val testSpec: FlickerTestParameter) {
private val instrumentation: Instrumentation = InstrumentationRegistry.getInstrumentation()
- private val testApp = ImeAppAutoFocusHelper(instrumentation, testSpec.config.startRotation)
+ private val testApp = ImeAppAutoFocusHelper(instrumentation, testSpec.startRotation)
@FlickerBuilderProvider
fun buildFlicker(): FlickerBuilder {
diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/ime/CloseImeAutoOpenWindowToHomeTest.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/ime/CloseImeAutoOpenWindowToHomeTest.kt
index 0582685..5315da1d 100644
--- a/tests/FlickerTests/src/com/android/server/wm/flicker/ime/CloseImeAutoOpenWindowToHomeTest.kt
+++ b/tests/FlickerTests/src/com/android/server/wm/flicker/ime/CloseImeAutoOpenWindowToHomeTest.kt
@@ -34,7 +34,6 @@
import com.android.server.wm.flicker.navBarLayerRotatesAndScales
import com.android.server.wm.flicker.navBarWindowIsVisible
import com.android.server.wm.flicker.entireScreenCovered
-import com.android.server.wm.flicker.startRotation
import com.android.server.wm.flicker.statusBarLayerIsVisible
import com.android.server.wm.flicker.statusBarLayerRotatesScales
import com.android.server.wm.flicker.statusBarWindowIsVisible
@@ -64,7 +63,7 @@
@Group2
class CloseImeAutoOpenWindowToHomeTest(private val testSpec: FlickerTestParameter) {
private val instrumentation: Instrumentation = InstrumentationRegistry.getInstrumentation()
- private val testApp = ImeAppAutoFocusHelper(instrumentation, testSpec.config.startRotation)
+ private val testApp = ImeAppAutoFocusHelper(instrumentation, testSpec.startRotation)
@FlickerBuilderProvider
fun buildFlicker(): FlickerBuilder {
diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/ime/CloseImeWindowToAppTest.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/ime/CloseImeWindowToAppTest.kt
index 91b3d3d..d063b69 100644
--- a/tests/FlickerTests/src/com/android/server/wm/flicker/ime/CloseImeWindowToAppTest.kt
+++ b/tests/FlickerTests/src/com/android/server/wm/flicker/ime/CloseImeWindowToAppTest.kt
@@ -119,14 +119,14 @@
@Presubmit
@Test
fun navBarLayerRotatesAndScales() {
- Assume.assumeFalse(testSpec.isRotated)
+ Assume.assumeFalse(testSpec.isLandscapeOrSeascapeAtStart)
testSpec.navBarLayerRotatesAndScales()
}
@FlakyTest
@Test
fun navBarLayerRotatesAndScales_Flaky() {
- Assume.assumeTrue(testSpec.isRotated)
+ Assume.assumeTrue(testSpec.isLandscapeOrSeascapeAtStart)
testSpec.navBarLayerRotatesAndScales()
}
diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/ime/LaunchAppShowImeOnStartTest.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/ime/LaunchAppShowImeOnStartTest.kt
index a9568b3..005c4f5 100644
--- a/tests/FlickerTests/src/com/android/server/wm/flicker/ime/LaunchAppShowImeOnStartTest.kt
+++ b/tests/FlickerTests/src/com/android/server/wm/flicker/ime/LaunchAppShowImeOnStartTest.kt
@@ -29,7 +29,6 @@
import com.android.server.wm.flicker.dsl.FlickerBuilder
import com.android.server.wm.flicker.helpers.ImeAppAutoFocusHelper
import com.android.server.wm.flicker.helpers.setRotation
-import com.android.server.wm.flicker.startRotation
import com.android.server.wm.traces.common.FlickerComponentName
import org.junit.FixMethodOrder
import org.junit.Test
@@ -71,14 +70,14 @@
@FixMethodOrder(MethodSorters.NAME_ASCENDING)
class LaunchAppShowImeOnStartTest(private val testSpec: FlickerTestParameter) {
private val instrumentation: Instrumentation = InstrumentationRegistry.getInstrumentation()
- private val testApp = ImeAppAutoFocusHelper(instrumentation, testSpec.config.startRotation)
+ private val testApp = ImeAppAutoFocusHelper(instrumentation, testSpec.startRotation)
@FlickerBuilderProvider
fun buildFlicker(): FlickerBuilder {
return FlickerBuilder(instrumentation).apply {
setup {
eachRun {
- this.setRotation(testSpec.config.startRotation)
+ this.setRotation(testSpec.startRotation)
}
}
teardown {
diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/ime/ReOpenImeWindowTest.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/ime/ReOpenImeWindowTest.kt
index f6febe9e..5d8a382 100644
--- a/tests/FlickerTests/src/com/android/server/wm/flicker/ime/ReOpenImeWindowTest.kt
+++ b/tests/FlickerTests/src/com/android/server/wm/flicker/ime/ReOpenImeWindowTest.kt
@@ -37,7 +37,6 @@
import com.android.server.wm.flicker.navBarWindowIsVisible
import com.android.server.wm.flicker.dsl.FlickerBuilder
import com.android.server.wm.flicker.entireScreenCovered
-import com.android.server.wm.flicker.startRotation
import com.android.server.wm.flicker.statusBarLayerIsVisible
import com.android.server.wm.flicker.statusBarLayerRotatesScales
import com.android.server.wm.flicker.statusBarWindowIsVisible
@@ -60,7 +59,7 @@
@Group2
class ReOpenImeWindowTest(private val testSpec: FlickerTestParameter) {
private val instrumentation: Instrumentation = InstrumentationRegistry.getInstrumentation()
- private val testApp = ImeAppAutoFocusHelper(instrumentation, testSpec.config.startRotation)
+ private val testApp = ImeAppAutoFocusHelper(instrumentation, testSpec.startRotation)
private val isShellTransitionsEnabled =
SystemProperties.getBoolean("persist.debug.shell_transit", false)
@@ -76,7 +75,7 @@
device.pressRecentApps()
wmHelper.waitImeGone()
wmHelper.waitForAppTransitionIdle()
- this.setRotation(testSpec.config.startRotation)
+ this.setRotation(testSpec.startRotation)
}
}
transitions {
diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/ime/SwitchImeWindowsFromGestureNavTest.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/ime/SwitchImeWindowsFromGestureNavTest.kt
index 4c506b0..56ec80c 100644
--- a/tests/FlickerTests/src/com/android/server/wm/flicker/ime/SwitchImeWindowsFromGestureNavTest.kt
+++ b/tests/FlickerTests/src/com/android/server/wm/flicker/ime/SwitchImeWindowsFromGestureNavTest.kt
@@ -33,7 +33,6 @@
import com.android.server.wm.flicker.helpers.WindowUtils
import com.android.server.wm.flicker.helpers.setRotation
import com.android.server.wm.flicker.navBarWindowIsVisible
-import com.android.server.wm.flicker.startRotation
import com.android.server.wm.flicker.statusBarWindowIsVisible
import com.android.server.wm.traces.common.FlickerComponentName
@@ -56,14 +55,14 @@
class SwitchImeWindowsFromGestureNavTest(private val testSpec: FlickerTestParameter) {
private val instrumentation: Instrumentation = InstrumentationRegistry.getInstrumentation()
private val testApp = SimpleAppHelper(instrumentation)
- private val imeTestApp = ImeAppAutoFocusHelper(instrumentation, testSpec.config.startRotation)
+ private val imeTestApp = ImeAppAutoFocusHelper(instrumentation, testSpec.startRotation)
@FlickerBuilderProvider
fun buildFlicker(): FlickerBuilder {
return FlickerBuilder(instrumentation).apply {
setup {
eachRun {
- this.setRotation(testSpec.config.startRotation)
+ this.setRotation(testSpec.startRotation)
testApp.launchViaIntent(wmHelper)
wmHelper.waitForFullScreenApp(testApp.component)
wmHelper.waitForAppTransitionIdle()
@@ -86,7 +85,7 @@
transitions {
// [Step1]: Swipe right from imeTestApp to testApp task
createTag(TAG_IME_VISIBLE)
- val displayBounds = WindowUtils.getDisplayBounds(testSpec.config.startRotation)
+ val displayBounds = WindowUtils.getDisplayBounds(testSpec.startRotation)
device.swipe(0, displayBounds.bounds.height(),
displayBounds.bounds.width(), displayBounds.bounds.height(), 50)
@@ -96,7 +95,7 @@
}
transitions {
// [Step2]: Swipe left to back to imeTestApp task
- val displayBounds = WindowUtils.getDisplayBounds(testSpec.config.startRotation)
+ val displayBounds = WindowUtils.getDisplayBounds(testSpec.startRotation)
device.swipe(displayBounds.bounds.width(), displayBounds.bounds.height(),
0, displayBounds.bounds.height(), 50)
wmHelper.waitForFullScreenApp(imeTestApp.component)
diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/launch/ActivitiesTransitionTest.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/launch/ActivitiesTransitionTest.kt
index f74a771..648353e 100644
--- a/tests/FlickerTests/src/com/android/server/wm/flicker/launch/ActivitiesTransitionTest.kt
+++ b/tests/FlickerTests/src/com/android/server/wm/flicker/launch/ActivitiesTransitionTest.kt
@@ -26,7 +26,6 @@
import com.android.server.wm.flicker.FlickerTestParameter
import com.android.server.wm.flicker.FlickerTestParameterFactory
import com.android.server.wm.flicker.LAUNCHER_COMPONENT
-import com.android.server.wm.flicker.repetitions
import com.android.server.wm.flicker.annotation.Group4
import com.android.server.wm.flicker.dsl.FlickerBuilder
import com.android.server.wm.flicker.helpers.TwoActivitiesAppHelper
@@ -60,7 +59,7 @@
@FixMethodOrder(MethodSorters.NAME_ASCENDING)
@Group4
class ActivitiesTransitionTest(val testSpec: FlickerTestParameter) {
- val instrumentation: Instrumentation = InstrumentationRegistry.getInstrumentation()
+ private val instrumentation: Instrumentation = InstrumentationRegistry.getInstrumentation()
private val testApp: TwoActivitiesAppHelper = TwoActivitiesAppHelper(instrumentation)
/**
@@ -70,8 +69,6 @@
@FlickerBuilderProvider
fun buildFlicker(): FlickerBuilder {
return FlickerBuilder(instrumentation).apply {
- withTestName { testSpec.name }
- repeat { testSpec.config.repetitions }
setup {
eachRun {
testApp.launchViaIntent(wmHelper)
diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppColdTest.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppColdTest.kt
index 2f5a389..c4fec7f 100644
--- a/tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppColdTest.kt
+++ b/tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppColdTest.kt
@@ -24,7 +24,6 @@
import com.android.server.wm.flicker.FlickerTestParameterFactory
import com.android.server.wm.flicker.annotation.Group1
import com.android.server.wm.flicker.helpers.setRotation
-import com.android.server.wm.flicker.startRotation
import com.android.server.wm.flicker.dsl.FlickerBuilder
import com.android.server.wm.flicker.rules.RemoveAllTasksButHomeRule.Companion.removeAllTasksButHome
import org.junit.FixMethodOrder
@@ -59,13 +58,13 @@
/**
* Defines the transition used to run the test
*/
- override val transition: FlickerBuilder.(Map<String, Any?>) -> Unit
+ override val transition: FlickerBuilder.() -> Unit
get() = {
- super.transition(this, it)
+ super.transition(this)
setup {
eachRun {
removeAllTasksButHome()
- this.setRotation(testSpec.config.startRotation)
+ this.setRotation(testSpec.startRotation)
}
}
teardown {
diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppFromOverviewTest.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppFromOverviewTest.kt
index a05b78c..c572e8b 100644
--- a/tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppFromOverviewTest.kt
+++ b/tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppFromOverviewTest.kt
@@ -27,7 +27,6 @@
import com.android.server.wm.flicker.annotation.Group1
import com.android.server.wm.flicker.helpers.reopenAppFromOverview
import com.android.server.wm.flicker.helpers.setRotation
-import com.android.server.wm.flicker.startRotation
import com.android.server.wm.flicker.dsl.FlickerBuilder
import com.android.server.wm.traces.common.WindowManagerConditionsFactory
import org.junit.FixMethodOrder
@@ -64,9 +63,9 @@
/**
* Defines the transition used to run the test
*/
- override val transition: FlickerBuilder.(Map<String, Any?>) -> Unit
+ override val transition: FlickerBuilder.() -> Unit
get() = {
- super.transition(this, it)
+ super.transition(this)
setup {
test {
testApp.launchViaIntent(wmHelper)
@@ -81,7 +80,7 @@
WindowManagerConditionsFactory.isActivityVisible(LAUNCHER_COMPONENT),
WindowManagerConditionsFactory.hasLayersAnimating().negate()
)
- this.setRotation(testSpec.config.startRotation)
+ this.setRotation(testSpec.startRotation)
}
}
transitions {
diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppNonResizeableTest.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppNonResizeableTest.kt
index ad0da5b..2c84305 100644
--- a/tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppNonResizeableTest.kt
+++ b/tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppNonResizeableTest.kt
@@ -66,9 +66,9 @@
/**
* Defines the transition used to run the test
*/
- override val transition: FlickerBuilder.(Map<String, Any?>) -> Unit
- get() = { args ->
- super.transition(this, args)
+ override val transition: FlickerBuilder.() -> Unit
+ get() = {
+ super.transition(this)
setup {
eachRun {
device.sleep()
diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppTransition.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppTransition.kt
index 7af7b3a..b104b97 100644
--- a/tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppTransition.kt
+++ b/tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppTransition.kt
@@ -31,9 +31,7 @@
import com.android.server.wm.flicker.navBarLayerIsVisible
import com.android.server.wm.flicker.navBarLayerRotatesAndScales
import com.android.server.wm.flicker.navBarWindowIsVisible
-import com.android.server.wm.flicker.repetitions
import com.android.server.wm.flicker.replacesLayer
-import com.android.server.wm.flicker.startRotation
import com.android.server.wm.flicker.statusBarLayerIsVisible
import com.android.server.wm.flicker.statusBarLayerRotatesScales
import com.android.server.wm.flicker.statusBarWindowIsVisible
@@ -50,13 +48,11 @@
/**
* Defines the transition used to run the test
*/
- protected open val transition: FlickerBuilder.(Map<String, Any?>) -> Unit = {
- withTestName { testSpec.name }
- repeat { testSpec.config.repetitions }
+ protected open val transition: FlickerBuilder.() -> Unit = {
setup {
test {
device.wakeUpAndGoToHomeScreen()
- this.setRotation(testSpec.config.startRotation)
+ this.setRotation(testSpec.startRotation)
}
}
teardown {
@@ -73,7 +69,7 @@
@FlickerBuilderProvider
fun buildFlicker(): FlickerBuilder {
return FlickerBuilder(instrumentation).apply {
- transition(testSpec.config)
+ transition()
}
}
diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppWarmTest.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppWarmTest.kt
index 5edee0c..dc7df34 100644
--- a/tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppWarmTest.kt
+++ b/tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppWarmTest.kt
@@ -24,7 +24,6 @@
import com.android.server.wm.flicker.FlickerTestParameterFactory
import com.android.server.wm.flicker.annotation.Group1
import com.android.server.wm.flicker.helpers.setRotation
-import com.android.server.wm.flicker.startRotation
import com.android.server.wm.flicker.dsl.FlickerBuilder
import org.junit.FixMethodOrder
import org.junit.Test
@@ -59,9 +58,9 @@
/**
* Defines the transition used to run the test
*/
- override val transition: FlickerBuilder.(Map<String, Any?>) -> Unit
+ override val transition: FlickerBuilder.() -> Unit
get() = {
- super.transition(this, it)
+ super.transition(this)
setup {
test {
testApp.launchViaIntent(wmHelper)
@@ -69,7 +68,7 @@
eachRun {
device.pressHome()
wmHelper.waitForHomeActivityVisible()
- this.setRotation(testSpec.config.startRotation)
+ this.setRotation(testSpec.startRotation)
}
}
teardown {
diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/launch/TaskTransitionTest.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/launch/TaskTransitionTest.kt
index 495e2d6..769cb1a 100644
--- a/tests/FlickerTests/src/com/android/server/wm/flicker/launch/TaskTransitionTest.kt
+++ b/tests/FlickerTests/src/com/android/server/wm/flicker/launch/TaskTransitionTest.kt
@@ -33,8 +33,6 @@
import com.android.server.wm.flicker.helpers.WindowUtils
import com.android.server.wm.flicker.navBarLayerIsVisible
import com.android.server.wm.flicker.navBarWindowIsVisible
-import com.android.server.wm.flicker.repetitions
-import com.android.server.wm.flicker.startRotation
import com.android.server.wm.flicker.statusBarLayerIsVisible
import com.android.server.wm.flicker.statusBarWindowIsVisible
import com.android.server.wm.flicker.testapp.ActivityOptions.LAUNCH_NEW_TASK_ACTIVITY_COMPONENT_NAME
@@ -71,8 +69,6 @@
@FlickerBuilderProvider
fun buildFlicker(): FlickerBuilder {
return FlickerBuilder(instrumentation).apply {
- withTestName { testSpec.name }
- repeat { testSpec.config.repetitions }
setup {
eachRun {
mTestApp.launchViaIntent(wmHelper)
@@ -149,7 +145,7 @@
@Test
fun colorLayerIsVisibleDuringTransition() {
val bgColorLayer = FlickerComponentName("", "colorBackgroundLayer")
- val displayBounds = WindowUtils.getDisplayBounds(testSpec.config.startRotation)
+ val displayBounds = WindowUtils.getDisplayBounds(testSpec.startRotation)
testSpec.assertLayers {
this.coversExactly(displayBounds, LAUNCH_NEW_TASK_ACTIVITY)
diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/quickswitch/QuickSwitchBetweenTwoAppsBackTest.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/quickswitch/QuickSwitchBetweenTwoAppsBackTest.kt
index 52904cc..5b0372d 100644
--- a/tests/FlickerTests/src/com/android/server/wm/flicker/quickswitch/QuickSwitchBetweenTwoAppsBackTest.kt
+++ b/tests/FlickerTests/src/com/android/server/wm/flicker/quickswitch/QuickSwitchBetweenTwoAppsBackTest.kt
@@ -32,11 +32,9 @@
import com.android.server.wm.flicker.helpers.NonResizeableAppHelper
import com.android.server.wm.flicker.helpers.SimpleAppHelper
import com.android.server.wm.flicker.helpers.WindowUtils
-import com.android.server.wm.flicker.helpers.isRotated
import com.android.server.wm.flicker.navBarLayerIsVisible
import com.android.server.wm.flicker.navBarLayerRotatesAndScales
import com.android.server.wm.flicker.navBarWindowIsVisible
-import com.android.server.wm.flicker.startRotation
import com.android.server.wm.flicker.statusBarLayerIsVisible
import com.android.server.wm.flicker.statusBarWindowIsVisible
import com.android.server.wm.traces.common.FlickerComponentName
@@ -68,7 +66,7 @@
private val testApp1 = SimpleAppHelper(instrumentation)
private val testApp2 = NonResizeableAppHelper(instrumentation)
- private val startDisplayBounds = WindowUtils.getDisplayBounds(testSpec.config.startRotation)
+ private val startDisplayBounds = WindowUtils.getDisplayBounds(testSpec.startRotation)
@FlickerBuilderProvider
fun buildFlicker(): FlickerBuilder {
@@ -92,7 +90,7 @@
startDisplayBounds.bounds.bottom,
2 * startDisplayBounds.bounds.right / 3,
startDisplayBounds.bounds.bottom,
- if (testSpec.config.startRotation.isRotated()) 75 else 30
+ if (testSpec.isLandscapeOrSeascapeAtStart) 75 else 30
)
wmHelper.waitForFullScreenApp(testApp1.component)
diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/quickswitch/QuickSwitchBetweenTwoAppsForwardTest.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/quickswitch/QuickSwitchBetweenTwoAppsForwardTest.kt
index 842aa2b..99bc115 100644
--- a/tests/FlickerTests/src/com/android/server/wm/flicker/quickswitch/QuickSwitchBetweenTwoAppsForwardTest.kt
+++ b/tests/FlickerTests/src/com/android/server/wm/flicker/quickswitch/QuickSwitchBetweenTwoAppsForwardTest.kt
@@ -32,12 +32,9 @@
import com.android.server.wm.flicker.helpers.NonResizeableAppHelper
import com.android.server.wm.flicker.helpers.SimpleAppHelper
import com.android.server.wm.flicker.helpers.WindowUtils
-import com.android.server.wm.flicker.helpers.isRotated
import com.android.server.wm.flicker.navBarLayerIsVisible
import com.android.server.wm.flicker.navBarLayerRotatesAndScales
import com.android.server.wm.flicker.navBarWindowIsVisible
-import com.android.server.wm.flicker.repetitions
-import com.android.server.wm.flicker.startRotation
import com.android.server.wm.flicker.statusBarLayerIsVisible
import com.android.server.wm.flicker.statusBarWindowIsVisible
import com.android.server.wm.traces.common.FlickerComponentName
@@ -69,13 +66,11 @@
private val testApp1 = SimpleAppHelper(instrumentation)
private val testApp2 = NonResizeableAppHelper(instrumentation)
- private val startDisplayBounds = WindowUtils.getDisplayBounds(testSpec.config.startRotation)
+ private val startDisplayBounds = WindowUtils.getDisplayBounds(testSpec.startRotation)
@FlickerBuilderProvider
fun buildFlicker(): FlickerBuilder {
return FlickerBuilder(instrumentation).apply {
- withTestName { testSpec.name }
- repeat { testSpec.config.repetitions }
setup {
eachRun {
testApp1.launchViaIntent(wmHelper)
@@ -93,7 +88,7 @@
startDisplayBounds.bounds.bottom,
2 * startDisplayBounds.bounds.right / 3,
startDisplayBounds.bounds.bottom,
- if (testSpec.config.startRotation.isRotated()) 75 else 30
+ if (testSpec.isLandscapeOrSeascapeAtStart) 75 else 30
)
wmHelper.waitForFullScreenApp(testApp1.component)
@@ -110,7 +105,7 @@
startDisplayBounds.bounds.bottom,
startDisplayBounds.bounds.right / 3,
startDisplayBounds.bounds.bottom,
- if (testSpec.config.startRotation.isRotated()) 75 else 30
+ if (testSpec.isLandscapeOrSeascapeAtStart) 75 else 30
)
wmHelper.waitForFullScreenApp(testApp2.component)
diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/quickswitch/QuickSwitchFromLauncherTest.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/quickswitch/QuickSwitchFromLauncherTest.kt
index 10ca0d9..dcb5c86 100644
--- a/tests/FlickerTests/src/com/android/server/wm/flicker/quickswitch/QuickSwitchFromLauncherTest.kt
+++ b/tests/FlickerTests/src/com/android/server/wm/flicker/quickswitch/QuickSwitchFromLauncherTest.kt
@@ -35,7 +35,6 @@
import com.android.server.wm.flicker.navBarLayerIsVisible
import com.android.server.wm.flicker.navBarLayerRotatesAndScales
import com.android.server.wm.flicker.navBarWindowIsVisible
-import com.android.server.wm.flicker.startRotation
import com.android.server.wm.flicker.statusBarLayerIsVisible
import com.android.server.wm.flicker.statusBarWindowIsVisible
import com.android.server.wm.traces.common.FlickerComponentName
@@ -64,7 +63,7 @@
class QuickSwitchFromLauncherTest(private val testSpec: FlickerTestParameter) {
private val instrumentation: Instrumentation = InstrumentationRegistry.getInstrumentation()
private val testApp = SimpleAppHelper(instrumentation)
- private val startDisplayBounds = WindowUtils.getDisplayBounds(testSpec.config.startRotation)
+ private val startDisplayBounds = WindowUtils.getDisplayBounds(testSpec.startRotation)
@FlickerBuilderProvider
fun buildFlicker(): FlickerBuilder {
diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/rotation/ChangeAppRotationTest.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/rotation/ChangeAppRotationTest.kt
index 0bde8a0..eb7d29d 100644
--- a/tests/FlickerTests/src/com/android/server/wm/flicker/rotation/ChangeAppRotationTest.kt
+++ b/tests/FlickerTests/src/com/android/server/wm/flicker/rotation/ChangeAppRotationTest.kt
@@ -85,9 +85,9 @@
val flickerRule = WMFlickerServiceRuleForTestSpec(testSpec)
override val testApp = SimpleAppHelper(instrumentation)
- override val transition: FlickerBuilder.(Map<String, Any?>) -> Unit
+ override val transition: FlickerBuilder.() -> Unit
get() = {
- super.transition(this, it)
+ super.transition(this)
setup {
test {
testApp.launchViaIntent(wmHelper)
diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/rotation/RotationTransition.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/rotation/RotationTransition.kt
index e850632..ce2347d 100644
--- a/tests/FlickerTests/src/com/android/server/wm/flicker/rotation/RotationTransition.kt
+++ b/tests/FlickerTests/src/com/android/server/wm/flicker/rotation/RotationTransition.kt
@@ -22,14 +22,12 @@
import com.android.server.wm.flicker.FlickerBuilderProvider
import com.android.server.wm.flicker.FlickerTestParameter
import com.android.server.wm.flicker.dsl.FlickerBuilder
-import com.android.server.wm.flicker.endRotation
import com.android.server.wm.flicker.helpers.StandardAppHelper
import com.android.server.wm.flicker.helpers.setRotation
import com.android.server.wm.flicker.navBarLayerIsVisible
import com.android.server.wm.flicker.navBarLayerRotatesAndScales
import com.android.server.wm.flicker.navBarWindowIsVisible
import com.android.server.wm.flicker.entireScreenCovered
-import com.android.server.wm.flicker.startRotation
import com.android.server.wm.traces.common.FlickerComponentName
import org.junit.Test
@@ -41,10 +39,10 @@
protected val instrumentation: Instrumentation = InstrumentationRegistry.getInstrumentation()
- protected open val transition: FlickerBuilder.(Map<String, Any?>) -> Unit = {
+ protected open val transition: FlickerBuilder.() -> Unit = {
setup {
eachRun {
- this.setRotation(testSpec.config.startRotation)
+ this.setRotation(testSpec.startRotation)
}
}
teardown {
@@ -53,7 +51,7 @@
}
}
transitions {
- this.setRotation(testSpec.config.endRotation)
+ this.setRotation(testSpec.endRotation)
}
}
@@ -64,7 +62,7 @@
@FlickerBuilderProvider
fun buildFlicker(): FlickerBuilder {
return FlickerBuilder(instrumentation).apply {
- transition(testSpec.config)
+ transition()
}
}
diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/rotation/SeamlessAppRotationTest.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/rotation/SeamlessAppRotationTest.kt
index 310f04b..c55d7af 100644
--- a/tests/FlickerTests/src/com/android/server/wm/flicker/rotation/SeamlessAppRotationTest.kt
+++ b/tests/FlickerTests/src/com/android/server/wm/flicker/rotation/SeamlessAppRotationTest.kt
@@ -81,14 +81,14 @@
) : RotationTransition(testSpec) {
override val testApp = SeamlessRotationAppHelper(instrumentation)
- override val transition: FlickerBuilder.(Map<String, Any?>) -> Unit
+ override val transition: FlickerBuilder.() -> Unit
get() = {
- super.transition(this, it)
+ super.transition(this)
setup {
test {
testApp.launchViaIntent(wmHelper,
- stringExtras = mapOf(
- ActivityOptions.EXTRA_STARVE_UI_THREAD to it.starveUiThread.toString())
+ stringExtras = mapOf(ActivityOptions.EXTRA_STARVE_UI_THREAD
+ to testSpec.starveUiThread.toString())
)
}
}
@@ -185,15 +185,17 @@
override fun navBarLayerRotatesAndScales() = super.navBarLayerRotatesAndScales()
companion object {
- private val Map<String, Any?>.starveUiThread
- get() = this.getOrDefault(ActivityOptions.EXTRA_STARVE_UI_THREAD, false) as Boolean
+ private val FlickerTestParameter.starveUiThread
+ get() = config.getOrDefault(ActivityOptions.EXTRA_STARVE_UI_THREAD, false) as Boolean
- private fun FlickerTestParameter.createConfig(
+ private fun createConfig(
+ sourceConfig: FlickerTestParameter,
starveUiThread: Boolean
- ): MutableMap<String, Any?> {
- val config = this.config.toMutableMap()
- config[ActivityOptions.EXTRA_STARVE_UI_THREAD] = starveUiThread
- return config
+ ): FlickerTestParameter {
+ val newConfig = sourceConfig.config.toMutableMap()
+ .also { it[ActivityOptions.EXTRA_STARVE_UI_THREAD] = starveUiThread }
+ val nameExt = if (starveUiThread) "_BUSY_UI_THREAD" else ""
+ return FlickerTestParameter(newConfig, nameOverride = "$sourceConfig$nameExt")
}
/**
@@ -206,15 +208,10 @@
private fun getConfigurations(): List<FlickerTestParameter> {
return FlickerTestParameterFactory.getInstance()
.getConfigRotationTests(repetitions = 2)
- .flatMap {
- val defaultRun = it.createConfig(starveUiThread = false)
- val busyUiRun = it.createConfig(starveUiThread = true)
- listOf(
- FlickerTestParameter(defaultRun),
- FlickerTestParameter(busyUiRun,
- name = "${FlickerTestParameter.defaultName(busyUiRun)}_BUSY_UI_THREAD"
- )
- )
+ .flatMap { sourceConfig ->
+ val defaultRun = createConfig(sourceConfig, starveUiThread = false)
+ val busyUiRun = createConfig(sourceConfig, starveUiThread = true)
+ listOf(defaultRun, busyUiRun)
}
}
diff --git a/tests/vcn/java/com/android/server/vcn/VcnGatewayConnectionTest.java b/tests/vcn/java/com/android/server/vcn/VcnGatewayConnectionTest.java
index 5253c3e..2b0037e 100644
--- a/tests/vcn/java/com/android/server/vcn/VcnGatewayConnectionTest.java
+++ b/tests/vcn/java/com/android/server/vcn/VcnGatewayConnectionTest.java
@@ -16,6 +16,7 @@
package com.android.server.vcn;
+import static android.net.IpSecManager.IpSecTunnelInterface;
import static android.net.NetworkCapabilities.NET_CAPABILITY_DUN;
import static android.net.NetworkCapabilities.NET_CAPABILITY_INTERNET;
import static android.net.NetworkCapabilities.NET_CAPABILITY_NOT_METERED;
@@ -24,6 +25,8 @@
import static android.net.NetworkCapabilities.TRANSPORT_CELLULAR;
import static android.net.NetworkCapabilities.TRANSPORT_WIFI;
+import static com.android.server.vcn.VcnGatewayConnection.DUMMY_ADDR;
+import static com.android.server.vcn.VcnGatewayConnection.VcnChildSessionConfiguration;
import static com.android.server.vcn.VcnGatewayConnection.VcnIkeSession;
import static com.android.server.vcn.VcnGatewayConnection.VcnNetworkAgent;
@@ -36,8 +39,11 @@
import static org.mockito.Mockito.CALLS_REAL_METHODS;
import static org.mockito.Mockito.doReturn;
import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.times;
import static org.mockito.Mockito.verify;
+import android.net.IpSecManager;
+import android.net.LinkAddress;
import android.net.LinkProperties;
import android.net.Network;
import android.net.NetworkCapabilities;
@@ -59,6 +65,8 @@
import org.junit.Test;
import org.junit.runner.RunWith;
+import java.net.InetAddress;
+import java.util.Arrays;
import java.util.Collections;
import java.util.HashMap;
import java.util.List;
@@ -71,6 +79,8 @@
public class VcnGatewayConnectionTest extends VcnGatewayConnectionTestBase {
private static final int TEST_UID = Process.myUid() + 1;
+ private static final String LOOPBACK_IFACE = "lo";
+
private static final ParcelUuid TEST_PARCEL_UUID = new ParcelUuid(UUID.randomUUID());
private static final int TEST_SIM_SLOT_INDEX = 1;
private static final int TEST_SUBSCRIPTION_ID_1 = 2;
@@ -78,6 +88,12 @@
private static final int TEST_SUBSCRIPTION_ID_2 = 3;
private static final SubscriptionInfo TEST_SUBINFO_2 = mock(SubscriptionInfo.class);
private static final Map<Integer, ParcelUuid> TEST_SUBID_TO_GROUP_MAP;
+ private static final String TEST_TCP_BUFFER_SIZES = "1,2,3,4,5,6";
+ private static final int TEST_MTU = 1300;
+ private static final int TEST_MTU_DELTA = 64;
+ private static final List<LinkAddress> TEST_INTERNAL_ADDRESSES =
+ Arrays.asList(new LinkAddress(DUMMY_ADDR, 16));
+ private static final List<InetAddress> TEST_DNS_ADDRESSES = Arrays.asList(DUMMY_ADDR);
private static final int TEST_UPSTREAM_BANDWIDTH = 1234;
private static final int TEST_DOWNSTREAM_BANDWIDTH = 2345;
@@ -169,6 +185,59 @@
}
@Test
+ public void testBuildLinkProperties() throws Exception {
+ final IpSecTunnelInterface tunnelIface =
+ mContext.getSystemService(IpSecManager.class)
+ .createIpSecTunnelInterface(
+ DUMMY_ADDR, DUMMY_ADDR, TEST_UNDERLYING_NETWORK_RECORD_1.network);
+
+ final LinkProperties underlyingLp = new LinkProperties();
+ underlyingLp.setInterfaceName(LOOPBACK_IFACE);
+ underlyingLp.setTcpBufferSizes(TEST_TCP_BUFFER_SIZES);
+ doReturn(TEST_MTU).when(mDeps).getUnderlyingIfaceMtu(LOOPBACK_IFACE);
+
+ final VcnChildSessionConfiguration childSessionConfig =
+ mock(VcnChildSessionConfiguration.class);
+ doReturn(TEST_INTERNAL_ADDRESSES).when(childSessionConfig).getInternalAddresses();
+ doReturn(TEST_DNS_ADDRESSES).when(childSessionConfig).getInternalDnsServers();
+
+ UnderlyingNetworkRecord record =
+ new UnderlyingNetworkRecord(
+ mock(Network.class, CALLS_REAL_METHODS),
+ new NetworkCapabilities.Builder().build(),
+ underlyingLp,
+ false);
+
+ final LinkProperties vcnLp1 =
+ mGatewayConnection.buildConnectedLinkProperties(
+ VcnGatewayConnectionConfigTest.buildTestConfig(),
+ tunnelIface,
+ childSessionConfig,
+ record);
+
+ verify(mDeps).getUnderlyingIfaceMtu(LOOPBACK_IFACE);
+
+ // Instead of having to recalculate the final MTU (after accounting for IPsec overhead),
+ // calculate another set of Link Properties with a lower MTU, and calculate the delta.
+ doReturn(TEST_MTU - TEST_MTU_DELTA).when(mDeps).getUnderlyingIfaceMtu(LOOPBACK_IFACE);
+
+ final LinkProperties vcnLp2 =
+ mGatewayConnection.buildConnectedLinkProperties(
+ VcnGatewayConnectionConfigTest.buildTestConfig(),
+ tunnelIface,
+ childSessionConfig,
+ record);
+
+ verify(mDeps, times(2)).getUnderlyingIfaceMtu(LOOPBACK_IFACE);
+
+ assertEquals(tunnelIface.getInterfaceName(), vcnLp1.getInterfaceName());
+ assertEquals(TEST_INTERNAL_ADDRESSES, vcnLp1.getLinkAddresses());
+ assertEquals(TEST_DNS_ADDRESSES, vcnLp1.getDnsServers());
+ assertEquals(TEST_TCP_BUFFER_SIZES, vcnLp1.getTcpBufferSizes());
+ assertEquals(TEST_MTU_DELTA, vcnLp1.getMtu() - vcnLp2.getMtu());
+ }
+
+ @Test
public void testSubscriptionSnapshotUpdateNotifiesUnderlyingNetworkTracker() {
verifyWakeLockSetUp();
diff --git a/tools/aapt2/OWNERS b/tools/aapt2/OWNERS
index 69dfcc9..4f655e5 100644
--- a/tools/aapt2/OWNERS
+++ b/tools/aapt2/OWNERS
@@ -1,4 +1,4 @@
set noparent
toddke@google.com
-rtmitchell@google.com
+zyy@google.com
patb@google.com
\ No newline at end of file