Merge "Clear mocks in tests"
diff --git a/apct-tests/perftests/utils/src/android/perftests/utils/TestPackageInstaller.java b/apct-tests/perftests/utils/src/android/perftests/utils/TestPackageInstaller.java
index 530dc9d..15a65ce 100644
--- a/apct-tests/perftests/utils/src/android/perftests/utils/TestPackageInstaller.java
+++ b/apct-tests/perftests/utils/src/android/perftests/utils/TestPackageInstaller.java
@@ -130,7 +130,8 @@
IntentSender getIntentSender(int sessionId) {
String action = BROADCAST_ACTION + "." + sessionId;
IntentFilter filter = new IntentFilter(action);
- mContext.registerReceiver(this, filter);
+ mContext.registerReceiver(this, filter,
+ Context.RECEIVER_EXPORTED_UNAUDITED);
Intent intent = new Intent(action);
PendingIntent pending = PendingIntent.getBroadcast(mContext, sessionId, intent,
diff --git a/apex/jobscheduler/framework/java/android/app/job/JobService.java b/apex/jobscheduler/framework/java/android/app/job/JobService.java
index c251529a..e5b0742 100644
--- a/apex/jobscheduler/framework/java/android/app/job/JobService.java
+++ b/apex/jobscheduler/framework/java/android/app/job/JobService.java
@@ -125,7 +125,7 @@
* will not be invoked.
*
* @param params Parameters specifying info about this job, including the optional
- * extras configured with {@link JobInfo.Builder#setExtras(android.os.PersistableBundle).
+ * extras configured with {@link JobInfo.Builder#setExtras(android.os.PersistableBundle)}.
* This object serves to identify this specific running job instance when calling
* {@link #jobFinished(JobParameters, boolean)}.
* @return {@code true} if your service will continue running, using a separate thread
diff --git a/core/api/current.txt b/core/api/current.txt
index 98d3a91..17beece 100644
--- a/core/api/current.txt
+++ b/core/api/current.txt
@@ -16525,6 +16525,7 @@
method @NonNull public static android.graphics.RenderEffect createColorFilterEffect(@NonNull android.graphics.ColorFilter);
method @NonNull public static android.graphics.RenderEffect createOffsetEffect(float, float);
method @NonNull public static android.graphics.RenderEffect createOffsetEffect(float, float, @NonNull android.graphics.RenderEffect);
+ method @NonNull public static android.graphics.RenderEffect createRuntimeShaderEffect(@NonNull android.graphics.RuntimeShader, @NonNull String);
method @NonNull public static android.graphics.RenderEffect createShaderEffect(@NonNull android.graphics.Shader);
}
@@ -20245,6 +20246,7 @@
public final class GnssMeasurementRequest implements android.os.Parcelable {
method public int describeContents();
+ method @IntRange(from=0) public int getIntervalMillis();
method public boolean isFullTracking();
method public void writeToParcel(@NonNull android.os.Parcel, int);
field @NonNull public static final android.os.Parcelable.Creator<android.location.GnssMeasurementRequest> CREATOR;
@@ -20255,6 +20257,7 @@
ctor public GnssMeasurementRequest.Builder(@NonNull android.location.GnssMeasurementRequest);
method @NonNull public android.location.GnssMeasurementRequest build();
method @NonNull public android.location.GnssMeasurementRequest.Builder setFullTracking(boolean);
+ method @NonNull public android.location.GnssMeasurementRequest.Builder setIntervalMillis(@IntRange(from=0) int);
}
public final class GnssMeasurementsEvent implements android.os.Parcelable {
@@ -35711,6 +35714,7 @@
field public static final String ACTION_APPLICATION_DETAILS_SETTINGS = "android.settings.APPLICATION_DETAILS_SETTINGS";
field public static final String ACTION_APPLICATION_DEVELOPMENT_SETTINGS = "android.settings.APPLICATION_DEVELOPMENT_SETTINGS";
field public static final String ACTION_APPLICATION_SETTINGS = "android.settings.APPLICATION_SETTINGS";
+ field public static final String ACTION_APP_LOCALE_SETTINGS = "android.settings.APP_LOCALE_SETTINGS";
field public static final String ACTION_APP_NOTIFICATION_BUBBLE_SETTINGS = "android.settings.APP_NOTIFICATION_BUBBLE_SETTINGS";
field public static final String ACTION_APP_NOTIFICATION_SETTINGS = "android.settings.APP_NOTIFICATION_SETTINGS";
field public static final String ACTION_APP_OPEN_BY_DEFAULT_SETTINGS = "android.settings.APP_OPEN_BY_DEFAULT_SETTINGS";
@@ -51384,9 +51388,9 @@
method public int getRecordCount();
method public int getWindowChanges();
method public void initFromParcel(android.os.Parcel);
- method public static android.view.accessibility.AccessibilityEvent obtain(int);
- method public static android.view.accessibility.AccessibilityEvent obtain(android.view.accessibility.AccessibilityEvent);
- method public static android.view.accessibility.AccessibilityEvent obtain();
+ method @Deprecated public static android.view.accessibility.AccessibilityEvent obtain(int);
+ method @Deprecated public static android.view.accessibility.AccessibilityEvent obtain(android.view.accessibility.AccessibilityEvent);
+ method @Deprecated public static android.view.accessibility.AccessibilityEvent obtain();
method public void setAction(int);
method public void setContentChangeTypes(int);
method public void setEventTime(long);
@@ -51574,13 +51578,13 @@
method public boolean isShowingHintText();
method public boolean isTextEntryKey();
method public boolean isVisibleToUser();
- method public static android.view.accessibility.AccessibilityNodeInfo obtain(android.view.View);
- method public static android.view.accessibility.AccessibilityNodeInfo obtain(android.view.View, int);
- method public static android.view.accessibility.AccessibilityNodeInfo obtain();
- method public static android.view.accessibility.AccessibilityNodeInfo obtain(android.view.accessibility.AccessibilityNodeInfo);
+ method @Deprecated public static android.view.accessibility.AccessibilityNodeInfo obtain(android.view.View);
+ method @Deprecated public static android.view.accessibility.AccessibilityNodeInfo obtain(android.view.View, int);
+ method @Deprecated public static android.view.accessibility.AccessibilityNodeInfo obtain();
+ method @Deprecated public static android.view.accessibility.AccessibilityNodeInfo obtain(android.view.accessibility.AccessibilityNodeInfo);
method public boolean performAction(int);
method public boolean performAction(int, android.os.Bundle);
- method public void recycle();
+ method @Deprecated public void recycle();
method public boolean refresh();
method public boolean refreshWithExtraData(String, android.os.Bundle);
method @Deprecated public void removeAction(int);
@@ -51759,8 +51763,8 @@
method public int getRowCount();
method public int getSelectionMode();
method public boolean isHierarchical();
- method public static android.view.accessibility.AccessibilityNodeInfo.CollectionInfo obtain(int, int, boolean);
- method public static android.view.accessibility.AccessibilityNodeInfo.CollectionInfo obtain(int, int, boolean, int);
+ method @Deprecated public static android.view.accessibility.AccessibilityNodeInfo.CollectionInfo obtain(int, int, boolean);
+ method @Deprecated public static android.view.accessibility.AccessibilityNodeInfo.CollectionInfo obtain(int, int, boolean, int);
field public static final int SELECTION_MODE_MULTIPLE = 2; // 0x2
field public static final int SELECTION_MODE_NONE = 0; // 0x0
field public static final int SELECTION_MODE_SINGLE = 1; // 0x1
@@ -51777,9 +51781,9 @@
method @Nullable public String getRowTitle();
method @Deprecated public boolean isHeading();
method public boolean isSelected();
- method public static android.view.accessibility.AccessibilityNodeInfo.CollectionItemInfo obtain(int, int, int, int, boolean);
- method public static android.view.accessibility.AccessibilityNodeInfo.CollectionItemInfo obtain(int, int, int, int, boolean, boolean);
- method @NonNull public static android.view.accessibility.AccessibilityNodeInfo.CollectionItemInfo obtain(@Nullable String, int, int, @Nullable String, int, int, boolean, boolean);
+ method @Deprecated public static android.view.accessibility.AccessibilityNodeInfo.CollectionItemInfo obtain(int, int, int, int, boolean);
+ method @Deprecated public static android.view.accessibility.AccessibilityNodeInfo.CollectionItemInfo obtain(int, int, int, int, boolean, boolean);
+ method @Deprecated @NonNull public static android.view.accessibility.AccessibilityNodeInfo.CollectionItemInfo obtain(@Nullable String, int, int, @Nullable String, int, int, boolean, boolean);
}
public static final class AccessibilityNodeInfo.CollectionItemInfo.Builder {
@@ -51807,7 +51811,7 @@
method public float getMax();
method public float getMin();
method public int getType();
- method public static android.view.accessibility.AccessibilityNodeInfo.RangeInfo obtain(int, float, float, float);
+ method @Deprecated public static android.view.accessibility.AccessibilityNodeInfo.RangeInfo obtain(int, float, float, float);
field public static final int RANGE_TYPE_FLOAT = 1; // 0x1
field public static final int RANGE_TYPE_INT = 0; // 0x0
field public static final int RANGE_TYPE_PERCENT = 2; // 0x2
@@ -51861,9 +51865,9 @@
method public boolean isFullScreen();
method public boolean isPassword();
method public boolean isScrollable();
- method public static android.view.accessibility.AccessibilityRecord obtain(android.view.accessibility.AccessibilityRecord);
- method public static android.view.accessibility.AccessibilityRecord obtain();
- method public void recycle();
+ method @Deprecated public static android.view.accessibility.AccessibilityRecord obtain(android.view.accessibility.AccessibilityRecord);
+ method @Deprecated public static android.view.accessibility.AccessibilityRecord obtain();
+ method @Deprecated public void recycle();
method public void setAddedCount(int);
method public void setBeforeText(CharSequence);
method public void setChecked(boolean);
diff --git a/core/api/system-current.txt b/core/api/system-current.txt
index 75c9be9..3a48d7c 100644
--- a/core/api/system-current.txt
+++ b/core/api/system-current.txt
@@ -6473,6 +6473,7 @@
method @Nullable public android.media.tv.tuner.DemuxCapabilities getDemuxCapabilities();
method @Nullable public android.media.tv.tuner.frontend.FrontendInfo getFrontendInfo();
method @Nullable public android.media.tv.tuner.frontend.FrontendStatus getFrontendStatus(@NonNull int[]);
+ method @IntRange(from=0xffffffff) public int getMaxNumberOfFrontends(int);
method @RequiresPermission("android.permission.TUNER_RESOURCE_ACCESS") public boolean hasUnusedFrontend(int);
method public boolean isLowestPriority(int);
method @Nullable @RequiresPermission(android.Manifest.permission.ACCESS_TV_DESCRAMBLER) public android.media.tv.tuner.Descrambler openDescrambler();
@@ -6485,6 +6486,7 @@
method @Nullable public android.media.tv.tuner.filter.TimeFilter openTimeFilter();
method public int scan(@NonNull android.media.tv.tuner.frontend.FrontendSettings, int, @NonNull java.util.concurrent.Executor, @NonNull android.media.tv.tuner.frontend.ScanCallback);
method public int setLnaEnabled(boolean);
+ method public int setMaxNumberOfFrontends(int, @IntRange(from=0) int);
method public void setOnTuneEventListener(@NonNull java.util.concurrent.Executor, @NonNull android.media.tv.tuner.frontend.OnTuneEventListener);
method public void setResourceLostListener(@NonNull java.util.concurrent.Executor, @NonNull android.media.tv.tuner.Tuner.OnResourceLostListener);
method public void shareFrontendFromTuner(@NonNull android.media.tv.tuner.Tuner);
diff --git a/core/api/test-current.txt b/core/api/test-current.txt
index fad681f..8ae6e4c 100644
--- a/core/api/test-current.txt
+++ b/core/api/test-current.txt
@@ -59,6 +59,7 @@
public static final class R.bool {
field public static final int config_assistantOnTopOfDream = 17891333; // 0x1110005
field public static final int config_perDisplayFocusEnabled = 17891332; // 0x1110004
+ field public static final int config_preventImeStartupUnlessTextEditor;
field public static final int config_remoteInsetsControllerControlsSystemBars = 17891334; // 0x1110006
}
@@ -2086,6 +2087,7 @@
field public static final String NAMESPACE_CONSTRAIN_DISPLAY_APIS = "constrain_display_apis";
field public static final String NAMESPACE_DEVICE_IDLE = "device_idle";
field public static final String NAMESPACE_JOB_SCHEDULER = "jobscheduler";
+ field public static final String NAMESPACE_SELECTION_TOOLBAR = "selection_toolbar";
}
public final class Settings {
@@ -2836,7 +2838,6 @@
method public void addChild(@NonNull android.os.IBinder);
method public long getSourceNodeId();
method public void setLeashedParent(@Nullable android.os.IBinder, int);
- method public static void setNumInstancesInUseCounter(java.util.concurrent.atomic.AtomicInteger);
method public void writeToParcelNoRecycle(android.os.Parcel, int);
}
diff --git a/core/java/android/app/Activity.java b/core/java/android/app/Activity.java
index 11a2ede..cf2b7ac 100644
--- a/core/java/android/app/Activity.java
+++ b/core/java/android/app/Activity.java
@@ -6191,18 +6191,20 @@
@Nullable
public Uri getReferrer() {
Intent intent = getIntent();
- try {
- Uri referrer = intent.getParcelableExtra(Intent.EXTRA_REFERRER);
- if (referrer != null) {
- return referrer;
+ if (intent != null) {
+ try {
+ Uri referrer = intent.getParcelableExtra(Intent.EXTRA_REFERRER);
+ if (referrer != null) {
+ return referrer;
+ }
+ String referrerName = intent.getStringExtra(Intent.EXTRA_REFERRER_NAME);
+ if (referrerName != null) {
+ return Uri.parse(referrerName);
+ }
+ } catch (BadParcelableException e) {
+ Log.w(TAG, "Cannot read referrer from intent;"
+ + " intent extras contain unknown custom Parcelable objects");
}
- String referrerName = intent.getStringExtra(Intent.EXTRA_REFERRER_NAME);
- if (referrerName != null) {
- return Uri.parse(referrerName);
- }
- } catch (BadParcelableException e) {
- Log.w(TAG, "Cannot read referrer from intent;"
- + " intent extras contain unknown custom Parcelable objects");
}
if (mReferrer != null) {
return new Uri.Builder().scheme("android-app").authority(mReferrer).build();
diff --git a/core/java/android/app/ActivityThread.java b/core/java/android/app/ActivityThread.java
index a8894dc..1778ea4 100644
--- a/core/java/android/app/ActivityThread.java
+++ b/core/java/android/app/ActivityThread.java
@@ -243,6 +243,7 @@
import java.util.Objects;
import java.util.TimeZone;
import java.util.concurrent.Executor;
+import java.util.concurrent.atomic.AtomicInteger;
import java.util.function.Consumer;
/**
@@ -344,11 +345,9 @@
*/
@UnsupportedAppUsage
final ArrayMap<IBinder, ActivityClientRecord> mActivities = new ArrayMap<>();
- /**
- * Maps from activity token to local record of the activities that are preparing to be launched.
- */
- final Map<IBinder, ActivityClientRecord> mLaunchingActivities =
- Collections.synchronizedMap(new ArrayMap<IBinder, ActivityClientRecord>());
+ /** Maps from activity token to the pending override configuration. */
+ @GuardedBy("mPendingOverrideConfigs")
+ private final ArrayMap<IBinder, Configuration> mPendingOverrideConfigs = new ArrayMap<>();
/** The activities to be truly destroyed (not include relaunch). */
final Map<IBinder, ClientTransactionItem> mActivitiesToBeDestroyed =
Collections.synchronizedMap(new ArrayMap<IBinder, ClientTransactionItem>());
@@ -358,6 +357,7 @@
// Number of activities that are currently visible on-screen.
@UnsupportedAppUsage
int mNumVisibleActivities = 0;
+ private final AtomicInteger mNumLaunchingActivities = new AtomicInteger();
@GuardedBy("mAppThread")
private int mLastProcessState = PROCESS_STATE_UNKNOWN;
@GuardedBy("mAppThread")
@@ -555,10 +555,6 @@
boolean hideForNow;
Configuration createdConfig;
Configuration overrideConfig;
- // Used to save the last reported configuration from server side so that activity
- // configuration transactions can always use the latest configuration.
- @GuardedBy("this")
- private Configuration mPendingOverrideConfig;
// Used for consolidating configs before sending on to Activity.
private Configuration tmpConfig = new Configuration();
// Callback used for updating activity override config and camera compat control state.
@@ -3361,21 +3357,6 @@
}
@Override
- public void addLaunchingActivity(IBinder token, ActivityClientRecord activity) {
- mLaunchingActivities.put(token, activity);
- }
-
- @Override
- public ActivityClientRecord getLaunchingActivity(IBinder token) {
- return mLaunchingActivities.get(token);
- }
-
- @Override
- public void removeLaunchingActivity(IBinder token) {
- mLaunchingActivities.remove(token);
- }
-
- @Override
public ActivityClientRecord getActivityClient(IBinder token) {
return mActivities.get(token);
}
@@ -3419,7 +3400,7 @@
// Defer the top state for VM to avoid aggressive JIT compilation affecting activity
// launch time.
if (processState == ActivityManager.PROCESS_STATE_TOP
- && !mLaunchingActivities.isEmpty()) {
+ && mNumLaunchingActivities.get() > 0) {
mPendingProcessState = processState;
mH.postDelayed(this::applyPendingProcessState, PENDING_TOP_PROCESS_STATE_TIMEOUT);
} else {
@@ -3435,7 +3416,7 @@
// Handle the pending configuration if the process state is changed from cached to
// non-cached. Except the case where there is a launching activity because the
// LaunchActivityItem will handle it.
- if (wasCached && !isCachedProcessState() && mLaunchingActivities.isEmpty()) {
+ if (wasCached && !isCachedProcessState() && mNumLaunchingActivities.get() == 0) {
final Configuration pendingConfig =
mConfigurationController.getPendingConfiguration(false /* clearPending */);
if (pendingConfig == null) {
@@ -3473,6 +3454,11 @@
}
}
+ @Override
+ public void countLaunchingActivities(int num) {
+ mNumLaunchingActivities.getAndAdd(num);
+ }
+
@UnsupportedAppUsage
public final void sendActivityResult(
IBinder token, String id, int requestCode,
@@ -6096,31 +6082,31 @@
}
/**
- * Sets the supplied {@code overrideConfig} as pending for the {@code activityToken}. Calling
+ * Sets the supplied {@code overrideConfig} as pending for the {@code token}. Calling
* this method prevents any calls to
* {@link #handleActivityConfigurationChanged(ActivityClientRecord, Configuration, int)} from
* processing any configurations older than {@code overrideConfig}.
*/
@Override
- public void updatePendingActivityConfiguration(ActivityClientRecord r,
- Configuration overrideConfig) {
- synchronized (r) {
- if (r.mPendingOverrideConfig != null
- && !r.mPendingOverrideConfig.isOtherSeqNewer(overrideConfig)) {
+ public void updatePendingActivityConfiguration(IBinder token, Configuration overrideConfig) {
+ synchronized (mPendingOverrideConfigs) {
+ final Configuration pendingOverrideConfig = mPendingOverrideConfigs.get(token);
+ if (pendingOverrideConfig != null
+ && !pendingOverrideConfig.isOtherSeqNewer(overrideConfig)) {
if (DEBUG_CONFIGURATION) {
- Slog.v(TAG, "Activity has newer configuration pending so drop this"
- + " transaction. overrideConfig=" + overrideConfig
- + " r.mPendingOverrideConfig=" + r.mPendingOverrideConfig);
+ Slog.v(TAG, "Activity has newer configuration pending so this transaction will"
+ + " be dropped. overrideConfig=" + overrideConfig
+ + " pendingOverrideConfig=" + pendingOverrideConfig);
}
return;
}
- r.mPendingOverrideConfig = overrideConfig;
+ mPendingOverrideConfigs.put(token, overrideConfig);
}
}
/**
* Handle new activity configuration and/or move to a different display. This method is a noop
- * if {@link #updatePendingActivityConfiguration(ActivityClientRecord, Configuration)} has been
+ * if {@link #updatePendingActivityConfiguration(IBinder, Configuration)} has been
* called with a newer config than {@code overrideConfig}.
*
* @param r Target activity record.
@@ -6131,16 +6117,17 @@
@Override
public void handleActivityConfigurationChanged(ActivityClientRecord r,
@NonNull Configuration overrideConfig, int displayId) {
- synchronized (r) {
- if (overrideConfig.isOtherSeqNewer(r.mPendingOverrideConfig)) {
+ synchronized (mPendingOverrideConfigs) {
+ final Configuration pendingOverrideConfig = mPendingOverrideConfigs.get(r.token);
+ if (overrideConfig.isOtherSeqNewer(pendingOverrideConfig)) {
if (DEBUG_CONFIGURATION) {
Slog.v(TAG, "Activity has newer configuration pending so drop this"
+ " transaction. overrideConfig=" + overrideConfig
- + " r.mPendingOverrideConfig=" + r.mPendingOverrideConfig);
+ + " pendingOverrideConfig=" + pendingOverrideConfig);
}
return;
}
- r.mPendingOverrideConfig = null;
+ mPendingOverrideConfigs.remove(r.token);
}
if (displayId == INVALID_DISPLAY) {
diff --git a/core/java/android/app/ClientTransactionHandler.java b/core/java/android/app/ClientTransactionHandler.java
index c743f65..d365269 100644
--- a/core/java/android/app/ClientTransactionHandler.java
+++ b/core/java/android/app/ClientTransactionHandler.java
@@ -83,6 +83,9 @@
/** Set current process state. */
public abstract void updateProcessState(int processState, boolean fromIpc);
+ /** Count how many activities are launching. */
+ public abstract void countLaunchingActivities(int num);
+
// Execute phase related logic and handlers. Methods here execute actual lifecycle transactions
// and deliver callbacks.
@@ -139,7 +142,7 @@
public abstract void performRestartActivity(@NonNull ActivityClientRecord r, boolean start);
/** Set pending activity configuration in case it will be updated by other transaction item. */
- public abstract void updatePendingActivityConfiguration(@NonNull ActivityClientRecord r,
+ public abstract void updatePendingActivityConfiguration(@NonNull IBinder token,
Configuration overrideConfig);
/** Deliver activity (override) configuration change. */
@@ -189,26 +192,6 @@
FixedRotationAdjustments fixedRotationAdjustments);
/**
- * Add {@link ActivityClientRecord} that is preparing to be launched.
- * @param token Activity token.
- * @param activity An initialized instance of {@link ActivityClientRecord} to use during launch.
- */
- public abstract void addLaunchingActivity(IBinder token, ActivityClientRecord activity);
-
- /**
- * Get {@link ActivityClientRecord} that is preparing to be launched.
- * @param token Activity token.
- * @return An initialized instance of {@link ActivityClientRecord} to use during launch.
- */
- public abstract ActivityClientRecord getLaunchingActivity(IBinder token);
-
- /**
- * Remove {@link ActivityClientRecord} from the launching activity list.
- * @param token Activity token.
- */
- public abstract void removeLaunchingActivity(IBinder token);
-
- /**
* Get {@link android.app.ActivityThread.ActivityClientRecord} instance that corresponds to the
* provided token.
*/
diff --git a/core/java/android/app/servertransaction/ActivityConfigurationChangeItem.java b/core/java/android/app/servertransaction/ActivityConfigurationChangeItem.java
index 032b57e..5a3ad31 100644
--- a/core/java/android/app/servertransaction/ActivityConfigurationChangeItem.java
+++ b/core/java/android/app/servertransaction/ActivityConfigurationChangeItem.java
@@ -40,11 +40,9 @@
@Override
public void preExecute(android.app.ClientTransactionHandler client, IBinder token) {
- final ActivityClientRecord r = getActivityClientRecord(client, token,
- true /* includeLaunching */);
// Notify the client of an upcoming change in the token configuration. This ensures that
// batches of config change items only process the newest configuration.
- client.updatePendingActivityConfiguration(r, mConfiguration);
+ client.updatePendingActivityConfiguration(token, mConfiguration);
}
@Override
diff --git a/core/java/android/app/servertransaction/ActivityTransactionItem.java b/core/java/android/app/servertransaction/ActivityTransactionItem.java
index 186f25d..6a6d76d 100644
--- a/core/java/android/app/servertransaction/ActivityTransactionItem.java
+++ b/core/java/android/app/servertransaction/ActivityTransactionItem.java
@@ -53,43 +53,23 @@
public abstract void execute(@NonNull ClientTransactionHandler client,
@NonNull ActivityClientRecord r, PendingTransactionActions pendingActions);
- @NonNull ActivityClientRecord getActivityClientRecord(
- @NonNull ClientTransactionHandler client, IBinder token) {
- return getActivityClientRecord(client, token, false /* includeLaunching */);
- }
-
/**
* Gets the {@link ActivityClientRecord} instance that corresponds to the provided token.
* @param client Target client handler.
* @param token Target activity token.
- * @param includeLaunching Indicate to find the {@link ActivityClientRecord} in launching
- * activity list.
- * <p>Note that there is no {@link android.app.Activity} instance in
- * {@link ActivityClientRecord} from the launching activity list.
* @return The {@link ActivityClientRecord} instance that corresponds to the provided token.
*/
@NonNull ActivityClientRecord getActivityClientRecord(
- @NonNull ClientTransactionHandler client, IBinder token, boolean includeLaunching) {
- ActivityClientRecord r = null;
- // Check launching Activity first to prevent race condition that activity instance has not
- // yet set to ActivityClientRecord.
- if (includeLaunching) {
- r = client.getLaunchingActivity(token);
- }
- // Then if we don't want to find launching Activity or the ActivityClientRecord doesn't
- // exist in launching Activity list. The ActivityClientRecord should have been initialized
- // and put in the Activity list.
- if (r == null) {
- r = client.getActivityClient(token);
- if (r != null && client.getActivity(token) == null) {
- throw new IllegalArgumentException("Activity must not be null to execute "
- + "transaction item");
- }
- }
+ @NonNull ClientTransactionHandler client, IBinder token) {
+ final ActivityClientRecord r = client.getActivityClient(token);
if (r == null) {
throw new IllegalArgumentException("Activity client record must not be null to execute "
+ "transaction item");
}
+ if (client.getActivity(token) == null) {
+ throw new IllegalArgumentException("Activity must not be null to execute "
+ + "transaction item");
+ }
return r;
}
}
diff --git a/core/java/android/app/servertransaction/LaunchActivityItem.java b/core/java/android/app/servertransaction/LaunchActivityItem.java
index 4de608b..0a2503c 100644
--- a/core/java/android/app/servertransaction/LaunchActivityItem.java
+++ b/core/java/android/app/servertransaction/LaunchActivityItem.java
@@ -82,12 +82,7 @@
@Override
public void preExecute(ClientTransactionHandler client, IBinder token) {
- ActivityClientRecord r = new ActivityClientRecord(token, mIntent, mIdent, mInfo,
- mOverrideConfig, mCompatInfo, mReferrer, mVoiceInteractor, mState, mPersistentState,
- mPendingResults, mPendingNewIntents, mActivityOptions, mIsForward, mProfilerInfo,
- client, mAssistToken, mFixedRotationAdjustments, mShareableActivityToken,
- mLaunchedFromBubble);
- client.addLaunchingActivity(token, r);
+ client.countLaunchingActivities(1);
client.updateProcessState(mProcState, false);
client.updatePendingConfiguration(mCurConfig);
if (mActivityClientController != null) {
@@ -99,7 +94,11 @@
public void execute(ClientTransactionHandler client, IBinder token,
PendingTransactionActions pendingActions) {
Trace.traceBegin(TRACE_TAG_ACTIVITY_MANAGER, "activityStart");
- ActivityClientRecord r = client.getLaunchingActivity(token);
+ ActivityClientRecord r = new ActivityClientRecord(token, mIntent, mIdent, mInfo,
+ mOverrideConfig, mCompatInfo, mReferrer, mVoiceInteractor, mState, mPersistentState,
+ mPendingResults, mPendingNewIntents, mActivityOptions, mIsForward, mProfilerInfo,
+ client, mAssistToken, mFixedRotationAdjustments, mShareableActivityToken,
+ mLaunchedFromBubble);
client.handleLaunchActivity(r, pendingActions, null /* customIntent */);
Trace.traceEnd(TRACE_TAG_ACTIVITY_MANAGER);
}
@@ -107,7 +106,7 @@
@Override
public void postExecute(ClientTransactionHandler client, IBinder token,
PendingTransactionActions pendingActions) {
- client.removeLaunchingActivity(token);
+ client.countLaunchingActivities(-1);
}
diff --git a/core/java/android/app/servertransaction/MoveToDisplayItem.java b/core/java/android/app/servertransaction/MoveToDisplayItem.java
index 4b8a347..2893ff2 100644
--- a/core/java/android/app/servertransaction/MoveToDisplayItem.java
+++ b/core/java/android/app/servertransaction/MoveToDisplayItem.java
@@ -40,11 +40,9 @@
@Override
public void preExecute(ClientTransactionHandler client, IBinder token) {
- final ActivityClientRecord r = getActivityClientRecord(client, token,
- true /* includeLaunching */);
// Notify the client of an upcoming change in the token configuration. This ensures that
// batches of config change items only process the newest configuration.
- client.updatePendingActivityConfiguration(r, mConfiguration);
+ client.updatePendingActivityConfiguration(token, mConfiguration);
}
@Override
diff --git a/core/java/android/bluetooth/BluetoothUuid.java b/core/java/android/bluetooth/BluetoothUuid.java
index bb537dd..2a8ff51 100644
--- a/core/java/android/bluetooth/BluetoothUuid.java
+++ b/core/java/android/bluetooth/BluetoothUuid.java
@@ -189,7 +189,7 @@
@NonNull
@SystemApi
public static final ParcelUuid CAP =
- ParcelUuid.fromString("00008FE0-0000-1000-8000-00805F9B34FB");
+ ParcelUuid.fromString("00001853-0000-1000-8000-00805F9B34FB");
/** @hide */
@NonNull
@SystemApi
diff --git a/core/java/android/companion/AssociationInfo.java b/core/java/android/companion/AssociationInfo.java
index 3f02aa2..78d5137 100644
--- a/core/java/android/companion/AssociationInfo.java
+++ b/core/java/android/companion/AssociationInfo.java
@@ -197,6 +197,20 @@
return macAddress.equals(mDeviceMacAddress);
}
+ /** @hide */
+ public @NonNull String toShortString() {
+ final StringBuilder sb = new StringBuilder();
+ sb.append("id=").append(mId);
+ if (mDeviceMacAddress != null) {
+ sb.append(", addr=").append(getDeviceMacAddressAsString());
+ }
+ if (mSelfManaged) {
+ sb.append(", self-managed");
+ }
+ sb.append(", pkg=u").append(mUserId).append('/').append(mPackageName);
+ return sb.toString();
+ }
+
@Override
public String toString() {
return "Association{"
diff --git a/core/java/android/content/Context.java b/core/java/android/content/Context.java
index 117f305..120a0a6 100644
--- a/core/java/android/content/Context.java
+++ b/core/java/android/content/Context.java
@@ -3615,9 +3615,8 @@
* {@link #BIND_ADJUST_WITH_ACTIVITY}.
* @return {@code true} if the system is in the process of bringing up a
* service that your client has permission to bind to; {@code false}
- * if the system couldn't find the service. If this value is {@code true}, you
- * should later call {@link #unbindService} to release the
- * connection.
+ * if the system couldn't find the service. You should call {@link #unbindService}
+ * to release the connection even if this method returned {@code false}.
*
* @throws SecurityException if the client does not have the required permission to bind.
*/
diff --git a/core/java/android/content/pm/PackageInstaller.java b/core/java/android/content/pm/PackageInstaller.java
index 251d5e8..495100b 100644
--- a/core/java/android/content/pm/PackageInstaller.java
+++ b/core/java/android/content/pm/PackageInstaller.java
@@ -2374,7 +2374,7 @@
STAGED_SESSION_UNKNOWN,
STAGED_SESSION_CONFLICT})
@Retention(RetentionPolicy.SOURCE)
- public @interface StagedSessionErrorCode{}
+ public @interface SessionErrorCode {}
/**
* Constant indicating that no error occurred during the preparation or the activation of
* this staged session.
@@ -2486,13 +2486,13 @@
public int[] childSessionIds = NO_SESSIONS;
/** {@hide} */
- public boolean isStagedSessionApplied;
+ public boolean isSessionApplied;
/** {@hide} */
- public boolean isStagedSessionReady;
+ public boolean isSessionReady;
/** {@hide} */
- public boolean isStagedSessionFailed;
- private int mStagedSessionErrorCode;
- private String mStagedSessionErrorMessage;
+ public boolean isSessionFailed;
+ private int mSessionErrorCode;
+ private String mSessionErrorMessage;
/** {@hide} */
public boolean isCommitted;
@@ -2553,11 +2553,11 @@
if (childSessionIds == null) {
childSessionIds = NO_SESSIONS;
}
- isStagedSessionApplied = source.readBoolean();
- isStagedSessionReady = source.readBoolean();
- isStagedSessionFailed = source.readBoolean();
- mStagedSessionErrorCode = source.readInt();
- mStagedSessionErrorMessage = source.readString();
+ isSessionApplied = source.readBoolean();
+ isSessionReady = source.readBoolean();
+ isSessionFailed = source.readBoolean();
+ mSessionErrorCode = source.readInt();
+ mSessionErrorMessage = source.readString();
isCommitted = source.readBoolean();
rollbackDataPolicy = source.readInt();
createdMillis = source.readLong();
@@ -2951,7 +2951,7 @@
* since that is the one that should have been {@link Session#commit committed}.
*/
public boolean isStagedSessionActive() {
- return isStaged && isCommitted && !isStagedSessionApplied && !isStagedSessionFailed
+ return isStaged && isCommitted && !isSessionApplied && !isSessionFailed
&& !hasParentSessionId();
}
@@ -2992,7 +2992,7 @@
*/
public boolean isStagedSessionApplied() {
checkSessionIsStaged();
- return isStagedSessionApplied;
+ return isSessionApplied;
}
/**
@@ -3001,7 +3001,7 @@
*/
public boolean isStagedSessionReady() {
checkSessionIsStaged();
- return isStagedSessionReady;
+ return isSessionReady;
}
/**
@@ -3010,16 +3010,16 @@
*/
public boolean isStagedSessionFailed() {
checkSessionIsStaged();
- return isStagedSessionFailed;
+ return isSessionFailed;
}
/**
* If something went wrong with a staged session, clients can check this error code to
* understand which kind of failure happened. Only meaningful if {@code isStaged} is true.
*/
- public @StagedSessionErrorCode int getStagedSessionErrorCode() {
+ public @SessionErrorCode int getStagedSessionErrorCode() {
checkSessionIsStaged();
- return mStagedSessionErrorCode;
+ return mSessionErrorCode;
}
/**
@@ -3028,14 +3028,13 @@
*/
public @NonNull String getStagedSessionErrorMessage() {
checkSessionIsStaged();
- return mStagedSessionErrorMessage;
+ return mSessionErrorMessage;
}
/** {@hide} */
- public void setStagedSessionErrorCode(@StagedSessionErrorCode int errorCode,
- String errorMessage) {
- mStagedSessionErrorCode = errorCode;
- mStagedSessionErrorMessage = errorMessage;
+ public void setSessionErrorCode(@SessionErrorCode int errorCode, String errorMessage) {
+ mSessionErrorCode = errorCode;
+ mSessionErrorMessage = errorMessage;
}
/**
@@ -3124,11 +3123,11 @@
dest.writeBoolean(forceQueryable);
dest.writeInt(parentSessionId);
dest.writeIntArray(childSessionIds);
- dest.writeBoolean(isStagedSessionApplied);
- dest.writeBoolean(isStagedSessionReady);
- dest.writeBoolean(isStagedSessionFailed);
- dest.writeInt(mStagedSessionErrorCode);
- dest.writeString(mStagedSessionErrorMessage);
+ dest.writeBoolean(isSessionApplied);
+ dest.writeBoolean(isSessionReady);
+ dest.writeBoolean(isSessionFailed);
+ dest.writeInt(mSessionErrorCode);
+ dest.writeString(mSessionErrorMessage);
dest.writeBoolean(isCommitted);
dest.writeInt(rollbackDataPolicy);
dest.writeLong(createdMillis);
diff --git a/core/java/android/hardware/SystemSensorManager.java b/core/java/android/hardware/SystemSensorManager.java
index 282f1d3..3a8513b 100644
--- a/core/java/android/hardware/SystemSensorManager.java
+++ b/core/java/android/hardware/SystemSensorManager.java
@@ -464,7 +464,8 @@
IntentFilter filter = new IntentFilter("dynamic_sensor_change");
filter.addAction(Intent.ACTION_DYNAMIC_SENSOR_CHANGED);
- mContext.registerReceiver(mDynamicSensorBroadcastReceiver, filter);
+ mContext.registerReceiver(mDynamicSensorBroadcastReceiver, filter,
+ Context.RECEIVER_NOT_EXPORTED);
}
}
diff --git a/core/java/android/hardware/display/DisplayManagerGlobal.java b/core/java/android/hardware/display/DisplayManagerGlobal.java
index 01833fd..e731165 100644
--- a/core/java/android/hardware/display/DisplayManagerGlobal.java
+++ b/core/java/android/hardware/display/DisplayManagerGlobal.java
@@ -965,7 +965,7 @@
private static final class DisplayListenerDelegate extends Handler {
public final DisplayListener mListener;
- public long mEventsMask;
+ public volatile long mEventsMask;
private final DisplayInfo mDisplayInfo = new DisplayInfo();
@@ -985,12 +985,12 @@
removeCallbacksAndMessages(null);
}
- public synchronized void setEventsMask(@EventsMask long newEventsMask) {
+ public void setEventsMask(@EventsMask long newEventsMask) {
mEventsMask = newEventsMask;
}
@Override
- public synchronized void handleMessage(Message msg) {
+ public void handleMessage(Message msg) {
switch (msg.what) {
case EVENT_DISPLAY_ADDED:
if ((mEventsMask & DisplayManager.EVENT_FLAG_DISPLAY_ADDED) != 0) {
diff --git a/core/java/android/inputmethodservice/InputMethodService.java b/core/java/android/inputmethodservice/InputMethodService.java
index 22b444e..afaa085 100644
--- a/core/java/android/inputmethodservice/InputMethodService.java
+++ b/core/java/android/inputmethodservice/InputMethodService.java
@@ -51,7 +51,6 @@
import static android.view.ViewGroup.LayoutParams.WRAP_CONTENT;
import static android.view.WindowInsets.Type.navigationBars;
import static android.view.WindowInsets.Type.statusBars;
-import static android.view.WindowManager.LayoutParams.FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS;
import static java.lang.annotation.RetentionPolicy.SOURCE;
@@ -1340,28 +1339,43 @@
mInflater = (LayoutInflater)getSystemService(
Context.LAYOUT_INFLATER_SERVICE);
Trace.traceBegin(TRACE_TAG_WINDOW_MANAGER, "IMS.initSoftInputWindow");
- mWindow = new SoftInputWindow(this, "InputMethod", mTheme, null, null, mDispatcherState,
- WindowManager.LayoutParams.TYPE_INPUT_METHOD, Gravity.BOTTOM, false);
- mWindow.getWindow().getAttributes().setFitInsetsTypes(statusBars() | navigationBars());
- mWindow.getWindow().getAttributes().setFitInsetsSides(Side.all() & ~Side.BOTTOM);
- mWindow.getWindow().getAttributes().receiveInsetsIgnoringZOrder = true;
+ mWindow = new SoftInputWindow(this, mTheme, mDispatcherState);
- // Automotive devices may request the navigation bar to be hidden when the IME shows up
- // (controlled via config_automotiveHideNavBarForKeyboard) in order to maximize the visible
- // screen real estate. When this happens, the IME window should animate from the bottom of
- // the screen to reduce the jank that happens from the lack of synchronization between the
- // bottom system window and the IME window.
- if (mIsAutomotive && mAutomotiveHideNavBarForKeyboard) {
- mWindow.getWindow().setDecorFitsSystemWindows(false);
+ {
+ final Window window = mWindow.getWindow();
+ {
+ final WindowManager.LayoutParams lp = window.getAttributes();
+ lp.setTitle("InputMethod");
+ lp.type = WindowManager.LayoutParams.TYPE_INPUT_METHOD;
+ lp.width = WindowManager.LayoutParams.MATCH_PARENT;
+ lp.height = WindowManager.LayoutParams.WRAP_CONTENT;
+ lp.gravity = Gravity.BOTTOM;
+ lp.setFitInsetsTypes(statusBars() | navigationBars());
+ lp.setFitInsetsSides(Side.all() & ~Side.BOTTOM);
+ lp.receiveInsetsIgnoringZOrder = true;
+ window.setAttributes(lp);
+ }
+
+ // For ColorView in DecorView to work, FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS needs to be set
+ // by default (but IME developers can opt this out later if they want a new behavior).
+ final int windowFlags = WindowManager.LayoutParams.FLAG_LAYOUT_IN_SCREEN
+ | WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE
+ | WindowManager.LayoutParams.FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS;
+ final int windowFlagsMask = windowFlags
+ | WindowManager.LayoutParams.FLAG_DIM_BEHIND; // to be unset
+ window.setFlags(windowFlags, windowFlagsMask);
+
+ // Automotive devices may request the navigation bar to be hidden when the IME shows up
+ // (controlled via config_automotiveHideNavBarForKeyboard) in order to maximize the
+ // visible screen real estate. When this happens, the IME window should animate from the
+ // bottom of the screen to reduce the jank that happens from the lack of synchronization
+ // between the bottom system window and the IME window.
+ if (mIsAutomotive && mAutomotiveHideNavBarForKeyboard) {
+ window.setDecorFitsSystemWindows(false);
+ }
}
- // For ColorView in DecorView to work, FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS needs to be set
- // by default (but IME developers can opt this out later if they want a new behavior).
- mWindow.getWindow().setFlags(
- FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS, FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS);
-
initViews();
- mWindow.getWindow().setLayout(MATCH_PARENT, WRAP_CONTENT);
Trace.traceEnd(TRACE_TAG_WINDOW_MANAGER);
mInlineSuggestionSessionController = new InlineSuggestionSessionController(
diff --git a/core/java/android/inputmethodservice/SoftInputWindow.java b/core/java/android/inputmethodservice/SoftInputWindow.java
index c2286d1..6c8eb41 100644
--- a/core/java/android/inputmethodservice/SoftInputWindow.java
+++ b/core/java/android/inputmethodservice/SoftInputWindow.java
@@ -17,11 +17,7 @@
package android.inputmethodservice;
import static android.inputmethodservice.SoftInputWindowProto.BOUNDS;
-import static android.inputmethodservice.SoftInputWindowProto.GRAVITY;
-import static android.inputmethodservice.SoftInputWindowProto.NAME;
-import static android.inputmethodservice.SoftInputWindowProto.TAKES_FOCUS;
import static android.inputmethodservice.SoftInputWindowProto.WINDOW_STATE;
-import static android.inputmethodservice.SoftInputWindowProto.WINDOW_TYPE;
import static java.lang.annotation.RetentionPolicy.SOURCE;
@@ -33,7 +29,6 @@
import android.os.IBinder;
import android.util.Log;
import android.util.proto.ProtoOutputStream;
-import android.view.Gravity;
import android.view.KeyEvent;
import android.view.MotionEvent;
import android.view.View;
@@ -42,23 +37,15 @@
import java.lang.annotation.Retention;
/**
- * A SoftInputWindow is a Dialog that is intended to be used for a top-level input
- * method window. It will be displayed along the edge of the screen, moving
- * the application user interface away from it so that the focused item is
- * always visible.
- * @hide
+ * A {@link SoftInputWindow} is a {@link Dialog} that is intended to be used for a top-level input
+ * method window. It will be displayed along the edge of the screen, moving the application user
+ * interface away from it so that the focused item is always visible.
*/
-public final class SoftInputWindow extends Dialog {
+final class SoftInputWindow extends Dialog {
private static final boolean DEBUG = false;
private static final String TAG = "SoftInputWindow";
- private final String mName;
- private final Callback mCallback;
- private final KeyEvent.Callback mKeyEventCallback;
private final KeyEvent.DispatcherState mDispatcherState;
- private final int mWindowType;
- private final int mGravity;
- private final boolean mTakesFocus;
private final Rect mBounds = new Rect();
@Retention(SOURCE)
@@ -93,22 +80,12 @@
private int mWindowState = WindowState.TOKEN_PENDING;
/**
- * Used to provide callbacks.
- */
- public interface Callback {
- /**
- * Used to be notified when {@link Dialog#onBackPressed()} gets called.
- */
- void onBackPressed();
- }
-
- /**
* Set {@link IBinder} window token to the window.
*
* <p>This method can be called only once.</p>
* @param token {@link IBinder} token to be associated with the window.
*/
- public void setToken(IBinder token) {
+ void setToken(IBinder token) {
switch (mWindowState) {
case WindowState.TOKEN_PENDING:
// Normal scenario. Nothing to worry about.
@@ -152,18 +129,9 @@
* using styles. This theme is applied on top of the current theme in
* <var>context</var>. If 0, the default dialog theme will be used.
*/
- public SoftInputWindow(Context context, String name, int theme, Callback callback,
- KeyEvent.Callback keyEventCallback, KeyEvent.DispatcherState dispatcherState,
- int windowType, int gravity, boolean takesFocus) {
+ SoftInputWindow(Context context, int theme, KeyEvent.DispatcherState dispatcherState) {
super(context, theme);
- mName = name;
- mCallback = callback;
- mKeyEventCallback = keyEventCallback;
mDispatcherState = dispatcherState;
- mWindowType = windowType;
- mGravity = gravity;
- mTakesFocus = takesFocus;
- initDockWindow();
}
@Override
@@ -188,83 +156,6 @@
}
}
- private void updateWidthHeight(WindowManager.LayoutParams lp) {
- if (lp.gravity == Gravity.TOP || lp.gravity == Gravity.BOTTOM) {
- lp.width = WindowManager.LayoutParams.MATCH_PARENT;
- lp.height = WindowManager.LayoutParams.WRAP_CONTENT;
- } else {
- lp.width = WindowManager.LayoutParams.WRAP_CONTENT;
- lp.height = WindowManager.LayoutParams.MATCH_PARENT;
- }
- }
-
- @Override
- public boolean onKeyDown(int keyCode, KeyEvent event) {
- if (mKeyEventCallback != null && mKeyEventCallback.onKeyDown(keyCode, event)) {
- return true;
- }
- return super.onKeyDown(keyCode, event);
- }
-
- @Override
- public boolean onKeyLongPress(int keyCode, KeyEvent event) {
- if (mKeyEventCallback != null && mKeyEventCallback.onKeyLongPress(keyCode, event)) {
- return true;
- }
- return super.onKeyLongPress(keyCode, event);
- }
-
- @Override
- public boolean onKeyUp(int keyCode, KeyEvent event) {
- if (mKeyEventCallback != null && mKeyEventCallback.onKeyUp(keyCode, event)) {
- return true;
- }
- return super.onKeyUp(keyCode, event);
- }
-
- @Override
- public boolean onKeyMultiple(int keyCode, int count, KeyEvent event) {
- if (mKeyEventCallback != null && mKeyEventCallback.onKeyMultiple(keyCode, count, event)) {
- return true;
- }
- return super.onKeyMultiple(keyCode, count, event);
- }
-
- @Override
- public void onBackPressed() {
- if (mCallback != null) {
- mCallback.onBackPressed();
- } else {
- super.onBackPressed();
- }
- }
-
- private void initDockWindow() {
- WindowManager.LayoutParams lp = getWindow().getAttributes();
-
- lp.type = mWindowType;
- lp.setTitle(mName);
-
- lp.gravity = mGravity;
- updateWidthHeight(lp);
-
- getWindow().setAttributes(lp);
-
- int windowSetFlags = WindowManager.LayoutParams.FLAG_LAYOUT_IN_SCREEN;
- int windowModFlags = WindowManager.LayoutParams.FLAG_LAYOUT_IN_SCREEN
- | WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE
- | WindowManager.LayoutParams.FLAG_DIM_BEHIND;
-
- if (!mTakesFocus) {
- windowSetFlags |= WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE;
- } else {
- windowSetFlags |= WindowManager.LayoutParams.FLAG_NOT_TOUCH_MODAL;
- windowModFlags |= WindowManager.LayoutParams.FLAG_NOT_TOUCH_MODAL;
- }
-
- getWindow().setFlags(windowSetFlags, windowModFlags);
- }
-
@Override
public void show() {
switch (mWindowState) {
@@ -372,10 +263,6 @@
void dumpDebug(ProtoOutputStream proto, long fieldId) {
final long token = proto.start(fieldId);
- proto.write(NAME, mName);
- proto.write(WINDOW_TYPE, mWindowType);
- proto.write(GRAVITY, mGravity);
- proto.write(TAKES_FOCUS, mTakesFocus);
mBounds.dumpDebug(proto, BOUNDS);
proto.write(WINDOW_STATE, mWindowState);
proto.end(token);
diff --git a/core/java/android/net/NetworkPolicy.java b/core/java/android/net/NetworkPolicy.java
index a9c52f1..f4b427959f 100644
--- a/core/java/android/net/NetworkPolicy.java
+++ b/core/java/android/net/NetworkPolicy.java
@@ -369,11 +369,13 @@
try {
final NetworkTemplate.Builder builder = new NetworkTemplate.Builder(matchRule)
- .setWifiNetworkKey(wifiNetworkKey)
.setMeteredness(metered);
if (subscriberId != null) {
builder.setSubscriberIds(Set.of(subscriberId));
}
+ if (wifiNetworkKey != null) {
+ builder.setWifiNetworkKeys(Set.of(wifiNetworkKey));
+ }
return builder.build();
} catch (IllegalArgumentException e) {
throw new BackupUtils.BadVersionException(
@@ -393,7 +395,7 @@
case MATCH_MOBILE:
return !template.getSubscriberIds().isEmpty();
case MATCH_WIFI:
- if (Objects.equals(template.getWifiNetworkKey(), null)
+ if (template.getWifiNetworkKeys().isEmpty()
&& template.getSubscriberIds().isEmpty()) {
return false;
}
diff --git a/core/java/android/permission/PermissionManager.java b/core/java/android/permission/PermissionManager.java
index e2f5908..17f57a8 100644
--- a/core/java/android/permission/PermissionManager.java
+++ b/core/java/android/permission/PermissionManager.java
@@ -218,7 +218,8 @@
/**
* Checks whether a given data access chain described by the given {@link AttributionSource}
* has a given permission. Call this method if you are the datasource which would not blame you
- * for access to the data since you are the data.
+ * for access to the data since you are the data. Use this API if you are the datasource of the
+ * protected state.
*
* <strong>NOTE:</strong> Use this method only for permission checks at the
* point where you will deliver the permission protected data to clients.
diff --git a/core/java/android/permission/PermissionUsageHelper.java b/core/java/android/permission/PermissionUsageHelper.java
index f0e6624..658e033 100644
--- a/core/java/android/permission/PermissionUsageHelper.java
+++ b/core/java/android/permission/PermissionUsageHelper.java
@@ -318,7 +318,7 @@
String permGroup = usedPermGroups.get(permGroupNum);
ArrayMap<OpUsage, CharSequence> usagesWithLabels =
- getUniqueUsagesWithLabels(rawUsages.get(permGroup));
+ getUniqueUsagesWithLabels(permGroup, rawUsages.get(permGroup));
if (permGroup.equals(OPSTR_PHONE_CALL_MICROPHONE)) {
isPhone = true;
@@ -439,7 +439,8 @@
return ListFormatter.getInstance().format(labels);
}
- private ArrayMap<OpUsage, CharSequence> getUniqueUsagesWithLabels(List<OpUsage> usages) {
+ private ArrayMap<OpUsage, CharSequence> getUniqueUsagesWithLabels(String permGroup,
+ List<OpUsage> usages) {
ArrayMap<OpUsage, CharSequence> usagesAndLabels = new ArrayMap<>();
if (usages == null || usages.isEmpty()) {
@@ -474,7 +475,7 @@
// If this usage has a proxy, but is not a proxy, it is the end of a chain.
// TODO remove once camera converted
if (!proxies.containsKey(usageAttr) && usage.proxy != null
- && !usage.op.equals(OPSTR_RECORD_AUDIO)) {
+ && !MICROPHONE.equals(permGroup)) {
proxyLabels.put(usage, new ArrayList<>());
proxyPackages.add(usage.getPackageIdHash());
}
@@ -546,7 +547,7 @@
// TODO ntmyren: remove this proxy logic once camera is converted to AttributionSource
// For now: don't add mic proxy usages
- if (!start.op.equals(OPSTR_RECORD_AUDIO)) {
+ if (!MICROPHONE.equals(permGroup)) {
usagesAndLabels.put(start,
proxyLabelList.isEmpty() ? null : formatLabelList(proxyLabelList));
}
@@ -560,7 +561,8 @@
// if the list is empty or incomplete, do not show it.
if (usageList.isEmpty() || !usageList.get(lastVisible).isEnd()
|| !usageList.get(0).isStart()
- || !usageList.get(lastVisible).usage.op.equals(OPSTR_RECORD_AUDIO)) {
+ || !permGroup.equals(getGroupForOp(usageList.get(0).usage.op))
+ || !MICROPHONE.equals(permGroup)) {
continue;
}
diff --git a/core/java/android/provider/DeviceConfig.java b/core/java/android/provider/DeviceConfig.java
index 22b9578..6349cde 100644
--- a/core/java/android/provider/DeviceConfig.java
+++ b/core/java/android/provider/DeviceConfig.java
@@ -564,6 +564,14 @@
public static final String NAMESPACE_WINDOW_MANAGER_NATIVE_BOOT = "window_manager_native_boot";
/**
+ * Definitions for selection toolbar related functions.
+ *
+ * @hide
+ */
+ @TestApi
+ public static final String NAMESPACE_SELECTION_TOOLBAR = "selection_toolbar";
+
+ /**
* List of namespaces which can be read without READ_DEVICE_CONFIG permission
*
* @hide
@@ -571,7 +579,7 @@
@NonNull
private static final List<String> PUBLIC_NAMESPACES =
Arrays.asList(NAMESPACE_TEXTCLASSIFIER, NAMESPACE_RUNTIME, NAMESPACE_STATSD_JAVA,
- NAMESPACE_STATSD_JAVA_BOOT);
+ NAMESPACE_STATSD_JAVA_BOOT, NAMESPACE_SELECTION_TOOLBAR);
/**
* Privacy related properties definitions.
*
diff --git a/core/java/android/provider/Settings.java b/core/java/android/provider/Settings.java
index f5777ed..910fec6 100644
--- a/core/java/android/provider/Settings.java
+++ b/core/java/android/provider/Settings.java
@@ -950,6 +950,19 @@
"android.settings.LOCALE_SETTINGS";
/**
+ * Activity Action: Show settings to allow configuration of per application locale.
+ * <p>
+ * Input: The Intent's data URI can specify the application package name to directly invoke the
+ * app locale details GUI specific to the package name.
+ * For example "package:com.my.app".
+ * <p>
+ * Output: Nothing.
+ */
+ @SdkConstant(SdkConstantType.ACTIVITY_INTENT_ACTION)
+ public static final String ACTION_APP_LOCALE_SETTINGS =
+ "android.settings.APP_LOCALE_SETTINGS";
+
+ /**
* Activity Action: Show settings to allow configuration of lockscreen.
* <p>
* In some cases, a matching Activity may not exist, so ensure you
@@ -6495,6 +6508,27 @@
public static final String ALLOW_MOCK_LOCATION = "mock_location";
/**
+ * This is used by Bluetooth Manager to store adapter name
+ * @hide
+ */
+ @Readable(maxTargetSdk = Build.VERSION_CODES.S)
+ public static final String BLUETOOTH_NAME = "bluetooth_name";
+
+ /**
+ * This is used by Bluetooth Manager to store adapter address
+ * @hide
+ */
+ @Readable(maxTargetSdk = Build.VERSION_CODES.S)
+ public static final String BLUETOOTH_ADDRESS = "bluetooth_address";
+
+ /**
+ * This is used by Bluetooth Manager to store whether adapter address is valid
+ * @hide
+ */
+ @Readable(maxTargetSdk = Build.VERSION_CODES.S)
+ public static final String BLUETOOTH_ADDR_VALID = "bluetooth_addr_valid";
+
+ /**
* Setting to indicate that on device captions are enabled.
*
* @hide
@@ -6503,6 +6537,16 @@
@Readable
public static final String ODI_CAPTIONS_ENABLED = "odi_captions_enabled";
+
+ /**
+ * Setting to indicate live caption button show or hide in the volume
+ * rocker.
+ *
+ * @hide
+ */
+ public static final String ODI_CAPTIONS_VOLUME_UI_ENABLED =
+ "odi_captions_volume_ui_enabled";
+
/**
* On Android 8.0 (API level 26) and higher versions of the platform,
* a 64-bit number (expressed as a hexadecimal string), unique to
@@ -17037,6 +17081,15 @@
*/
public static final String CLOCKWORK_SYSUI_MAIN_ACTIVITY =
"clockwork_sysui_main_activity";
+
+ /**
+ * Setting to disable power button long press launching Assistant. It's boolean, i.e.
+ * enabled = 1, disabled = 0. By default, this setting is enabled.
+ *
+ * @hide
+ */
+ public static final String CLOCKWORK_LONG_PRESS_TO_ASSISTANT_ENABLED =
+ "clockwork_long_press_to_assistant_enabled";
}
}
diff --git a/core/java/android/provider/Telephony.java b/core/java/android/provider/Telephony.java
index e3c3969..34e35d4 100644
--- a/core/java/android/provider/Telephony.java
+++ b/core/java/android/provider/Telephony.java
@@ -3589,7 +3589,7 @@
* @hide
*/
public static final Uri PREFERRED_APN_URI = Uri.parse(
- "content://telephony/carriers/preferapn/subId/");
+ "content://telephony/carriers/preferapn/subId");
/**
* The {@code content://} style URL for the perferred APN set id.
@@ -3597,8 +3597,15 @@
* @hide
*/
public static final Uri PREFERRED_APN_SET_URI = Uri.parse(
- "content://telephony/carriers/preferapnset/subId/");
+ "content://telephony/carriers/preferapnset/subId");
+ /**
+ * The id of preferred APN.
+ *
+ * @see #PREFERRED_APN_URI
+ * @hide
+ */
+ public static final String APN_ID = "apn_id";
/**
* The column name for ENFORCE_MANAGED_URI, indicates whether DPC-owned APNs are enforced.
diff --git a/core/java/android/view/DisplayCutout.java b/core/java/android/view/DisplayCutout.java
index c1a5636..ae323226 100644
--- a/core/java/android/view/DisplayCutout.java
+++ b/core/java/android/view/DisplayCutout.java
@@ -585,8 +585,10 @@
* Returns a list of {@code Rect}s, each of which is the bounding rectangle for a non-functional
* area on the display.
*
- * There will be at most one non-functional area per short edge of the device, and none on
- * the long edges.
+ * There will be at most one non-functional area per edge of the device.
+ *
+ * <p>Note that there is no bounding rectangle for waterfall cutout since it just represents the
+ * curved areas of the display but not the non-functional areas.</p>
*
* @return a list of bounding {@code Rect}s, one for each display cutout area. No empty Rect is
* returned.
@@ -607,8 +609,10 @@
* functional area on the display. Ordinal value of BoundPosition is used as an index of
* the array.
*
- * There will be at most one non-functional area per short edge of the device, and none on
- * the long edges.
+ * There will be at most one non-functional area per edge of the device.
+ *
+ * <p>Note that there is no bounding rectangle for waterfall cutout since it just represents the
+ * curved areas of the display but not the non-functional areas.</p>
*
* @return an array of bounding {@code Rect}s, one for each display cutout area. This might
* contain ZERO_RECT, which means there is no cutout area at the position.
diff --git a/core/java/android/view/SurfaceControl.java b/core/java/android/view/SurfaceControl.java
index 3b52709..ab33fea 100644
--- a/core/java/android/view/SurfaceControl.java
+++ b/core/java/android/view/SurfaceControl.java
@@ -515,6 +515,15 @@
public static final int ENABLE_BACKPRESSURE = 0x00000100;
/**
+ * Buffers from this SurfaceControl should be considered display decorations.
+ *
+ * If the hardware has optimizations for display decorations (e.g. rounded corners, camera
+ * cutouts, etc), it should use them for this layer.
+ * @hide
+ */
+ public static final int DISPLAY_DECORATION = 0x00000200;
+
+ /**
* Surface creation flag: Creates a surface where color components are interpreted
* as "non pre-multiplied" by their alpha channel. Of course this flag is
* meaningless for surfaces without an alpha channel. By default
@@ -3266,6 +3275,21 @@
}
/**
+ * Sets whether the surface should take advantage of display decoration optimizations.
+ * @hide
+ */
+ public Transaction setDisplayDecoration(SurfaceControl sc, boolean displayDecoration) {
+ checkPreconditions(sc);
+ if (displayDecoration) {
+ nativeSetFlags(mNativeObject, sc.mNativeObject, DISPLAY_DECORATION,
+ DISPLAY_DECORATION);
+ } else {
+ nativeSetFlags(mNativeObject, sc.mNativeObject, 0, DISPLAY_DECORATION);
+ }
+ return this;
+ }
+
+ /**
* Indicates whether the surface must be considered opaque, even if its pixel format is
* set to translucent. This can be useful if an application needs full RGBA 8888 support
* for instance but will still draw every pixel opaque.
diff --git a/core/java/android/view/ThreadedRenderer.java b/core/java/android/view/ThreadedRenderer.java
index 00754af..1566f9e 100644
--- a/core/java/android/view/ThreadedRenderer.java
+++ b/core/java/android/view/ThreadedRenderer.java
@@ -478,6 +478,19 @@
}
/**
+ * Remove a frame drawing callback that was added via
+ * {@link #registerRtFrameCallback(FrameDrawingCallback)}
+ *
+ * @param callback The callback to unregister.
+ */
+ void unregisterRtFrameCallback(@NonNull FrameDrawingCallback callback) {
+ if (mNextRtFrameCallbacks == null) {
+ return;
+ }
+ mNextRtFrameCallbacks.remove(callback);
+ }
+
+ /**
* Destroys all hardware rendering resources associated with the specified
* view hierarchy.
*
@@ -679,9 +692,31 @@
if (mNextRtFrameCallbacks != null) {
final ArrayList<FrameDrawingCallback> frameCallbacks = mNextRtFrameCallbacks;
mNextRtFrameCallbacks = null;
- setFrameCallback(frame -> {
- for (int i = 0; i < frameCallbacks.size(); ++i) {
- frameCallbacks.get(i).onFrameDraw(frame);
+ setFrameCallback(new FrameDrawingCallback() {
+ @Override
+ public void onFrameDraw(long frame) {
+ }
+
+ @Override
+ public FrameCommitCallback onFrameDraw(int syncResult, long frame) {
+ ArrayList<FrameCommitCallback> frameCommitCallbacks = new ArrayList<>();
+ for (int i = 0; i < frameCallbacks.size(); ++i) {
+ FrameCommitCallback frameCommitCallback = frameCallbacks.get(i)
+ .onFrameDraw(syncResult, frame);
+ if (frameCommitCallback != null) {
+ frameCommitCallbacks.add(frameCommitCallback);
+ }
+ }
+
+ if (frameCommitCallbacks.isEmpty()) {
+ return null;
+ }
+
+ return didProduceBuffer -> {
+ for (int i = 0; i < frameCommitCallbacks.size(); ++i) {
+ frameCommitCallbacks.get(i).onFrameCommit(didProduceBuffer);
+ }
+ };
}
});
}
diff --git a/core/java/android/view/View.java b/core/java/android/view/View.java
index 8fda48b..258359e 100644
--- a/core/java/android/view/View.java
+++ b/core/java/android/view/View.java
@@ -15272,7 +15272,7 @@
* @param event the KeyEvent object that defines the button action
*/
public boolean onKeyDown(int keyCode, KeyEvent event) {
- if (KeyEvent.isConfirmKey(keyCode)) {
+ if (event.hasNoModifiers() && KeyEvent.isConfirmKey(keyCode)) {
if ((mViewFlags & ENABLED_MASK) == DISABLED) {
return true;
}
@@ -15329,7 +15329,7 @@
* @param event The KeyEvent object that defines the button action.
*/
public boolean onKeyUp(int keyCode, KeyEvent event) {
- if (KeyEvent.isConfirmKey(keyCode)) {
+ if (event.hasNoModifiers() && KeyEvent.isConfirmKey(keyCode)) {
if ((mViewFlags & ENABLED_MASK) == DISABLED) {
return true;
}
diff --git a/core/java/android/view/ViewRootImpl.java b/core/java/android/view/ViewRootImpl.java
index af496dc..f86abec 100644
--- a/core/java/android/view/ViewRootImpl.java
+++ b/core/java/android/view/ViewRootImpl.java
@@ -16,6 +16,8 @@
package android.view;
+import static android.graphics.HardwareRenderer.SYNC_CONTEXT_IS_STOPPED;
+import static android.graphics.HardwareRenderer.SYNC_LOST_SURFACE_REWARD_IF_FOUND;
import static android.os.IInputConstants.INVALID_INPUT_EVENT_ID;
import static android.view.Display.DEFAULT_DISPLAY;
import static android.view.Display.INVALID_DISPLAY;
@@ -491,6 +493,9 @@
protected final ViewFrameInfo mViewFrameInfo = new ViewFrameInfo();
private final InputEventAssigner mInputEventAssigner = new InputEventAssigner();
+ // Whether to draw this surface as DISPLAY_DECORATION.
+ boolean mDisplayDecorationCached = false;
+
/**
* Update the Choreographer's FrameInfo object with the timing information for the current
* ViewRootImpl instance. Erase the data in the current ViewFrameInfo to prepare for the next
@@ -1392,11 +1397,20 @@
*/
public void registerRtFrameCallback(@NonNull FrameDrawingCallback callback) {
if (mAttachInfo.mThreadedRenderer != null) {
- mAttachInfo.mThreadedRenderer.registerRtFrameCallback(frame -> {
- try {
- callback.onFrameDraw(frame);
- } catch (Exception e) {
- Log.e(TAG, "Exception while executing onFrameDraw", e);
+ mAttachInfo.mThreadedRenderer.registerRtFrameCallback(new FrameDrawingCallback() {
+ @Override
+ public void onFrameDraw(long frame) {
+ }
+
+ @Override
+ public HardwareRenderer.FrameCommitCallback onFrameDraw(int syncResult,
+ long frame) {
+ try {
+ return callback.onFrameDraw(syncResult, frame);
+ } catch (Exception e) {
+ Log.e(TAG, "Exception while executing onFrameDraw", e);
+ }
+ return null;
}
});
}
@@ -1959,26 +1973,29 @@
return mBoundsLayer;
}
- Surface getOrCreateBLASTSurface() {
+ void updateBlastSurfaceIfNeeded() {
if (!mSurfaceControl.isValid()) {
- return null;
+ return;
}
- Surface ret = null;
- if (mBlastBufferQueue == null) {
- mBlastBufferQueue = new BLASTBufferQueue(mTag, mSurfaceControl,
- mSurfaceSize.x, mSurfaceSize.y,
- mWindowAttributes.format);
- // We only return the Surface the first time, as otherwise
- // it hasn't changed and there is no need to update.
- ret = mBlastBufferQueue.createSurface();
- } else {
+ if (mBlastBufferQueue != null && mBlastBufferQueue.isSameSurfaceControl(mSurfaceControl)) {
mBlastBufferQueue.update(mSurfaceControl,
mSurfaceSize.x, mSurfaceSize.y,
mWindowAttributes.format);
+ return;
}
- return ret;
+ // If the SurfaceControl has been updated, destroy and recreate the BBQ to reset the BQ and
+ // BBQ states.
+ if (mBlastBufferQueue != null) {
+ mBlastBufferQueue.destroy();
+ }
+ mBlastBufferQueue = new BLASTBufferQueue(mTag, mSurfaceControl,
+ mSurfaceSize.x, mSurfaceSize.y, mWindowAttributes.format);
+ Surface blastSurface = mBlastBufferQueue.createSurface();
+ // Only call transferFrom if the surface has changed to prevent inc the generation ID and
+ // causing EGL resources to be recreated.
+ mSurface.transferFrom(blastSurface);
}
private void setBoundsLayerCrop(Transaction t) {
@@ -2842,6 +2859,12 @@
if (mSurfaceControl.isValid()) {
updateOpacity(mWindowAttributes, dragResizing,
surfaceControlChanged /*forceUpdate */);
+ // No need to updateDisplayDecoration if it's a new SurfaceControl and
+ // mDisplayDecorationCached is false, since that's the default for a new
+ // SurfaceControl.
+ if (surfaceControlChanged && mDisplayDecorationCached) {
+ updateDisplayDecoration();
+ }
}
if (DEBUG_LAYOUT) Log.v(mTag, "relayout: frame=" + frame.toShortString()
@@ -4020,61 +4043,6 @@
return mAttachInfo.mThreadedRenderer != null && mAttachInfo.mThreadedRenderer.isEnabled();
}
- private boolean addFrameCompleteCallbackIfNeeded(boolean useBlastSync,
- boolean reportNextDraw) {
- if (!isHardwareEnabled()) {
- return false;
- }
-
- if (!useBlastSync && !reportNextDraw) {
- return false;
- }
-
- if (DEBUG_BLAST) {
- Log.d(mTag, "Creating frameCompleteCallback");
- }
-
- final Consumer<SurfaceControl.Transaction> blastSyncConsumer = mBLASTDrawConsumer;
- mBLASTDrawConsumer = null;
-
- mAttachInfo.mThreadedRenderer.setFrameCompleteCallback(() -> {
- long frameNr = mBlastBufferQueue.getLastAcquiredFrameNum();
- if (DEBUG_BLAST) {
- Log.d(mTag, "Received frameCompleteCallback "
- + " lastAcquiredFrameNum=" + frameNr
- + " lastAttemptedDrawFrameNum=" + mRtLastAttemptedDrawFrameNum);
- }
-
- boolean frameWasNotDrawn = frameNr != mRtLastAttemptedDrawFrameNum;
- // If frame wasn't drawn, clear out the next transaction so it doesn't affect the next
- // draw attempt. The next transaction and transaction complete callback were only set
- // for the current draw attempt.
- if (frameWasNotDrawn) {
- mBlastBufferQueue.setSyncTransaction(null);
- // Apply the transactions that were sent to mergeWithNextTransaction since the
- // frame didn't draw on this vsync. It's possible the frame will draw later, but
- // it's better to not be sync than to block on a frame that may never come.
- mBlastBufferQueue.applyPendingTransactions(mRtLastAttemptedDrawFrameNum);
- }
-
- Transaction tmpTransaction = new Transaction();
- tmpTransaction.merge(mRtBLASTSyncTransaction);
- mHandler.postAtFrontOfQueue(() -> {
- if (useBlastSync) {
- mSurfaceChangedTransaction.merge(tmpTransaction);
- if (blastSyncConsumer != null) {
- blastSyncConsumer.accept(mSurfaceChangedTransaction);
- }
- }
-
- if (reportNextDraw) {
- pendingDrawFinished();
- }
- });
- });
- return true;
- }
-
private void addFrameCommitCallbackIfNeeded() {
if (!isHardwareEnabled()) {
return;
@@ -4105,51 +4073,131 @@
});
}
- private void addFrameCallbackIfNeeded(boolean useBlastSync) {
+ private HardwareRenderer.FrameCommitCallback createFrameCommitCallbackForSync(
+ boolean useBlastSync, boolean reportNextDraw, Consumer<Transaction> blastSyncConsumer) {
+ return didProduceBuffer -> {
+ if (DEBUG_BLAST) {
+ Log.d(mTag, "Received frameCommittedCallback "
+ + " lastAttemptedDrawFrameNum=" + mRtLastAttemptedDrawFrameNum
+ + " didProduceBuffer=" + didProduceBuffer);
+ }
+
+ // If frame wasn't drawn, clear out the next transaction so it doesn't affect the next
+ // draw attempt. The next transaction and transaction complete callback were only set
+ // for the current draw attempt.
+ if (!didProduceBuffer) {
+ mBlastBufferQueue.setSyncTransaction(null);
+ // Apply the transactions that were sent to mergeWithNextTransaction since the
+ // frame didn't draw on this vsync. It's possible the frame will draw later, but
+ // it's better to not be sync than to block on a frame that may never come.
+ mBlastBufferQueue.applyPendingTransactions(mRtLastAttemptedDrawFrameNum);
+ }
+
+ Transaction tmpTransaction = new Transaction();
+ tmpTransaction.merge(mRtBLASTSyncTransaction);
+ // Post at front of queue so the buffer can be processed immediately and allow RT
+ // to continue processing new buffers. If RT tries to process buffers before the sync
+ // buffer is applied, the new buffers will not get acquired and could result in a
+ // deadlock. UI thread would wait on RT, but RT would be blocked waiting for a free
+ // buffer.
+ mHandler.postAtFrontOfQueue(() -> {
+ if (useBlastSync) {
+ mSurfaceChangedTransaction.merge(tmpTransaction);
+ if (blastSyncConsumer != null) {
+ blastSyncConsumer.accept(mSurfaceChangedTransaction);
+ }
+ }
+
+ if (reportNextDraw) {
+ pendingDrawFinished();
+ }
+ });
+ };
+ }
+
+ @Nullable
+ private FrameDrawingCallback createFrameDrawingCallbackIfNeeded(boolean useBlastSync,
+ boolean reportNextDraw) {
+ if (!isHardwareEnabled()) {
+ return null;
+ }
final boolean hasBlurUpdates = mBlurRegionAggregator.hasUpdates();
final boolean needsCallbackForBlur = hasBlurUpdates || mBlurRegionAggregator.hasRegions();
- if (!useBlastSync && !needsCallbackForBlur) {
- return;
+ if (!useBlastSync && !needsCallbackForBlur && !reportNextDraw) {
+ return null;
}
+ final Consumer<SurfaceControl.Transaction> blastSyncConsumer = mBLASTDrawConsumer;
+ mBLASTDrawConsumer = null;
+
if (DEBUG_BLAST) {
Log.d(mTag, "Creating frameDrawingCallback"
+ " nextDrawUseBlastSync=" + useBlastSync
- + " hasBlurUpdates=" + hasBlurUpdates);
+ + " reportNextDraw=" + reportNextDraw
+ + " hasBlurUpdates=" + hasBlurUpdates
+ + " hasBlastSyncConsumer=" + (blastSyncConsumer != null));
}
+
final BackgroundBlurDrawable.BlurRegion[] blurRegionsForFrame =
needsCallbackForBlur ? mBlurRegionAggregator.getBlurRegionsCopyForRT() : null;
// The callback will run on the render thread.
- HardwareRenderer.FrameDrawingCallback frameDrawingCallback = frame -> {
- if (DEBUG_BLAST) {
- Log.d(mTag, "Received frameDrawingCallback frameNum=" + frame + "."
- + " Creating transactionCompleteCallback=" + useBlastSync);
+ return new FrameDrawingCallback() {
+ @Override
+ public void onFrameDraw(long frame) {
}
- mRtLastAttemptedDrawFrameNum = frame;
+ @Override
+ public HardwareRenderer.FrameCommitCallback onFrameDraw(int syncResult, long frame) {
+ if (DEBUG_BLAST) {
+ Log.d(mTag,
+ "Received frameDrawingCallback syncResult=" + syncResult + " frameNum="
+ + frame + ".");
+ }
- if (needsCallbackForBlur) {
- mBlurRegionAggregator
- .dispatchBlurTransactionIfNeeded(frame, blurRegionsForFrame, hasBlurUpdates);
- }
+ mRtLastAttemptedDrawFrameNum = frame;
- if (mBlastBufferQueue == null) {
- return;
- }
+ if (needsCallbackForBlur) {
+ mBlurRegionAggregator.dispatchBlurTransactionIfNeeded(frame,
+ blurRegionsForFrame, hasBlurUpdates);
+ }
- if (useBlastSync) {
- // Frame callbacks will always occur after submitting draw requests and before
- // the draw actually occurs. This will ensure that we set the next transaction
- // for the frame that's about to get drawn and not on a previous frame that.
+ if (mBlastBufferQueue == null) {
+ return null;
+ }
- // We don't need to synchronize mRtBLASTSyncTransaction here since it's not
- // being modified and only sent to BlastBufferQueue.
- mBlastBufferQueue.setSyncTransaction(mRtBLASTSyncTransaction);
+ if (!useBlastSync && !reportNextDraw) {
+ return null;
+ }
+
+ // If the syncResults are SYNC_LOST_SURFACE_REWARD_IF_FOUND or
+ // SYNC_CONTEXT_IS_STOPPED it means nothing will draw. There's no need to set up
+ // any blast sync or commit callback, and the code should directly call
+ // pendingDrawFinished.
+ if ((syncResult
+ & (SYNC_LOST_SURFACE_REWARD_IF_FOUND | SYNC_CONTEXT_IS_STOPPED)) != 0) {
+ if (reportNextDraw) {
+ mHandler.postAtFrontOfQueue(() -> pendingDrawFinished());
+ }
+ return null;
+ }
+
+ if (DEBUG_BLAST) {
+ Log.d(mTag, "Setting up sync and frameCommitCallback");
+ }
+
+ if (useBlastSync) {
+ // Frame callbacks will always occur after submitting draw requests and before
+ // the draw actually occurs. This will ensure that we set the next transaction
+ // for the frame that's about to get drawn and not on a previous frame.
+ mBlastBufferQueue.setSyncTransaction(mRtBLASTSyncTransaction);
+ }
+
+ return createFrameCommitCallbackForSync(useBlastSync, reportNextDraw,
+ blastSyncConsumer);
}
};
- registerRtFrameCallback(frameDrawingCallback);
}
private void performDraw(boolean useBlastSync) {
@@ -4165,15 +4213,20 @@
mIsDrawing = true;
Trace.traceBegin(Trace.TRACE_TAG_VIEW, "draw");
- addFrameCallbackIfNeeded(useBlastSync);
+ FrameDrawingCallback frameDrawingCallback = createFrameDrawingCallbackIfNeeded(useBlastSync,
+ mReportNextDraw);
+ if (frameDrawingCallback != null) {
+ mAttachInfo.mThreadedRenderer.registerRtFrameCallback(frameDrawingCallback);
+ }
addFrameCommitCallbackIfNeeded();
- boolean usingAsyncReport = addFrameCompleteCallbackIfNeeded(useBlastSync, mReportNextDraw);
+ boolean usingAsyncReport = isHardwareEnabled() && (useBlastSync || mReportNextDraw);
try {
boolean canUseAsync = draw(fullRedrawNeeded);
if (usingAsyncReport && !canUseAsync) {
- mAttachInfo.mThreadedRenderer.setFrameCompleteCallback(null);
+ mAttachInfo.mThreadedRenderer.setFrameCallback(null);
usingAsyncReport = false;
+ mAttachInfo.mThreadedRenderer.unregisterRtFrameCallback(frameDrawingCallback);
}
} finally {
mIsDrawing = false;
@@ -7543,7 +7596,7 @@
// When a new focused view is selected, we consume the navigation key because
// navigation doesn't make much sense unless a view already has focus so
// the key's purpose is to set focus.
- if (isNavigationKey(event)) {
+ if (event.hasNoModifiers() && isNavigationKey(event)) {
return ensureTouchMode(false);
}
@@ -7843,13 +7896,7 @@
if (!useBLAST()) {
mSurface.copyFrom(mSurfaceControl);
} else {
- final Surface blastSurface = getOrCreateBLASTSurface();
- // If blastSurface == null that means it hasn't changed since the last time we
- // called. In this situation, avoid calling transferFrom as we would then
- // inc the generation ID and cause EGL resources to be recreated.
- if (blastSurface != null) {
- mSurface.transferFrom(blastSurface);
- }
+ updateBlastSurfaceIfNeeded();
}
if (mAttachInfo.mThreadedRenderer != null) {
mAttachInfo.mThreadedRenderer.setSurfaceControl(mSurfaceControl);
@@ -10399,6 +10446,23 @@
}
/**
+ * @hide
+ */
+ public void setDisplayDecoration(boolean displayDecoration) {
+ if (displayDecoration == mDisplayDecorationCached) return;
+
+ mDisplayDecorationCached = displayDecoration;
+
+ if (mSurfaceControl.isValid()) {
+ updateDisplayDecoration();
+ }
+ }
+
+ private void updateDisplayDecoration() {
+ mTransaction.setDisplayDecoration(mSurfaceControl, mDisplayDecorationCached).apply();
+ }
+
+ /**
* Sends a list of blur regions to SurfaceFlinger, tagged with a frame.
*
* @param regionCopy List of regions
diff --git a/core/java/android/view/WindowManager.java b/core/java/android/view/WindowManager.java
index aa6cb83..cd9f3eb6 100644
--- a/core/java/android/view/WindowManager.java
+++ b/core/java/android/view/WindowManager.java
@@ -105,6 +105,7 @@
import android.os.Build;
import android.os.Bundle;
import android.os.IBinder;
+import android.os.IInputConstants;
import android.os.Parcel;
import android.os.Parcelable;
import android.text.TextUtils;
@@ -120,6 +121,7 @@
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
+import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.Objects;
@@ -2755,7 +2757,7 @@
*
* <p>Applications that target {@link android.os.Build.VERSION_CODES#P} and later, this flag
* is ignored unless there is a focused view that returns {@code true} from
- * {@link View#isInEditMode()} when the window is focused.</p>
+ * {@link View#onCheckIsTextEditor()} when the window is focused.</p>
*/
public static final int SOFT_INPUT_STATE_VISIBLE = 4;
@@ -2765,7 +2767,7 @@
*
* <p>Applications that target {@link android.os.Build.VERSION_CODES#P} and later, this flag
* is ignored unless there is a focused view that returns {@code true} from
- * {@link View#isInEditMode()} when the window is focused.</p>
+ * {@link View#onCheckIsTextEditor()} when the window is focused.</p>
*/
public static final int SOFT_INPUT_STATE_ALWAYS_VISIBLE = 5;
@@ -3325,21 +3327,13 @@
public static final int LAYOUT_IN_DISPLAY_CUTOUT_MODE_ALWAYS = 3;
/**
- * When this window has focus, disable touch pad pointer gesture processing.
- * The window will receive raw position updates from the touch pad instead
- * of pointer movements and synthetic touch events.
- *
- * @hide
- */
- public static final int INPUT_FEATURE_DISABLE_POINTER_GESTURES = 0x00000001;
-
- /**
* Does not construct an input channel for this window. The channel will therefore
* be incapable of receiving input.
*
* @hide
*/
- public static final int INPUT_FEATURE_NO_INPUT_CHANNEL = 0x00000002;
+ public static final int INPUT_FEATURE_NO_INPUT_CHANNEL =
+ IInputConstants.InputFeature.NO_INPUT_CHANNEL;
/**
* When this window has focus, does not call user activity for all input events so
@@ -3352,7 +3346,8 @@
* @hide
*/
@UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
- public static final int INPUT_FEATURE_DISABLE_USER_ACTIVITY = 0x00000004;
+ public static final int INPUT_FEATURE_DISABLE_USER_ACTIVITY =
+ IInputConstants.InputFeature.DISABLE_USER_ACTIVITY;
/**
* An input spy window. This window will receive all pointer events within its touchable
@@ -3361,7 +3356,25 @@
* event's coordinates.
* @hide
*/
- public static final int INPUT_FEATURE_SPY = 0x00000020;
+ public static final int INPUT_FEATURE_SPY =
+ IInputConstants.InputFeature.SPY;
+
+ /**
+ * When used with the window flag {@link #FLAG_NOT_TOUCHABLE}, this window will continue
+ * to receive events from a stylus device within its touchable region. All other pointer
+ * events, such as from a mouse or touchscreen, will be dispatched to the windows behind it.
+ *
+ * This input feature has no effect when the window flag {@link #FLAG_NOT_TOUCHABLE} is
+ * not set.
+ *
+ * The window must be a trusted overlay to use this input feature.
+ *
+ * @see #FLAG_NOT_TOUCHABLE
+ *
+ * @hide
+ */
+ public static final int INPUT_FEATURE_INTERCEPTS_STYLUS =
+ IInputConstants.InputFeature.INTERCEPTS_STYLUS;
/**
* An internal annotation for flags that can be specified to {@link #inputFeatures}.
@@ -3370,18 +3383,20 @@
*/
@Retention(RetentionPolicy.SOURCE)
@IntDef(flag = true, prefix = { "INPUT_FEATURE_" }, value = {
- INPUT_FEATURE_DISABLE_POINTER_GESTURES,
INPUT_FEATURE_NO_INPUT_CHANNEL,
INPUT_FEATURE_DISABLE_USER_ACTIVITY,
+ INPUT_FEATURE_SPY,
+ INPUT_FEATURE_INTERCEPTS_STYLUS,
})
public @interface InputFeatureFlags {}
/**
* Control special features of the input subsystem.
*
- * @see #INPUT_FEATURE_DISABLE_POINTER_GESTURES
* @see #INPUT_FEATURE_NO_INPUT_CHANNEL
* @see #INPUT_FEATURE_DISABLE_USER_ACTIVITY
+ * @see #INPUT_FEATURE_SPY
+ * @see #INPUT_FEATURE_INTERCEPTS_STYLUS
* @hide
*/
@InputFeatureFlags
@@ -4485,7 +4500,7 @@
sb.append(hasSystemUiListeners);
}
if (inputFeatures != 0) {
- sb.append(" if=").append(inputFeatureToString(inputFeatures));
+ sb.append(" if=").append(inputFeaturesToString(inputFeatures));
}
if (userActivityTimeout >= 0) {
sb.append(" userActivityTimeout=").append(userActivityTimeout);
@@ -4787,17 +4802,28 @@
}
}
- private static String inputFeatureToString(int inputFeature) {
- switch (inputFeature) {
- case INPUT_FEATURE_DISABLE_POINTER_GESTURES:
- return "DISABLE_POINTER_GESTURES";
- case INPUT_FEATURE_NO_INPUT_CHANNEL:
- return "NO_INPUT_CHANNEL";
- case INPUT_FEATURE_DISABLE_USER_ACTIVITY:
- return "DISABLE_USER_ACTIVITY";
- default:
- return Integer.toString(inputFeature);
+ private static String inputFeaturesToString(int inputFeatures) {
+ final List<String> features = new ArrayList<>();
+ if ((inputFeatures & INPUT_FEATURE_NO_INPUT_CHANNEL) != 0) {
+ inputFeatures &= ~INPUT_FEATURE_NO_INPUT_CHANNEL;
+ features.add("INPUT_FEATURE_NO_INPUT_CHANNEL");
}
+ if ((inputFeatures & INPUT_FEATURE_DISABLE_USER_ACTIVITY) != 0) {
+ inputFeatures &= ~INPUT_FEATURE_DISABLE_USER_ACTIVITY;
+ features.add("INPUT_FEATURE_DISABLE_USER_ACTIVITY");
+ }
+ if ((inputFeatures & INPUT_FEATURE_SPY) != 0) {
+ inputFeatures &= ~INPUT_FEATURE_SPY;
+ features.add("INPUT_FEATURE_SPY");
+ }
+ if ((inputFeatures & INPUT_FEATURE_INTERCEPTS_STYLUS) != 0) {
+ inputFeatures &= ~INPUT_FEATURE_INTERCEPTS_STYLUS;
+ features.add("INPUT_FEATURE_INTERCEPTS_STYLUS");
+ }
+ if (inputFeatures != 0) {
+ features.add(Integer.toHexString(inputFeatures));
+ }
+ return String.join(" | ", features);
}
/**
diff --git a/core/java/android/view/accessibility/AccessibilityEvent.java b/core/java/android/view/accessibility/AccessibilityEvent.java
index 83712b4..a427ab8 100644
--- a/core/java/android/view/accessibility/AccessibilityEvent.java
+++ b/core/java/android/view/accessibility/AccessibilityEvent.java
@@ -24,7 +24,6 @@
import android.os.Parcelable;
import android.text.TextUtils;
import android.util.Log;
-import android.util.Pools.SynchronizedPool;
import com.android.internal.util.BitUtils;
@@ -806,10 +805,6 @@
*/
public static final int TYPES_ALL_MASK = 0xFFFFFFFF;
- private static final int MAX_POOL_SIZE = 10;
- private static final SynchronizedPool<AccessibilityEvent> sPool =
- new SynchronizedPool<>(MAX_POOL_SIZE);
-
@UnsupportedAppUsage
private @EventType int mEventType;
private CharSequence mPackageName;
@@ -1170,7 +1165,7 @@
*/
public static AccessibilityEvent obtainWindowsChangedEvent(
int windowId, int windowChangeTypes) {
- final AccessibilityEvent event = AccessibilityEvent.obtain(TYPE_WINDOWS_CHANGED);
+ final AccessibilityEvent event = new AccessibilityEvent(TYPE_WINDOWS_CHANGED);
event.setWindowId(windowId);
event.setWindowChanges(windowChangeTypes);
event.setImportantForAccessibility(true);
@@ -1178,69 +1173,58 @@
}
/**
- * Returns a cached instance if such is available or a new one is
- * instantiated with its type property set.
+ * Instantiates a new AccessibilityEvent instance with its type property set.
*
- * <p>In most situations object pooling is not beneficial. Create a new instance using the
- * constructor {@link #AccessibilityEvent(int)} instead.
- *
+ * @deprecated Object pooling has been discontinued. Create a new instance using the
+ * constructor {@link #AccessibilityEvent()} instead.
* @param eventType The event type.
* @return An instance.
*/
+ @Deprecated
public static AccessibilityEvent obtain(int eventType) {
- AccessibilityEvent event = AccessibilityEvent.obtain();
+ AccessibilityEvent event = new AccessibilityEvent();
event.setEventType(eventType);
return event;
}
/**
- * Returns a cached instance if such is available or a new one is
- * created. The returned instance is initialized from the given
+ * Instantiates a new AccessibilityEvent instance.
+ * The returned instance is initialized from the given
* <code>event</code>.
*
- * <p>In most situations object pooling is not beneficial. Create a new instance using the
- * constructor {@link #AccessibilityEvent(AccessibilityEvent)} instead.
- *
+ * @deprecated Object pooling has been discontinued. Create a new instance using the
+ * constructor {@link #AccessibilityEvent()} instead.
* @param event The other event.
* @return An instance.
*/
+ @Deprecated
public static AccessibilityEvent obtain(AccessibilityEvent event) {
- AccessibilityEvent eventClone = AccessibilityEvent.obtain();
+ AccessibilityEvent eventClone = new AccessibilityEvent();
eventClone.init(event);
return eventClone;
}
/**
- * Returns a cached instance if such is available or a new one is
- * instantiated.
+ * Instantiates a new AccessibilityEvent instance.
*
- * <p>In most situations object pooling is not beneficial. Create a new instance using the
+ * @deprecated Object pooling has been discontinued. Create a new instance using the
* constructor {@link #AccessibilityEvent()} instead.
- *
* @return An instance.
*/
+ @Deprecated
public static AccessibilityEvent obtain() {
- AccessibilityEvent event = sPool.acquire();
- if (event == null) event = new AccessibilityEvent();
- if (DEBUG_ORIGIN) event.originStackTrace = Thread.currentThread().getStackTrace();
- return event;
+ return new AccessibilityEvent();
}
/**
- * Recycles an instance back to be reused.
- * <p>
- * <b>Note: You must not touch the object after calling this function.</b>
- * </p>
+ * Previously would recycle an instance back to be reused.
*
- * <p>In most situations object pooling is not beneficial, and recycling is not necessary.
- *
- * @throws IllegalStateException If the event is already recycled.
+ * @deprecated Object pooling has been discontinued. Calling this function now will have
+ * no effect.
*/
@Override
- public void recycle() {
- clear();
- sPool.release(this);
- }
+ @Deprecated
+ public void recycle() {}
/**
* Clears the state of this instance.
@@ -1260,7 +1244,6 @@
if (mRecords != null) {
while (!mRecords.isEmpty()) {
AccessibilityRecord record = mRecords.remove(0);
- record.recycle();
}
}
if (DEBUG_ORIGIN) originStackTrace = null;
@@ -1288,7 +1271,7 @@
if (recordCount > 0) {
mRecords = new ArrayList<>(recordCount);
for (int i = 0; i < recordCount; i++) {
- AccessibilityRecord record = AccessibilityRecord.obtain();
+ AccessibilityRecord record = new AccessibilityRecord();
readAccessibilityRecordFromParcel(record, parcel);
record.mConnectionId = mConnectionId;
mRecords.add(record);
@@ -1527,7 +1510,7 @@
public static final @android.annotation.NonNull Parcelable.Creator<AccessibilityEvent> CREATOR =
new Parcelable.Creator<AccessibilityEvent>() {
public AccessibilityEvent createFromParcel(Parcel parcel) {
- AccessibilityEvent event = AccessibilityEvent.obtain();
+ AccessibilityEvent event = new AccessibilityEvent();
event.initFromParcel(parcel);
return event;
}
diff --git a/core/java/android/view/accessibility/AccessibilityNodeInfo.java b/core/java/android/view/accessibility/AccessibilityNodeInfo.java
index 9511958..db7c663 100644
--- a/core/java/android/view/accessibility/AccessibilityNodeInfo.java
+++ b/core/java/android/view/accessibility/AccessibilityNodeInfo.java
@@ -50,7 +50,6 @@
import android.util.ArraySet;
import android.util.Log;
import android.util.LongArray;
-import android.util.Pools.SynchronizedPool;
import android.util.Size;
import android.util.TypedValue;
import android.view.SurfaceView;
@@ -68,7 +67,6 @@
import java.util.List;
import java.util.Map;
import java.util.Objects;
-import java.util.concurrent.atomic.AtomicInteger;
/**
* This class represents a node of the window content as well as actions that
@@ -723,9 +721,6 @@
*/
private static final int VIRTUAL_DESCENDANT_ID_SHIFT = 32;
- // TODO(b/129300068): Remove sNumInstancesInUse.
- private static AtomicInteger sNumInstancesInUse;
-
/**
* Gets the accessibility view id which identifies a View in the view three.
*
@@ -769,11 +764,6 @@
return (((long) virtualDescendantId) << VIRTUAL_DESCENDANT_ID_SHIFT) | accessibilityViewId;
}
- // Housekeeping.
- private static final int MAX_POOL_SIZE = 50;
- private static final SynchronizedPool<AccessibilityNodeInfo> sPool =
- new SynchronizedPool<>(MAX_POOL_SIZE);
-
private static final AccessibilityNodeInfo DEFAULT = new AccessibilityNodeInfo();
@UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
@@ -869,7 +859,7 @@
* @param info The other info.
*/
public AccessibilityNodeInfo(@NonNull AccessibilityNodeInfo info) {
- init(info, false /* usePoolingInfo */);
+ init(info);
}
/**
@@ -1009,13 +999,7 @@
if (refreshedInfo == null) {
return false;
}
- // Hard-to-reproduce bugs seem to be due to some tools recycling a node on another
- // thread. If that happens, the init will re-seal the node, which then is in a bad state
- // when it is obtained. Enforce sealing again before we init to fail when a node has been
- // recycled during a refresh to catch such errors earlier.
- enforceSealed();
- init(refreshedInfo, true /* usePoolingInfo */);
- refreshedInfo.recycle();
+ init(refreshedInfo);
return true;
}
@@ -3599,25 +3583,23 @@
* Returns a cached instance if such is available otherwise a new one
* and sets the source.
*
- * <p>In most situations object pooling is not beneficial. Create a new instance using the
+ * @deprecated Object pooling has been discontinued. Create a new instance using the
* constructor {@link #AccessibilityNodeInfo(View)} instead.
- *
* @param source The source view.
* @return An instance.
*
* @see #setSource(View)
*/
+ @Deprecated
public static AccessibilityNodeInfo obtain(View source) {
- AccessibilityNodeInfo info = AccessibilityNodeInfo.obtain();
- info.setSource(source);
- return info;
+ return new AccessibilityNodeInfo(source);
}
/**
* Returns a cached instance if such is available otherwise a new one
* and sets the source.
*
- * <p>In most situations object pooling is not beneficial. Create a new instance using the
+ * @deprecated Object pooling has been discontinued. Create a new instance using the
* constructor {@link #AccessibilityNodeInfo(View, int)} instead.
*
* @param root The root of the virtual subtree.
@@ -3626,71 +3608,45 @@
*
* @see #setSource(View, int)
*/
+ @Deprecated
public static AccessibilityNodeInfo obtain(View root, int virtualDescendantId) {
- AccessibilityNodeInfo info = AccessibilityNodeInfo.obtain();
- info.setSource(root, virtualDescendantId);
- return info;
+ return new AccessibilityNodeInfo(root, virtualDescendantId);
}
/**
- * Returns a cached instance if such is available otherwise a new one.
+ * Instantiates a new AccessibilityNodeInfo.
*
- * <p>In most situations object pooling is not beneficial. Create a new instance using the
+ * @deprecated Object pooling has been discontinued. Create a new instance using the
* constructor {@link #AccessibilityNodeInfo()} instead.
- *
* @return An instance.
*/
+ @Deprecated
public static AccessibilityNodeInfo obtain() {
- AccessibilityNodeInfo info = sPool.acquire();
- if (sNumInstancesInUse != null) {
- sNumInstancesInUse.incrementAndGet();
- }
- return (info != null) ? info : new AccessibilityNodeInfo();
+ return new AccessibilityNodeInfo();
}
/**
- * Returns a cached instance if such is available or a new one is
- * create. The returned instance is initialized from the given
+ * Instantiates a new AccessibilityNodeInfo initialized from the given
* <code>info</code>.
*
- * <p>In most situations object pooling is not beneficial. Create a new instance using the
+ * @deprecated Object pooling has been discontinued. Create a new instance using the
* constructor {@link #AccessibilityNodeInfo(AccessibilityNodeInfo)} instead.
- *
* @param info The other info.
* @return An instance.
*/
+ @Deprecated
public static AccessibilityNodeInfo obtain(AccessibilityNodeInfo info) {
- AccessibilityNodeInfo infoClone = AccessibilityNodeInfo.obtain();
- infoClone.init(info, true /* usePoolingInfo */);
- return infoClone;
+ return new AccessibilityNodeInfo(info);
}
/**
- * Return an instance back to be reused.
- * <p>
- * <strong>Note:</strong> You must not touch the object after calling this function.
+ * Would previously return an instance back to be reused.
*
- * <p>In most situations object pooling is not beneficial, and recycling is not necessary.
- *
- * @throws IllegalStateException If the info is already recycled.
+ * @deprecated Object pooling has been discontinued. Calling this function now will have
+ * no effect.
*/
- public void recycle() {
- clear();
- sPool.release(this);
- if (sNumInstancesInUse != null) {
- sNumInstancesInUse.decrementAndGet();
- }
- }
-
- /**
- * Specify a counter that will be incremented on obtain() and decremented on recycle()
- *
- * @hide
- */
- @TestApi
- public static void setNumInstancesInUseCounter(AtomicInteger counter) {
- sNumInstancesInUse = counter;
- }
+ @Deprecated
+ public void recycle() {}
/**
* {@inheritDoc}
@@ -3704,7 +3660,6 @@
writeToParcelNoRecycle(parcel, flags);
// Since instances of this class are fetched via synchronous i.e. blocking
// calls in IPCs we always recycle as soon as the instance is marshaled.
- recycle();
}
/** @hide */
@@ -4000,9 +3955,8 @@
* Initializes this instance from another one.
*
* @param other The other instance.
- * @param usePoolingInfos whether using pooled object internally or not
*/
- private void init(AccessibilityNodeInfo other, boolean usePoolingInfos) {
+ private void init(AccessibilityNodeInfo other) {
mSealed = other.mSealed;
mSourceNodeId = other.mSourceNodeId;
mParentNodeId = other.mParentNodeId;
@@ -4062,11 +4016,7 @@
mExtras = other.mExtras != null ? new Bundle(other.mExtras) : null;
- if (usePoolingInfos) {
- initPoolingInfos(other);
- } else {
- initCopyInfos(other);
- }
+ initCopyInfos(other);
final TouchDelegateInfo otherInfo = other.mTouchDelegateInfo;
mTouchDelegateInfo = (otherInfo != null)
@@ -4077,21 +4027,6 @@
mLeashedParentNodeId = other.mLeashedParentNodeId;
}
- private void initPoolingInfos(AccessibilityNodeInfo other) {
- if (mRangeInfo != null) mRangeInfo.recycle();
- mRangeInfo = (other.mRangeInfo != null)
- ? RangeInfo.obtain(other.mRangeInfo) : null;
- if (mCollectionInfo != null) mCollectionInfo.recycle();
- mCollectionInfo = (other.mCollectionInfo != null)
- ? CollectionInfo.obtain(other.mCollectionInfo) : null;
- if (mCollectionItemInfo != null) mCollectionItemInfo.recycle();
- mCollectionItemInfo = (other.mCollectionItemInfo != null)
- ? CollectionItemInfo.obtain(other.mCollectionItemInfo) : null;
- if (mExtraRenderingInfo != null) mExtraRenderingInfo.recycle();
- mExtraRenderingInfo = (other.mExtraRenderingInfo != null)
- ? ExtraRenderingInfo.obtain(other.mExtraRenderingInfo) : null;
- }
-
private void initCopyInfos(AccessibilityNodeInfo other) {
RangeInfo ri = other.mRangeInfo;
mRangeInfo = (ri == null) ? null
@@ -4205,27 +4140,24 @@
? parcel.readBundle()
: null;
- if (mRangeInfo != null) mRangeInfo.recycle();
mRangeInfo = isBitSet(nonDefaultFields, fieldIndex++)
- ? RangeInfo.obtain(
+ ? new RangeInfo(
parcel.readInt(),
parcel.readFloat(),
parcel.readFloat(),
parcel.readFloat())
: null;
- if (mCollectionInfo != null) mCollectionInfo.recycle();
mCollectionInfo = isBitSet(nonDefaultFields, fieldIndex++)
- ? CollectionInfo.obtain(
+ ? new CollectionInfo(
parcel.readInt(),
parcel.readInt(),
parcel.readInt() == 1,
parcel.readInt())
: null;
- if (mCollectionItemInfo != null) mCollectionItemInfo.recycle();
mCollectionItemInfo = isBitSet(nonDefaultFields, fieldIndex++)
- ? CollectionItemInfo.obtain(
+ ? new CollectionItemInfo(
parcel.readString(),
parcel.readInt(),
parcel.readInt(),
@@ -4241,8 +4173,7 @@
}
if (isBitSet(nonDefaultFields, fieldIndex++)) {
- if (mExtraRenderingInfo != null) mExtraRenderingInfo.recycle();
- mExtraRenderingInfo = ExtraRenderingInfo.obtain();
+ mExtraRenderingInfo = new ExtraRenderingInfo(null);
mExtraRenderingInfo.mLayoutSize = (Size) parcel.readValue(null);
mExtraRenderingInfo.mTextSizeInPx = parcel.readFloat();
mExtraRenderingInfo.mTextSizeUnit = parcel.readInt();
@@ -4265,7 +4196,7 @@
* Clears the state of this instance.
*/
private void clear() {
- init(DEFAULT, true /* usePoolingInfo */);
+ init(DEFAULT);
}
private static boolean isDefaultStandardAction(AccessibilityAction action) {
@@ -5235,7 +5166,6 @@
* handled by the {@link AccessibilityNodeInfo} to which this object is attached.
*/
public static final class RangeInfo {
- private static final int MAX_POOL_SIZE = 10;
/** Range type: integer. */
public static final int RANGE_TYPE_INT = 0;
@@ -5244,35 +5174,16 @@
/** Range type: percent with values from zero to one hundred. */
public static final int RANGE_TYPE_PERCENT = 2;
- private static final SynchronizedPool<RangeInfo> sPool =
- new SynchronizedPool<AccessibilityNodeInfo.RangeInfo>(MAX_POOL_SIZE);
-
private int mType;
private float mMin;
private float mMax;
private float mCurrent;
-
/**
- * Obtains a pooled instance that is a clone of another one.
+ * Instantiates a new RangeInfo.
*
- * <p>In most situations object pooling is not beneficial. Create a new instance using the
- * constructor {@link AccessibilityNodeInfo.RangeInfo#RangeInfo(int,
- * float, float, float)} instead.
- *
- * @param other The instance to clone.
- *
- * @hide
- */
- public static RangeInfo obtain(RangeInfo other) {
- return obtain(other.mType, other.mMin, other.mMax, other.mCurrent);
- }
-
- /**
- * Obtains a pooled instance.
- *
- * <p>In most situations object pooling is not beneficial. Create a new instance using the
- * constructor {@link AccessibilityNodeInfo.RangeInfo#RangeInfo(int,
- * float, float, float)} instead.
+ * @deprecated Object pooling has been discontinued. Create a new instance using the
+ * constructor {@link AccessibilityNodeInfo.RangeInfo#RangeInfo(int, float, float,
+ * float)} instead.
*
* @param type The type of the range.
* @param min The minimum value. Use {@code Float.NEGATIVE_INFINITY} if the range has no
@@ -5281,17 +5192,9 @@
* maximum.
* @param current The current value.
*/
+ @Deprecated
public static RangeInfo obtain(int type, float min, float max, float current) {
- RangeInfo info = sPool.acquire();
- if (info == null) {
- return new RangeInfo(type, min, max, current);
- }
-
- info.mType = type;
- info.mMin = min;
- info.mMax = max;
- info.mCurrent = current;
- return info;
+ return new RangeInfo(type, min, max, current);
}
/**
@@ -5354,12 +5257,11 @@
/**
* Recycles this instance.
*
- * <p>In most situations object pooling is not beneficial, and recycling is not necessary.
+ * @deprecated Object pooling has been discontinued. Calling this function now will have
+ * no effect.
*/
- void recycle() {
- clear();
- sPool.release(this);
- }
+ @Deprecated
+ void recycle() {}
private void clear() {
mType = 0;
@@ -5392,20 +5294,15 @@
/** Selection mode where multiple items may be selected. */
public static final int SELECTION_MODE_MULTIPLE = 2;
- private static final int MAX_POOL_SIZE = 20;
-
- private static final SynchronizedPool<CollectionInfo> sPool =
- new SynchronizedPool<>(MAX_POOL_SIZE);
-
private int mRowCount;
private int mColumnCount;
private boolean mHierarchical;
private int mSelectionMode;
/**
- * Obtains a pooled instance that is a clone of another one.
+ * Instantiates a CollectionInfo that is a clone of another one.
*
- * <p>In most situations object pooling is not beneficial. Create a new instance using the
+ * @deprecated Object pooling has been discontinued. Create a new instance using the
* constructor {@link
* AccessibilityNodeInfo.CollectionInfo#CollectionInfo} instead.
*
@@ -5413,14 +5310,14 @@
* @hide
*/
public static CollectionInfo obtain(CollectionInfo other) {
- return CollectionInfo.obtain(other.mRowCount, other.mColumnCount, other.mHierarchical,
+ return new CollectionInfo(other.mRowCount, other.mColumnCount, other.mHierarchical,
other.mSelectionMode);
}
/**
* Obtains a pooled instance.
*
- * <p>In most situations object pooling is not beneficial. Create a new instance using the
+ * @deprecated Object pooling has been discontinued. Create a new instance using the
* constructor {@link
* AccessibilityNodeInfo.CollectionInfo#CollectionInfo(int, int,
* boolean)} instead.
@@ -5431,13 +5328,13 @@
*/
public static CollectionInfo obtain(int rowCount, int columnCount,
boolean hierarchical) {
- return obtain(rowCount, columnCount, hierarchical, SELECTION_MODE_NONE);
+ return new CollectionInfo(rowCount, columnCount, hierarchical, SELECTION_MODE_NONE);
}
/**
* Obtains a pooled instance.
*
- * <p>In most situations object pooling is not beneficial. Create a new instance using the
+ * @deprecated Object pooling has been discontinued. Create a new instance using the
* constructor {@link
* AccessibilityNodeInfo.CollectionInfo#CollectionInfo(int, int,
* boolean, int)} instead.
@@ -5454,16 +5351,7 @@
*/
public static CollectionInfo obtain(int rowCount, int columnCount,
boolean hierarchical, int selectionMode) {
- final CollectionInfo info = sPool.acquire();
- if (info == null) {
- return new CollectionInfo(rowCount, columnCount, hierarchical, selectionMode);
- }
-
- info.mRowCount = rowCount;
- info.mColumnCount = columnCount;
- info.mHierarchical = hierarchical;
- info.mSelectionMode = selectionMode;
- return info;
+ return new CollectionInfo(rowCount, columnCount, hierarchical, selectionMode);
}
/**
@@ -5535,14 +5423,13 @@
}
/**
- * Recycles this instance.
+ * Previously would recycle this instance.
*
- * <p>In most situations object pooling is not beneficial, and recycling is not necessary.
+ * @deprecated Object pooling has been discontinued. Calling this function now will have
+ * no effect.
*/
- void recycle() {
- clear();
- sPool.release(this);
- }
+ @Deprecated
+ void recycle() {}
private void clear() {
mRowCount = 0;
@@ -5566,15 +5453,10 @@
* </p>
*/
public static final class CollectionItemInfo {
- private static final int MAX_POOL_SIZE = 20;
-
- private static final SynchronizedPool<CollectionItemInfo> sPool =
- new SynchronizedPool<>(MAX_POOL_SIZE);
-
/**
- * Obtains a pooled instance that is a clone of another one.
+ * Instantiates a CollectionItemInfo that is a clone of another one.
*
- * <p>In most situations object pooling is not beneficial. Create a new instance using the
+ * @deprecated Object pooling has been discontinued. Create a new instance using the
* constructor {@link
* AccessibilityNodeInfo.CollectionItemInfo#CollectionItemInfo}
* instead.
@@ -5582,20 +5464,20 @@
* @param other The instance to clone.
* @hide
*/
+ @Deprecated
public static CollectionItemInfo obtain(CollectionItemInfo other) {
- return CollectionItemInfo.obtain(other.mRowTitle, other.mRowIndex, other.mRowSpan,
- other.mColumnTitle, other.mColumnIndex, other.mColumnSpan, other.mHeading,
- other.mSelected);
+ return new CollectionItemInfo(other.mRowTitle, other.mRowIndex, other.mRowSpan,
+ other.mColumnTitle, other.mColumnIndex, other.mColumnSpan, other.mHeading,
+ other.mSelected);
}
/**
- * Obtains a pooled instance.
+ * Instantiates a new CollectionItemInfo.
*
- * <p>In most situations object pooling is not beneficial. Create a new instance using the
+ * @deprecated Object pooling has been discontinued. Create a new instance using the
* constructor {@link
* AccessibilityNodeInfo.CollectionItemInfo#CollectionItemInfo(int,
* int, int, int, boolean)} instead.
- *
* @param rowIndex The row index at which the item is located.
* @param rowSpan The number of rows the item spans.
* @param columnIndex The column index at which the item is located.
@@ -5603,37 +5485,39 @@
* @param heading Whether the item is a heading. (Prefer
* {@link AccessibilityNodeInfo#setHeading(boolean)}).
*/
+ @Deprecated
public static CollectionItemInfo obtain(int rowIndex, int rowSpan,
int columnIndex, int columnSpan, boolean heading) {
- return obtain(rowIndex, rowSpan, columnIndex, columnSpan, heading, false);
+ return new CollectionItemInfo(rowIndex, rowSpan, columnIndex, columnSpan, heading,
+ false);
}
/**
- * Obtains a pooled instance.
+ * Instantiates a new CollectionItemInfo.
*
- * <p>In most situations object pooling is not beneficial. Creates a new instance using the
+ * @deprecated Object pooling has been discontinued. Create a new instance using the
* constructor {@link
* AccessibilityNodeInfo.CollectionItemInfo#CollectionItemInfo(int,
- * int, int, int, boolean, boolean)} instead.
- *
+ * int, int, int, boolean)} instead.
* @param rowIndex The row index at which the item is located.
* @param rowSpan The number of rows the item spans.
* @param columnIndex The column index at which the item is located.
* @param columnSpan The number of columns the item spans.
* @param heading Whether the item is a heading. (Prefer
- * {@link AccessibilityNodeInfo#setHeading(boolean)})
+ * {@link AccessibilityNodeInfo#setHeading(boolean)}).
* @param selected Whether the item is selected.
*/
+ @Deprecated
public static CollectionItemInfo obtain(int rowIndex, int rowSpan,
int columnIndex, int columnSpan, boolean heading, boolean selected) {
- return obtain(null, rowIndex, rowSpan, null, columnIndex,
- columnSpan, heading, selected);
+ return new CollectionItemInfo(rowIndex, rowSpan, columnIndex, columnSpan, heading,
+ selected);
}
/**
- * Obtains a pooled instance.
+ * Instantiates a new CollectionItemInfo.
*
- * <p>In most situations object pooling is not beneficial. Creates a new instance using the
+ * @deprecated Object pooling has been discontinued. Creates a new instance using the
* constructor {@link
* AccessibilityNodeInfo.CollectionItemInfo#CollectionItemInfo(int,
* int, int, int, boolean, boolean)} instead.
@@ -5648,25 +5532,13 @@
* {@link AccessibilityNodeInfo#setHeading(boolean)})
* @param selected Whether the item is selected.
*/
+ @Deprecated
@NonNull
public static CollectionItemInfo obtain(@Nullable String rowTitle, int rowIndex,
int rowSpan, @Nullable String columnTitle, int columnIndex, int columnSpan,
boolean heading, boolean selected) {
- final CollectionItemInfo info = sPool.acquire();
- if (info == null) {
- return new CollectionItemInfo(rowTitle, rowIndex, rowSpan, columnTitle,
- columnIndex, columnSpan, heading, selected);
- }
-
- info.mRowIndex = rowIndex;
- info.mRowSpan = rowSpan;
- info.mColumnIndex = columnIndex;
- info.mColumnSpan = columnSpan;
- info.mHeading = heading;
- info.mSelected = selected;
- info.mRowTitle = rowTitle;
- info.mColumnTitle = columnTitle;
- return info;
+ return new CollectionItemInfo(rowTitle, rowIndex, rowSpan, columnTitle, columnIndex,
+ columnSpan, heading, selected);
}
private boolean mHeading;
@@ -5817,12 +5689,11 @@
/**
* Recycles this instance.
*
- * <p>In most situations object pooling is not beneficial, and recycling is not necessary.
+ * @deprecated Object pooling has been discontinued. Calling this function now will have
+ * no effect.
*/
- void recycle() {
- clear();
- sPool.release(this);
- }
+ @Deprecated
+ void recycle() {}
private void clear() {
mColumnIndex = 0;
@@ -6151,34 +6022,34 @@
*/
public static final class ExtraRenderingInfo {
private static final int UNDEFINED_VALUE = -1;
- private static final int MAX_POOL_SIZE = 20;
- private static final SynchronizedPool<ExtraRenderingInfo> sPool =
- new SynchronizedPool<>(MAX_POOL_SIZE);
private Size mLayoutSize;
private float mTextSizeInPx = UNDEFINED_VALUE;
private int mTextSizeUnit = UNDEFINED_VALUE;
/**
- * Obtains a pooled instance.
+ * Instantiates an ExtraRenderingInfo, by copying an existing one.
+ *
* @hide
+ * @deprecated Object pooling has been discontinued. Create a new instance using the
+ * constructor {@link #ExtraRenderingInfo(ExtraRenderingInfo)} instead.
*/
+ @Deprecated
@NonNull
public static ExtraRenderingInfo obtain() {
- final ExtraRenderingInfo info = sPool.acquire();
- if (info == null) {
- return new ExtraRenderingInfo(null);
- }
- return info;
+ return new ExtraRenderingInfo(null);
}
- /** Obtains a pooled instance that is a clone of another one. */
+ /**
+ * Instantiates an ExtraRenderingInfo, by copying an existing one.
+ *
+ * @deprecated Object pooling has been discontinued. Create a new instance using the
+ * constructor {@link #ExtraRenderingInfo(ExtraRenderingInfo)} instead.
+ * @param other
+ */
+ @Deprecated
private static ExtraRenderingInfo obtain(ExtraRenderingInfo other) {
- ExtraRenderingInfo extraRenderingInfo = ExtraRenderingInfo.obtain();
- extraRenderingInfo.mLayoutSize = other.mLayoutSize;
- extraRenderingInfo.mTextSizeInPx = other.mTextSizeInPx;
- extraRenderingInfo.mTextSizeUnit = other.mTextSizeUnit;
- return extraRenderingInfo;
+ return new ExtraRenderingInfo(other);
}
/**
@@ -6268,14 +6139,13 @@
}
/**
- * Recycles this instance.
+ * Previously would recycle this instance.
*
- * <p>In most situations object pooling is not beneficial, and recycling is not necessary.
+ * @deprecated Object pooling has been discontinued. Calling this function now will have
+ * no effect.
*/
- void recycle() {
- clear();
- sPool.release(this);
- }
+ @Deprecated
+ void recycle() {}
private void clear() {
mLayoutSize = null;
@@ -6291,7 +6161,7 @@
new Parcelable.Creator<AccessibilityNodeInfo>() {
@Override
public AccessibilityNodeInfo createFromParcel(Parcel parcel) {
- AccessibilityNodeInfo info = AccessibilityNodeInfo.obtain();
+ AccessibilityNodeInfo info = new AccessibilityNodeInfo();
info.initFromParcel(parcel);
return info;
}
diff --git a/core/java/android/view/accessibility/AccessibilityRecord.java b/core/java/android/view/accessibility/AccessibilityRecord.java
index f26abb2..426a3f4 100644
--- a/core/java/android/view/accessibility/AccessibilityRecord.java
+++ b/core/java/android/view/accessibility/AccessibilityRecord.java
@@ -78,13 +78,6 @@
| AccessibilityNodeInfo.FLAG_PREFETCH_SIBLINGS
| AccessibilityNodeInfo.FLAG_PREFETCH_DESCENDANTS;
- // Housekeeping
- private static final int MAX_POOL_SIZE = 10;
- private static final Object sPoolLock = new Object();
- private static AccessibilityRecord sPool;
- private static int sPoolSize;
- private AccessibilityRecord mNext;
- private boolean mIsInPool;
@UnsupportedAppUsage
boolean mSealed;
@@ -821,15 +814,14 @@
}
/**
- * Returns a cached instance if such is available or a new one is
- * instantiated. The instance is initialized with data from the
+ * Instantiates a new record initialized with data from the
* given record.
*
- * <p>In most situations object pooling is not beneficial. Create a new instance using the
- * constructor {@link #AccessibilityRecord(AccessibilityRecord)} instead.
- *
+ * @deprecated Object pooling has been discontinued. Create a new instance using the
+ * constructor {@link #AccessibilityRecord()} instead.
* @return An instance.
*/
+ @Deprecated
public static AccessibilityRecord obtain(AccessibilityRecord record) {
AccessibilityRecord clone = AccessibilityRecord.obtain();
clone.init(record);
@@ -837,51 +829,25 @@
}
/**
- * Returns a cached instance if such is available or a new one is
- * instantiated.
+ * Instantiates a new record.
*
- * <p>In most situations object pooling is not beneficial. Create a new instance using the
+ * @deprecated Object pooling has been discontinued. Create a new instance using the
* constructor {@link #AccessibilityRecord()} instead.
- *
* @return An instance.
*/
+ @Deprecated
public static AccessibilityRecord obtain() {
- synchronized (sPoolLock) {
- if (sPool != null) {
- AccessibilityRecord record = sPool;
- sPool = sPool.mNext;
- sPoolSize--;
- record.mNext = null;
- record.mIsInPool = false;
- return record;
- }
- return new AccessibilityRecord();
- }
+ return new AccessibilityRecord();
}
/**
- * Return an instance back to be reused.
- * <p>
- * <strong>Note:</strong> You must not touch the object after calling this function.
+ * Would previously return an instance back to be reused.
*
- * <p>In most situations object pooling is not beneficial, and recycling is not necessary.
- *
- * @throws IllegalStateException If the record is already recycled.
+ * @deprecated Object pooling has been discontinued. Calling this function now will have
+ * no effect.
*/
- public void recycle() {
- if (mIsInPool) {
- throw new IllegalStateException("Record already recycled!");
- }
- clear();
- synchronized (sPoolLock) {
- if (sPoolSize <= MAX_POOL_SIZE) {
- mNext = sPool;
- sPool = this;
- mIsInPool = true;
- sPoolSize++;
- }
- }
- }
+ @Deprecated
+ public void recycle() { }
/**
* Initialize this record from another one.
diff --git a/core/java/android/view/inputmethod/InputMethodManager.java b/core/java/android/view/inputmethod/InputMethodManager.java
index 7566cea..0bb3cc2 100644
--- a/core/java/android/view/inputmethod/InputMethodManager.java
+++ b/core/java/android/view/inputmethod/InputMethodManager.java
@@ -86,6 +86,7 @@
import android.view.autofill.AutofillManager;
import com.android.internal.annotations.GuardedBy;
+import com.android.internal.inputmethod.DirectBootAwareness;
import com.android.internal.inputmethod.ImeTracing;
import com.android.internal.inputmethod.InputBindResult;
import com.android.internal.inputmethod.InputMethodDebug;
@@ -1234,6 +1235,26 @@
}
/**
+ * Returns the list of installed input methods for the specified user.
+ *
+ * @param userId user ID to query
+ * @param directBootAwareness {@code true} if caller want to query installed input methods list
+ * on user locked state.
+ * @return {@link List} of {@link InputMethodInfo}.
+ * @hide
+ */
+ @RequiresPermission(INTERACT_ACROSS_USERS_FULL)
+ @NonNull
+ public List<InputMethodInfo> getInputMethodListAsUser(@UserIdInt int userId,
+ @DirectBootAwareness int directBootAwareness) {
+ try {
+ return mService.getAwareLockedInputMethodList(userId, directBootAwareness);
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
+ }
+ }
+
+ /**
* Returns the list of enabled input methods.
*
* <p>On multi user environment, this API returns a result for the calling process user.</p>
diff --git a/core/java/android/widget/Toast.java b/core/java/android/widget/Toast.java
index 862829b..dbf3570 100644
--- a/core/java/android/widget/Toast.java
+++ b/core/java/android/widget/Toast.java
@@ -74,6 +74,9 @@
* <p>
* Note that toasts being sent from the background are rate limited, so avoid sending such toasts
* in quick succession.
+ * <p>
+ * Starting with Android 12 (API level 31), apps targeting Android 12 or newer will have
+ * their toasts limited to two lines.
*
* <div class="special reference">
* <h3>Developer Guides</h3>
diff --git a/core/java/com/android/internal/app/ChooserActivity.java b/core/java/com/android/internal/app/ChooserActivity.java
index bc3c2f5..025f711 100644
--- a/core/java/com/android/internal/app/ChooserActivity.java
+++ b/core/java/com/android/internal/app/ChooserActivity.java
@@ -190,7 +190,7 @@
* the handover intent.
* TODO: investigate whether the privileged query is necessary to determine the availability.
*/
- protected static final String EXTRA_IS_APP_PREDICTION_SERVICE_AVAILABLE =
+ public static final String EXTRA_IS_APP_PREDICTION_SERVICE_AVAILABLE =
"com.android.internal.app.ChooserActivity.EXTRA_IS_APP_PREDICTION_SERVICE_AVAILABLE";
/**
diff --git a/core/java/com/android/internal/inputmethod/DirectBootAwareness.java b/core/java/com/android/internal/inputmethod/DirectBootAwareness.java
new file mode 100644
index 0000000..51f914f
--- /dev/null
+++ b/core/java/com/android/internal/inputmethod/DirectBootAwareness.java
@@ -0,0 +1,47 @@
+/*
+ * 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.internal.inputmethod;
+
+
+import static java.lang.annotation.RetentionPolicy.SOURCE;
+
+import android.annotation.IntDef;
+
+import java.lang.annotation.Retention;
+
+/**
+ * Specifies the decided filtering mode regarding IMEs' DirectBoot awareness when querying IMEs.
+ */
+@Retention(SOURCE)
+@IntDef({DirectBootAwareness.AUTO, DirectBootAwareness.ANY})
+public @interface DirectBootAwareness {
+ /**
+ * The same semantics as {@link android.content.pm.PackageManager.MATCH_DIRECT_BOOT_AUTO}, that
+ * is, if the user to be queried is still locked, then only DirectBoot-aware IMEs will be
+ * matched. If the user to be queried is already unlocked, then IMEs will not be filtered out
+ * based on their DirectBoot awareness.
+ */
+ int AUTO = 0;
+ /**
+ * The same semantics as specifying <strong>both</strong>
+ * {@link android.content.pm.PackageManager.MATCH_DIRECT_BOOT_AWARE} and
+ * {@link android.content.pm.PackageManager.MATCH_DIRECT_BOOT_UNAWARE}, that is, IME will never
+ * be filtered out based on their DirectBoot awareness, no matter whether the user to be queried
+ * is still locked or already unlocked.
+ */
+ int ANY = 1;
+}
diff --git a/core/java/com/android/internal/os/BatteryUsageStatsProvider.java b/core/java/com/android/internal/os/BatteryUsageStatsProvider.java
index 88425be..c1111ec 100644
--- a/core/java/com/android/internal/os/BatteryUsageStatsProvider.java
+++ b/core/java/com/android/internal/os/BatteryUsageStatsProvider.java
@@ -21,11 +21,13 @@
import android.os.BatteryStats;
import android.os.BatteryUsageStats;
import android.os.BatteryUsageStatsQuery;
+import android.os.Parcel;
import android.os.SystemClock;
import android.os.UidBatteryConsumer;
import android.util.Log;
import android.util.SparseArray;
+import com.android.internal.annotations.GuardedBy;
import com.android.internal.annotations.VisibleForTesting;
import java.util.ArrayList;
@@ -133,9 +135,12 @@
*/
@VisibleForTesting
public BatteryUsageStats getBatteryUsageStats(BatteryUsageStatsQuery query) {
- return getBatteryUsageStats(query, currentTimeMillis());
+ synchronized (mStats) {
+ return getBatteryUsageStats(query, currentTimeMillis());
+ }
}
+ @GuardedBy("mStats")
private BatteryUsageStats getBatteryUsageStats(BatteryUsageStatsQuery query,
long currentTimeMs) {
if (query.getToTimestamp() == 0) {
@@ -145,6 +150,7 @@
}
}
+ @GuardedBy("mStats")
private BatteryUsageStats getCurrentBatteryUsageStats(BatteryUsageStatsQuery query,
long currentTimeMs) {
final long realtimeUs = elapsedRealtime() * 1000;
@@ -189,7 +195,12 @@
}
BatteryStatsImpl batteryStatsImpl = (BatteryStatsImpl) mStats;
- batteryUsageStatsBuilder.setBatteryHistory(batteryStatsImpl.mHistoryBuffer);
+
+ // Make a copy of battery history to avoid concurrent modification.
+ Parcel historyBuffer = Parcel.obtain();
+ historyBuffer.appendFrom(batteryStatsImpl.mHistoryBuffer, 0,
+ batteryStatsImpl.mHistoryBuffer.dataSize());
+ batteryUsageStatsBuilder.setBatteryHistory(historyBuffer);
}
return batteryUsageStatsBuilder.build();
diff --git a/core/java/com/android/internal/policy/IKeyguardService.aidl b/core/java/com/android/internal/policy/IKeyguardService.aidl
index 76aa7a0..36b7ee5 100644
--- a/core/java/com/android/internal/policy/IKeyguardService.aidl
+++ b/core/java/com/android/internal/policy/IKeyguardService.aidl
@@ -15,6 +15,7 @@
*/
package com.android.internal.policy;
+import android.content.Intent;
import com.android.internal.policy.IKeyguardDrawnCallback;
import com.android.internal.policy.IKeyguardDismissCallback;
import com.android.internal.policy.IKeyguardStateCallback;
@@ -113,7 +114,20 @@
/**
* Notifies the Keyguard that the power key was pressed while locked and launched Home rather
- * than putting the device to sleep or waking up.
+ * than putting the device to sleep or waking up. Note that it's called only if the device is
+ * interactive.
*/
void onShortPowerPressedGoHome();
+
+ /**
+ * Notifies the Keyguard that it needs to bring up a bouncer and then launch the intent as soon
+ * as user unlocks the watch.
+ */
+ void dismissKeyguardToLaunch(in Intent intentToLaunch);
+
+ /**
+ * Notifies the Keyguard that a key was pressed while locked so the Keyguard can handle it.
+ * Note that it's called only if the device is interactive.
+ */
+ void onSystemKeyPressed(int keycode);
}
diff --git a/core/java/com/android/internal/view/IInputMethodManager.aidl b/core/java/com/android/internal/view/IInputMethodManager.aidl
index 350ec33..2dc7c42c 100644
--- a/core/java/com/android/internal/view/IInputMethodManager.aidl
+++ b/core/java/com/android/internal/view/IInputMethodManager.aidl
@@ -35,6 +35,7 @@
// TODO: Use ParceledListSlice instead
List<InputMethodInfo> getInputMethodList(int userId);
+ List<InputMethodInfo> getAwareLockedInputMethodList(int userId, int directBootAwareness);
// TODO: Use ParceledListSlice instead
List<InputMethodInfo> getEnabledInputMethodList(int userId);
List<InputMethodSubtype> getEnabledInputMethodSubtypeList(in String imiId,
diff --git a/core/jni/android_graphics_BLASTBufferQueue.cpp b/core/jni/android_graphics_BLASTBufferQueue.cpp
index 78e5adc..55f1369 100644
--- a/core/jni/android_graphics_BLASTBufferQueue.cpp
+++ b/core/jni/android_graphics_BLASTBufferQueue.cpp
@@ -37,16 +37,6 @@
return reinterpret_cast<jlong>(queue.get());
}
-static jlong nativeCreateAndUpdate(JNIEnv* env, jclass clazz, jstring jName, jlong surfaceControl,
- jlong width, jlong height, jint format) {
- ScopedUtfChars name(env, jName);
- sp<BLASTBufferQueue> queue =
- new BLASTBufferQueue(name.c_str(), reinterpret_cast<SurfaceControl*>(surfaceControl),
- width, height, format);
- queue->incStrong((void*)nativeCreate);
- return reinterpret_cast<jlong>(queue.get());
-}
-
static void nativeDestroy(JNIEnv* env, jclass clazz, jlong ptr) {
sp<BLASTBufferQueue> queue = reinterpret_cast<BLASTBufferQueue*>(ptr);
queue->decStrong((void*)nativeCreate);
@@ -91,11 +81,15 @@
queue->applyPendingTransactions(frameNum);
}
+static bool nativeIsSameSurfaceControl(JNIEnv* env, jclass clazz, jlong ptr, jlong surfaceControl) {
+ sp<BLASTBufferQueue> queue = reinterpret_cast<BLASTBufferQueue*>(ptr);
+ return queue->isSameSurfaceControl(reinterpret_cast<SurfaceControl*>(surfaceControl));
+}
+
static const JNINativeMethod gMethods[] = {
/* name, signature, funcPtr */
// clang-format off
{"nativeCreate", "(Ljava/lang/String;)J", (void*)nativeCreate},
- {"nativeCreateAndUpdate", "(Ljava/lang/String;JJJI)J", (void*)nativeCreateAndUpdate},
{"nativeGetSurface", "(JZ)Landroid/view/Surface;", (void*)nativeGetSurface},
{"nativeDestroy", "(J)V", (void*)nativeDestroy},
{"nativeSetSyncTransaction", "(JJZ)V", (void*)nativeSetSyncTransaction},
@@ -103,6 +97,7 @@
{"nativeMergeWithNextTransaction", "(JJJ)V", (void*)nativeMergeWithNextTransaction},
{"nativeGetLastAcquiredFrameNum", "(J)J", (void*)nativeGetLastAcquiredFrameNum},
{"nativeApplyPendingTransactions", "(JJ)V", (void*)nativeApplyPendingTransactions},
+ {"nativeIsSameSurfaceControl", "(JJ)Z", (void*)nativeIsSameSurfaceControl},
// clang-format on
};
diff --git a/core/jni/android_media_AudioSystem.cpp b/core/jni/android_media_AudioSystem.cpp
index 0b0870d..3e2b258 100644
--- a/core/jni/android_media_AudioSystem.cpp
+++ b/core/jni/android_media_AudioSystem.cpp
@@ -2782,13 +2782,13 @@
static jint convertAudioDirectModeFromNative(audio_direct_mode_t directMode) {
jint result = DIRECT_NOT_SUPPORTED;
- if ((directMode | AUDIO_DIRECT_OFFLOAD_SUPPORTED) != AUDIO_DIRECT_NOT_SUPPORTED) {
+ if ((directMode & AUDIO_DIRECT_OFFLOAD_SUPPORTED) != AUDIO_DIRECT_NOT_SUPPORTED) {
result |= DIRECT_OFFLOAD_SUPPORTED;
}
- if ((directMode | AUDIO_DIRECT_OFFLOAD_GAPLESS_SUPPORTED) != AUDIO_DIRECT_NOT_SUPPORTED) {
+ if ((directMode & AUDIO_DIRECT_OFFLOAD_GAPLESS_SUPPORTED) != AUDIO_DIRECT_NOT_SUPPORTED) {
result |= DIRECT_OFFLOAD_GAPLESS_SUPPORTED;
}
- if ((directMode | AUDIO_DIRECT_BITSTREAM_SUPPORTED) != AUDIO_DIRECT_NOT_SUPPORTED) {
+ if ((directMode & AUDIO_DIRECT_BITSTREAM_SUPPORTED) != AUDIO_DIRECT_NOT_SUPPORTED) {
result |= DIRECT_BITSTREAM_SUPPORTED;
}
return result;
@@ -2799,7 +2799,7 @@
JNIAudioAttributeHelper::UniqueAaPtr paa = JNIAudioAttributeHelper::makeUnique();
jint jStatus = JNIAudioAttributeHelper::nativeFromJava(env, jaa, paa.get());
if (jStatus != (jint)AUDIO_JAVA_SUCCESS) {
- return AUDIO_DIRECT_NOT_SUPPORTED;
+ return DIRECT_NOT_SUPPORTED;
}
audio_config_t nConfig;
@@ -2809,7 +2809,7 @@
status_t status = AudioSystem::getDirectPlaybackSupport(paa.get(), &nConfig, &directMode);
if (status != NO_ERROR) {
ALOGW("%s native returned error %d", __func__, status);
- return AUDIO_DIRECT_NOT_SUPPORTED;
+ return DIRECT_NOT_SUPPORTED;
}
return convertAudioDirectModeFromNative(directMode);
}
diff --git a/core/proto/android/inputmethodservice/softinputwindow.proto b/core/proto/android/inputmethodservice/softinputwindow.proto
index 85b7d73..e0ba6bf 100644
--- a/core/proto/android/inputmethodservice/softinputwindow.proto
+++ b/core/proto/android/inputmethodservice/softinputwindow.proto
@@ -23,10 +23,10 @@
option java_multiple_files = true;
message SoftInputWindowProto {
- optional string name = 1;
- optional int32 window_type = 2;
- optional int32 gravity = 3;
- optional bool takes_focus = 4;
+ reserved 1; // name
+ reserved 2; // window_type
+ reserved 3; // gravity
+ reserved 4; // takes_focus
optional .android.graphics.RectProto bounds = 5;
optional int32 window_state = 6;
}
\ No newline at end of file
diff --git a/core/proto/android/providers/settings/secure.proto b/core/proto/android/providers/settings/secure.proto
index ba4a5b0..5090d8c 100644
--- a/core/proto/android/providers/settings/secure.proto
+++ b/core/proto/android/providers/settings/secure.proto
@@ -85,6 +85,7 @@
optional SettingProto accessibility_floating_menu_icon_type = 39 [ (android.privacy).dest = DEST_AUTOMATIC ];
optional SettingProto accessibility_floating_menu_opacity = 40 [ (android.privacy).dest = DEST_AUTOMATIC ];
optional SettingProto accessibility_floating_menu_fade_enabled = 41 [ (android.privacy).dest = DEST_AUTOMATIC ];
+ optional SettingProto odi_captions_volume_ui_enabled = 42 [ (android.privacy).dest = DEST_AUTOMATIC ];
}
optional Accessibility accessibility = 2;
diff --git a/core/res/res/values/attrs.xml b/core/res/res/values/attrs.xml
index bfc1c83..1705371 100644
--- a/core/res/res/values/attrs.xml
+++ b/core/res/res/values/attrs.xml
@@ -8440,6 +8440,8 @@
<!-- Component name of an activity that allows the user to modify
the settings for this dream. -->
<attr name="settingsActivity" />
+ <!-- A preview, in a drawable resource id, of what the Dream will look like. -->
+ <attr name="previewImage" format="reference" />
</declare-styleable>
<!-- Use <code>trust-agent</code> as the root tag of the XML resource that
diff --git a/core/res/res/values/attrs_manifest.xml b/core/res/res/values/attrs_manifest.xml
index 06f347f..db24475 100644
--- a/core/res/res/values/attrs_manifest.xml
+++ b/core/res/res/values/attrs_manifest.xml
@@ -3070,7 +3070,6 @@
<declare-styleable name="AndroidManifestMetaData"
parent="AndroidManifestApplication
AndroidManifestActivity
- AndroidManifestApexSystemService
AndroidManifestReceiver
AndroidManifestProvider
AndroidManifestService
@@ -3105,7 +3104,6 @@
<declare-styleable name="AndroidManifestProperty"
parent="AndroidManifestApplication
AndroidManifestActivity
- AndroidManifestApexSystemService
AndroidManifestReceiver
AndroidManifestProvider
AndroidManifestService">
diff --git a/core/res/res/values/config.xml b/core/res/res/values/config.xml
index bd0604e..7d8bcea 100644
--- a/core/res/res/values/config.xml
+++ b/core/res/res/values/config.xml
@@ -2897,6 +2897,10 @@
<!-- Make the IME killable by the lowmemorykiller by raising its oom_score_adj. -->
<bool name="config_killableInputMethods">false</bool>
+ <!-- Prevent the InputMethodManagerService from starting up the IME unless
+ the currently focused view is a text editor. -->
+ <bool name="config_preventImeStartupUnlessTextEditor">false</bool>
+
<!-- The list of classes that should be added to the notification ranking pipeline.
See {@link com.android.server.notification.NotificationSignalExtractor}
If you add a new extractor to this list make sure to update
diff --git a/core/res/res/values/public.xml b/core/res/res/values/public.xml
index bc127d9..1f560f4 100644
--- a/core/res/res/values/public.xml
+++ b/core/res/res/values/public.xml
@@ -3313,6 +3313,8 @@
<staging-public-group type="bool" first-id="0x01cf0000">
<!-- @hide @SystemApi -->
<public name="config_systemCaptionsServiceCallsEnabled" />
+ <!-- @hide @TestApi -->
+ <public name="config_preventImeStartupUnlessTextEditor" />
</staging-public-group>
<staging-public-group type="fraction" first-id="0x01ce0000">
diff --git a/core/res/res/values/symbols.xml b/core/res/res/values/symbols.xml
index 7687b93..ba4aa81 100644
--- a/core/res/res/values/symbols.xml
+++ b/core/res/res/values/symbols.xml
@@ -2250,6 +2250,7 @@
<java-symbol type="bool" name="config_autoResetAirplaneMode" />
<java-symbol type="string" name="config_notificationAccessConfirmationActivity" />
<java-symbol type="bool" name="config_killableInputMethods" />
+ <java-symbol type="bool" name="config_preventImeStartupUnlessTextEditor" />
<java-symbol type="layout" name="resolver_list" />
<java-symbol type="id" name="resolver_list" />
diff --git a/core/tests/coretests/src/android/app/activity/ActivityThreadTest.java b/core/tests/coretests/src/android/app/activity/ActivityThreadTest.java
index 5db6a3e..bfb2fd5 100644
--- a/core/tests/coretests/src/android/app/activity/ActivityThreadTest.java
+++ b/core/tests/coretests/src/android/app/activity/ActivityThreadTest.java
@@ -273,13 +273,14 @@
newerConfig.orientation = orientation == ORIENTATION_LANDSCAPE
? ORIENTATION_PORTRAIT : ORIENTATION_LANDSCAPE;
newerConfig.seq = seq + 2;
- final ActivityClientRecord r = getActivityClientRecord(activity);
- activityThread.updatePendingActivityConfiguration(r, newerConfig);
+ activityThread.updatePendingActivityConfiguration(activity.getActivityToken(),
+ newerConfig);
final Configuration olderConfig = new Configuration();
olderConfig.orientation = orientation;
olderConfig.seq = seq + 1;
+ final ActivityClientRecord r = getActivityClientRecord(activity);
activityThread.handleActivityConfigurationChanged(r, olderConfig, INVALID_DISPLAY);
assertEquals(numOfConfig, activity.mNumOfConfigChanges);
assertEquals(olderConfig.orientation, activity.mConfig.orientation);
@@ -504,7 +505,8 @@
? ORIENTATION_LANDSCAPE : ORIENTATION_PORTRAIT;
final ActivityClientRecord r = getActivityClientRecord(activity);
- activityThread.updatePendingActivityConfiguration(r, newActivityConfig);
+ activityThread.updatePendingActivityConfiguration(activity.getActivityToken(),
+ newActivityConfig);
activityThread.handleActivityConfigurationChanged(r, newActivityConfig,
INVALID_DISPLAY);
diff --git a/core/tests/coretests/src/android/net/NetworkPolicyTest.kt b/core/tests/coretests/src/android/net/NetworkPolicyTest.kt
index 121caef..3c8f90c 100644
--- a/core/tests/coretests/src/android/net/NetworkPolicyTest.kt
+++ b/core/tests/coretests/src/android/net/NetworkPolicyTest.kt
@@ -32,14 +32,14 @@
import kotlin.test.assertTrue
private const val TEST_IMSI1 = "TESTIMSI1"
-private const val TEST_SSID1 = "TESTISSID1"
+private const val TEST_WIFI_NETWORK_KEY1 = "TESTKEY1"
@RunWith(AndroidJUnit4::class)
class NetworkPolicyTest {
@Test
fun testTemplateBackupRestore() {
assertPolicyBackupRestore(createTestPolicyForTemplate(
- NetworkTemplate.buildTemplateWifi(TEST_SSID1)))
+ NetworkTemplate.buildTemplateWifi(TEST_WIFI_NETWORK_KEY1)))
assertPolicyBackupRestore(createTestPolicyForTemplate(
NetworkTemplate.buildTemplateMobileAll(TEST_IMSI1)))
assertPolicyBackupRestore(createTestPolicyForTemplate(
@@ -79,6 +79,6 @@
// Verify wifi template can be persistable if the Wifi Network Key is supplied.
assertTrue(NetworkPolicy.isTemplatePersistable(NetworkTemplate.Builder(MATCH_WIFI)
- .setWifiNetworkKey(TEST_SSID1).build()))
+ .setWifiNetworkKeys(setOf(TEST_WIFI_NETWORK_KEY1)).build()))
}
-}
\ No newline at end of file
+}
diff --git a/core/tests/coretests/src/android/view/accessibility/AccessibilityEventTest.java b/core/tests/coretests/src/android/view/accessibility/AccessibilityEventTest.java
index 2c8c385..6df9002 100644
--- a/core/tests/coretests/src/android/view/accessibility/AccessibilityEventTest.java
+++ b/core/tests/coretests/src/android/view/accessibility/AccessibilityEventTest.java
@@ -42,14 +42,14 @@
// and assertAccessibilityEventCleared
/** The number of properties of the {@link AccessibilityEvent} class. */
- private static final int A11Y_EVENT_NON_STATIC_FIELD_COUNT = 34;
+ private static final int A11Y_EVENT_NON_STATIC_FIELD_COUNT = 32;
// The number of fields tested in the corresponding CTS AccessibilityRecordTest:
// assertAccessibilityRecordCleared, fullyPopulateAccessibilityRecord,
// and assertEqualAccessibilityRecord
/** The number of properties of the {@link AccessibilityRecord} class. */
- private static final int A11Y_RECORD_NON_STATIC_FIELD_COUNT = 25;
+ private static final int A11Y_RECORD_NON_STATIC_FIELD_COUNT = 23;
@Test
public void testImportantForAccessibiity_getSetWorkAcrossParceling() {
diff --git a/core/tests/coretests/src/android/view/accessibility/AccessibilityManagerTest.java b/core/tests/coretests/src/android/view/accessibility/AccessibilityManagerTest.java
index 212fdca..bb1a3b18 100644
--- a/core/tests/coretests/src/android/view/accessibility/AccessibilityManagerTest.java
+++ b/core/tests/coretests/src/android/view/accessibility/AccessibilityManagerTest.java
@@ -17,7 +17,6 @@
package android.view.accessibility;
import static junit.framework.TestCase.assertFalse;
-import static junit.framework.TestCase.assertSame;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertTrue;
@@ -188,17 +187,6 @@
}
@Test
- public void testSendAccessibilityEvent_AccessibilityEnabled() throws Exception {
- AccessibilityEvent sentEvent = AccessibilityEvent.obtain(
- AccessibilityEvent.TYPE_ANNOUNCEMENT);
-
- AccessibilityManager manager = createManager(WITH_A11Y_ENABLED);
- manager.sendAccessibilityEvent(sentEvent);
-
- assertSame("The event should be recycled.", sentEvent, AccessibilityEvent.obtain());
- }
-
- @Test
public void testSendAccessibilityEvent_AccessibilityDisabled() throws Exception {
AccessibilityEvent sentEvent = AccessibilityEvent.obtain();
diff --git a/core/tests/coretests/src/android/view/accessibility/RecycleAccessibilityEventTest.java b/core/tests/coretests/src/android/view/accessibility/RecycleAccessibilityEventTest.java
deleted file mode 100644
index 11f4e3c..0000000
--- a/core/tests/coretests/src/android/view/accessibility/RecycleAccessibilityEventTest.java
+++ /dev/null
@@ -1,80 +0,0 @@
-/**
- * Copyright (C) 2009 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.view.accessibility;
-
-import androidx.test.filters.SmallTest;
-
-import junit.framework.TestCase;
-
-/**
- * This class exercises the caching and recycling of {@link AccessibilityEvent}s.
- */
-public class RecycleAccessibilityEventTest extends TestCase {
-
- private static final String CLASS_NAME = "foo.bar.baz.Test";
- private static final String PACKAGE_NAME = "foo.bar.baz";
- private static final String TEXT = "Some stuff";
-
- private static final String CONTENT_DESCRIPTION = "Content description";
- private static final int ITEM_COUNT = 10;
- private static final int CURRENT_ITEM_INDEX = 1;
-
- private static final int FROM_INDEX = 1;
- private static final int ADDED_COUNT = 2;
- private static final int REMOVED_COUNT = 1;
-
- /**
- * If an {@link AccessibilityEvent} is marshaled/unmarshaled correctly
- */
- @SmallTest
- public void testAccessibilityEventViewTextChangedType() {
- AccessibilityEvent first =
- AccessibilityEvent.obtain(AccessibilityEvent.TYPE_VIEW_TEXT_CHANGED);
- assertNotNull(first);
-
- first.setClassName(CLASS_NAME);
- first.setPackageName(PACKAGE_NAME);
- first.getText().add(TEXT);
- first.setFromIndex(FROM_INDEX);
- first.setAddedCount(ADDED_COUNT);
- first.setRemovedCount(REMOVED_COUNT);
- first.setChecked(true);
- first.setContentDescription(CONTENT_DESCRIPTION);
- first.setItemCount(ITEM_COUNT);
- first.setCurrentItemIndex(CURRENT_ITEM_INDEX);
- first.setEnabled(true);
- first.setPassword(true);
-
- first.recycle();
-
- assertNotNull(first);
- assertNull(first.getClassName());
- assertNull(first.getPackageName());
- assertEquals(0, first.getText().size());
- assertFalse(first.isChecked());
- assertNull(first.getContentDescription());
- assertEquals(-1, first.getItemCount());
- assertEquals(AccessibilityEvent.INVALID_POSITION, first.getCurrentItemIndex());
- assertFalse(first.isEnabled());
- assertFalse(first.isPassword());
- assertEquals(-1, first.getFromIndex());
- assertEquals(-1, first.getAddedCount());
- assertEquals(-1, first.getRemovedCount());
-
- // get another event from the pool (this must be the recycled first)
- AccessibilityEvent second = AccessibilityEvent.obtain();
- assertEquals(first, second);
- }
-}
diff --git a/core/tests/coretests/src/com/android/internal/app/ChooserActivityTest.java b/core/tests/coretests/src/com/android/internal/app/ChooserActivityTest.java
index c0ced6c..69ff7c6 100644
--- a/core/tests/coretests/src/com/android/internal/app/ChooserActivityTest.java
+++ b/core/tests/coretests/src/com/android/internal/app/ChooserActivityTest.java
@@ -180,6 +180,16 @@
return clientIntent;
}
+ /**
+ * Whether {@code #testIsAppPredictionServiceAvailable} should verify the behavior after
+ * changing the availability conditions at runtime. In the unbundled chooser, the availability
+ * is cached at start and will never be re-evaluated.
+ * TODO: remove when we no longer want to test the system's on-the-fly evaluation.
+ */
+ protected boolean shouldTestTogglingAppPredictionServiceAvailabilityAtRuntime() {
+ return true;
+ }
+
/* --------
* The code in this section is unorthodox and can be simplified/reverted when we no longer need
* to support the parallel chooser implementations.
@@ -784,7 +794,8 @@
assertThat(logger.get(1).atomId, is(FrameworkStatsLog.SHARESHEET_STARTED));
assertThat(logger.get(1).intent, is(Intent.ACTION_SEND));
assertThat(logger.get(1).mimeType, is("text/plain"));
- assertThat(logger.get(1).packageName, is("com.android.frameworks.coretests"));
+ assertThat(logger.get(1).packageName, is(
+ InstrumentationRegistry.getInstrumentation().getTargetContext().getPackageName()));
assertThat(logger.get(1).appProvidedApp, is(0));
assertThat(logger.get(1).appProvidedDirect, is(0));
assertThat(logger.get(1).isWorkprofile, is(false));
@@ -802,7 +813,7 @@
assertThat(logger.event(4).getId(),
is(ChooserActivityLogger.SharesheetStandardEvent.SHARESHEET_EXPANDED.getId()));
- // SHARESHEET_EDIT_TARGET_SELECTED:
+ // SHARESHEET_NEARBY_TARGET_SELECTED:
assertThat(logger.get(5).atomId, is(FrameworkStatsLog.RANKING_SELECTED));
assertThat(logger.get(5).targetType,
is(ChooserActivityLogger
@@ -814,7 +825,7 @@
- @Test
+ @Test @Ignore
public void testEditImageLogs() throws Exception {
Intent sendIntent = createSendImageIntent(
Uri.parse("android.resource://com.android.frameworks.coretests/"
@@ -853,7 +864,8 @@
assertThat(logger.get(1).atomId, is(FrameworkStatsLog.SHARESHEET_STARTED));
assertThat(logger.get(1).intent, is(Intent.ACTION_SEND));
assertThat(logger.get(1).mimeType, is("image/png"));
- assertThat(logger.get(1).packageName, is("com.android.frameworks.coretests"));
+ assertThat(logger.get(1).packageName, is(
+ InstrumentationRegistry.getInstrumentation().getTargetContext().getPackageName()));
assertThat(logger.get(1).appProvidedApp, is(0));
assertThat(logger.get(1).appProvidedDirect, is(0));
assertThat(logger.get(1).isWorkprofile, is(false));
@@ -1321,6 +1333,10 @@
} else {
assertThat(activity.isAppPredictionServiceAvailable(), is(true));
+ if (!shouldTestTogglingAppPredictionServiceAvailabilityAtRuntime()) {
+ return;
+ }
+
ChooserActivityOverrideData.getInstance().resources =
Mockito.spy(activity.getResources());
when(
@@ -2101,7 +2117,8 @@
assertThat(logger.get(1).atomId, is(FrameworkStatsLog.SHARESHEET_STARTED));
assertThat(logger.get(1).intent, is(Intent.ACTION_SEND));
assertThat(logger.get(1).mimeType, is("text/plain"));
- assertThat(logger.get(1).packageName, is("com.android.frameworks.coretests"));
+ assertThat(logger.get(1).packageName, is(
+ InstrumentationRegistry.getInstrumentation().getTargetContext().getPackageName()));
assertThat(logger.get(1).appProvidedApp, is(0));
assertThat(logger.get(1).appProvidedDirect, is(0));
assertThat(logger.get(1).isWorkprofile, is(false));
@@ -2119,7 +2136,7 @@
assertThat(logger.event(4).getId(),
is(ChooserActivityLogger.SharesheetStandardEvent.SHARESHEET_EXPANDED.getId()));
- // SHARESHEET_EDIT_TARGET_SELECTED:
+ // SHARESHEET_APP_TARGET_SELECTED:
assertThat(logger.get(5).atomId, is(FrameworkStatsLog.RANKING_SELECTED));
assertThat(logger.get(5).targetType,
is(ChooserActivityLogger
@@ -2197,7 +2214,8 @@
assertThat(logger.get(1).atomId, is(FrameworkStatsLog.SHARESHEET_STARTED));
assertThat(logger.get(1).intent, is(Intent.ACTION_SEND));
assertThat(logger.get(1).mimeType, is("text/plain"));
- assertThat(logger.get(1).packageName, is("com.android.frameworks.coretests"));
+ assertThat(logger.get(1).packageName, is(
+ InstrumentationRegistry.getInstrumentation().getTargetContext().getPackageName()));
assertThat(logger.get(1).appProvidedApp, is(0));
assertThat(logger.get(1).appProvidedDirect, is(0));
assertThat(logger.get(1).isWorkprofile, is(false));
@@ -2215,7 +2233,7 @@
.SharesheetTargetSelectedEvent.SHARESHEET_SERVICE_TARGET_SELECTED.getId()));
}
- @Test
+ @Test @Ignore
public void testEmptyDirectRowLogging() throws InterruptedException {
Intent sendIntent = createSendTextIntent();
// We need app targets for direct targets to get displayed
@@ -2259,7 +2277,8 @@
assertThat(logger.get(1).atomId, is(FrameworkStatsLog.SHARESHEET_STARTED));
assertThat(logger.get(1).intent, is(Intent.ACTION_SEND));
assertThat(logger.get(1).mimeType, is("text/plain"));
- assertThat(logger.get(1).packageName, is("com.android.frameworks.coretests"));
+ assertThat(logger.get(1).packageName, is(
+ InstrumentationRegistry.getInstrumentation().getTargetContext().getPackageName()));
assertThat(logger.get(1).appProvidedApp, is(0));
assertThat(logger.get(1).appProvidedDirect, is(0));
assertThat(logger.get(1).isWorkprofile, is(false));
@@ -2320,7 +2339,8 @@
assertThat(logger.get(1).atomId, is(FrameworkStatsLog.SHARESHEET_STARTED));
assertThat(logger.get(1).intent, is(Intent.ACTION_SEND));
assertThat(logger.get(1).mimeType, is("text/plain"));
- assertThat(logger.get(1).packageName, is("com.android.frameworks.coretests"));
+ assertThat(logger.get(1).packageName, is(
+ InstrumentationRegistry.getInstrumentation().getTargetContext().getPackageName()));
assertThat(logger.get(1).appProvidedApp, is(0));
assertThat(logger.get(1).appProvidedDirect, is(0));
assertThat(logger.get(1).isWorkprofile, is(false));
@@ -2338,7 +2358,7 @@
assertThat(logger.event(4).getId(),
is(ChooserActivityLogger.SharesheetStandardEvent.SHARESHEET_EXPANDED.getId()));
- // SHARESHEET_EDIT_TARGET_SELECTED:
+ // SHARESHEET_COPY_TARGET_SELECTED:
assertThat(logger.get(5).atomId, is(FrameworkStatsLog.RANKING_SELECTED));
assertThat(logger.get(5).targetType,
is(ChooserActivityLogger
@@ -2386,7 +2406,8 @@
assertThat(logger.get(1).atomId, is(FrameworkStatsLog.SHARESHEET_STARTED));
assertThat(logger.get(1).intent, is(Intent.ACTION_SEND));
assertThat(logger.get(1).mimeType, is(TEST_MIME_TYPE));
- assertThat(logger.get(1).packageName, is("com.android.frameworks.coretests"));
+ assertThat(logger.get(1).packageName, is(
+ InstrumentationRegistry.getInstrumentation().getTargetContext().getPackageName()));
assertThat(logger.get(1).appProvidedApp, is(0));
assertThat(logger.get(1).appProvidedDirect, is(0));
assertThat(logger.get(1).isWorkprofile, is(false));
diff --git a/graphics/java/android/graphics/BLASTBufferQueue.java b/graphics/java/android/graphics/BLASTBufferQueue.java
index 8d3eadb..a9e730d 100644
--- a/graphics/java/android/graphics/BLASTBufferQueue.java
+++ b/graphics/java/android/graphics/BLASTBufferQueue.java
@@ -27,8 +27,6 @@
// Note: This field is accessed by native code.
public long mNativeObject; // BLASTBufferQueue*
- private static native long nativeCreateAndUpdate(String name, long surfaceControl, long width,
- long height, int format);
private static native long nativeCreate(String name);
private static native void nativeDestroy(long ptr);
private static native Surface nativeGetSurface(long ptr, boolean includeSurfaceControlHandle);
@@ -40,11 +38,13 @@
long frameNumber);
private static native long nativeGetLastAcquiredFrameNum(long ptr);
private static native void nativeApplyPendingTransactions(long ptr, long frameNumber);
+ private static native boolean nativeIsSameSurfaceControl(long ptr, long surfaceControlPtr);
/** Create a new connection with the surface flinger. */
public BLASTBufferQueue(String name, SurfaceControl sc, int width, int height,
@PixelFormat.Format int format) {
- mNativeObject = nativeCreateAndUpdate(name, sc.mNativeObject, width, height, format);
+ this(name);
+ update(sc, width, height, format);
}
public BLASTBufferQueue(String name) {
@@ -152,4 +152,11 @@
public long getLastAcquiredFrameNum() {
return nativeGetLastAcquiredFrameNum(mNativeObject);
}
+
+ /**
+ * @return True if the associated SurfaceControl has the same handle as {@param sc}.
+ */
+ public boolean isSameSurfaceControl(SurfaceControl sc) {
+ return nativeIsSameSurfaceControl(mNativeObject, sc.mNativeObject);
+ }
}
diff --git a/graphics/java/android/graphics/HardwareRenderer.java b/graphics/java/android/graphics/HardwareRenderer.java
index 2b18350..fd4bed1 100644
--- a/graphics/java/android/graphics/HardwareRenderer.java
+++ b/graphics/java/android/graphics/HardwareRenderer.java
@@ -902,6 +902,20 @@
* @param frame The id of the frame being drawn.
*/
void onFrameDraw(long frame);
+
+ /**
+ * Invoked during a frame drawing.
+ *
+ * @param syncResult The result of the draw. Should be a value or a combination of values
+ * from {@link SyncAndDrawResult}
+ * @param frame The id of the frame being drawn.
+ *
+ * @return A {@link FrameCommitCallback} that will report back if the current vsync draws.
+ */
+ default FrameCommitCallback onFrameDraw(@SyncAndDrawResult int syncResult, long frame) {
+ onFrameDraw(frame);
+ return null;
+ }
}
/**
diff --git a/graphics/java/android/graphics/RenderEffect.java b/graphics/java/android/graphics/RenderEffect.java
index ad4c3fe..b8a4685 100644
--- a/graphics/java/android/graphics/RenderEffect.java
+++ b/graphics/java/android/graphics/RenderEffect.java
@@ -290,6 +290,22 @@
return new RenderEffect(nativeCreateShaderEffect(shader.getNativeInstance()));
}
+ /**
+ * Create a {@link RenderEffect} that executes the provided {@link RuntimeShader} and passes
+ * the contents of the {@link android.graphics.RenderNode} that this RenderEffect is installed
+ * on as an input to the shader.
+ * @param shader the runtime shader that will bind the inputShaderName to the RenderEffect input
+ * @param uniformShaderName the uniform name defined in the RuntimeShader's program to which
+ * the contents of the RenderNode will be bound
+ */
+ @NonNull
+ public static RenderEffect createRuntimeShaderEffect(
+ @NonNull RuntimeShader shader, @NonNull String uniformShaderName) {
+ return new RenderEffect(
+ nativeCreateRuntimeShaderEffect(shader.getNativeShaderBuilder(),
+ uniformShaderName));
+ }
+
private final long mNativeRenderEffect;
/* only constructed from static factory methods */
@@ -318,5 +334,7 @@
private static native long nativeCreateBlendModeEffect(long dst, long src, int blendmode);
private static native long nativeCreateChainEffect(long outer, long inner);
private static native long nativeCreateShaderEffect(long shader);
+ private static native long nativeCreateRuntimeShaderEffect(
+ long shaderBuilder, String inputShaderName);
private static native long nativeGetFinalizer();
}
diff --git a/libs/WindowManager/Shell/Android.bp b/libs/WindowManager/Shell/Android.bp
index cdff585..e9b3c49 100644
--- a/libs/WindowManager/Shell/Android.bp
+++ b/libs/WindowManager/Shell/Android.bp
@@ -43,7 +43,7 @@
name: "wm_shell_util-sources",
srcs: [
"src/com/android/wm/shell/util/**/*.java",
- "src/com/android/wm/shell/common/split/SplitScreenConstants.java"
+ "src/com/android/wm/shell/common/split/SplitScreenConstants.java",
],
path: "src",
}
@@ -74,13 +74,13 @@
],
tools: ["protologtool"],
cmd: "$(location protologtool) transform-protolog-calls " +
- "--protolog-class com.android.internal.protolog.common.ProtoLog " +
- "--protolog-impl-class com.android.wm.shell.protolog.ShellProtoLogImpl " +
- "--protolog-cache-class com.android.wm.shell.protolog.ShellProtoLogCache " +
- "--loggroups-class com.android.wm.shell.protolog.ShellProtoLogGroup " +
- "--loggroups-jar $(location :wm_shell_protolog-groups) " +
- "--output-srcjar $(out) " +
- "$(locations :wm_shell-sources)",
+ "--protolog-class com.android.internal.protolog.common.ProtoLog " +
+ "--protolog-impl-class com.android.wm.shell.protolog.ShellProtoLogImpl " +
+ "--protolog-cache-class com.android.wm.shell.protolog.ShellProtoLogCache " +
+ "--loggroups-class com.android.wm.shell.protolog.ShellProtoLogGroup " +
+ "--loggroups-jar $(location :wm_shell_protolog-groups) " +
+ "--output-srcjar $(out) " +
+ "$(locations :wm_shell-sources)",
out: ["wm_shell_protolog.srcjar"],
}
@@ -92,13 +92,14 @@
],
tools: ["protologtool"],
cmd: "$(location protologtool) generate-viewer-config " +
- "--protolog-class com.android.internal.protolog.common.ProtoLog " +
- "--loggroups-class com.android.wm.shell.protolog.ShellProtoLogGroup " +
- "--loggroups-jar $(location :wm_shell_protolog-groups) " +
- "--viewer-conf $(out) " +
- "$(locations :wm_shell-sources)",
+ "--protolog-class com.android.internal.protolog.common.ProtoLog " +
+ "--loggroups-class com.android.wm.shell.protolog.ShellProtoLogGroup " +
+ "--loggroups-jar $(location :wm_shell_protolog-groups) " +
+ "--viewer-conf $(out) " +
+ "$(locations :wm_shell-sources)",
out: ["wm_shell_protolog.json"],
}
+
// End ProtoLog
java_library {
@@ -123,11 +124,12 @@
"res",
],
java_resources: [
- ":generate-wm_shell_protolog.json"
+ ":generate-wm_shell_protolog.json",
],
static_libs: [
"androidx.appcompat_appcompat",
"androidx.arch.core_core-runtime",
+ "androidx-constraintlayout_constraintlayout",
"androidx.dynamicanimation_dynamicanimation",
"androidx.recyclerview_recyclerview",
"kotlinx-coroutines-android",
diff --git a/libs/WindowManager/Shell/res/layout/badged_image_view.xml b/libs/WindowManager/Shell/res/layout/badged_image_view.xml
new file mode 100644
index 0000000..5f07121
--- /dev/null
+++ b/libs/WindowManager/Shell/res/layout/badged_image_view.xml
@@ -0,0 +1,55 @@
+<?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.
+ -->
+<merge xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:app="http://schemas.android.com/apk/res-auto">
+
+ <ImageView
+ android:id="@+id/icon_view"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent"
+ android:contentDescription="@null" />
+
+ <!--
+ Icon badge size is defined in Launcher3 BaseIconFactory as 0.444 of icon size.
+ Constraint guide starts from left, which means for a badge positioned on the right,
+ percent has to be 1 - 0.444 to have the same effect.
+ -->
+ <androidx.constraintlayout.widget.Guideline
+ android:id="@+id/app_icon_constraint_horizontal"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:orientation="horizontal"
+ app:layout_constraintGuide_percent="0.556" />
+
+ <androidx.constraintlayout.widget.Guideline
+ android:id="@+id/app_icon_constraint_vertical"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:orientation="vertical"
+ app:layout_constraintGuide_percent="0.556" />
+
+ <ImageView
+ android:id="@+id/app_icon_view"
+ android:layout_width="0dp"
+ android:layout_height="0dp"
+ android:contentDescription="@null"
+ app:layout_constraintBottom_toBottomOf="parent"
+ app:layout_constraintLeft_toLeftOf="@id/app_icon_constraint_vertical"
+ app:layout_constraintRight_toRightOf="parent"
+ app:layout_constraintTop_toTopOf="@id/app_icon_constraint_horizontal" />
+
+</merge>
\ No newline at end of file
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BadgedImageView.java b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BadgedImageView.java
index 686fbbf..c52d87d 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BadgedImageView.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BadgedImageView.java
@@ -19,7 +19,6 @@
import android.annotation.Nullable;
import android.content.Context;
import android.content.res.TypedArray;
-import android.graphics.Bitmap;
import android.graphics.Canvas;
import android.graphics.Outline;
import android.graphics.Path;
@@ -27,14 +26,16 @@
import android.graphics.drawable.Drawable;
import android.util.AttributeSet;
import android.util.PathParser;
-import android.view.Gravity;
+import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewOutlineProvider;
-import android.widget.FrameLayout;
import android.widget.ImageView;
+import androidx.constraintlayout.widget.ConstraintLayout;
+
import com.android.launcher3.icons.DotRenderer;
import com.android.launcher3.icons.IconNormalizer;
+import com.android.wm.shell.R;
import com.android.wm.shell.animation.Interpolators;
import java.util.EnumSet;
@@ -46,14 +47,12 @@
* Badge = the icon associated with the app that created this bubble, this will show work profile
* badge if appropriate.
*/
-public class BadgedImageView extends FrameLayout {
+public class BadgedImageView extends ConstraintLayout {
/** Same value as Launcher3 dot code */
public static final float WHITE_SCRIM_ALPHA = 0.54f;
/** Same as value in Launcher3 IconShape */
public static final int DEFAULT_PATH_SIZE = 100;
- /** Same as value in Launcher3 BaseIconFactory */
- private static final float ICON_BADGE_SCALE = 0.444f;
/**
* Flags that suppress the visibility of the 'new' dot, for one reason or another. If any of
@@ -105,11 +104,13 @@
public BadgedImageView(Context context, AttributeSet attrs, int defStyleAttr,
int defStyleRes) {
super(context, attrs, defStyleAttr, defStyleRes);
+ // We manage positioning the badge ourselves
+ setLayoutDirection(LAYOUT_DIRECTION_LTR);
- mBubbleIcon = new ImageView(context);
- addView(mBubbleIcon);
- mAppIcon = new ImageView(context);
- addView(mAppIcon);
+ LayoutInflater.from(context).inflate(R.layout.badged_image_view, this);
+
+ mBubbleIcon = findViewById(R.id.icon_view);
+ mAppIcon = findViewById(R.id.app_icon_view);
final TypedArray ta = mContext.obtainStyledAttributes(attrs, new int[]{android.R.attr.src},
defStyleAttr, defStyleRes);
@@ -161,6 +162,7 @@
public void setRenderedBubble(BubbleViewProvider bubble) {
mBubble = bubble;
mBubbleIcon.setImageBitmap(bubble.getBubbleIcon());
+ mAppIcon.setImageBitmap(bubble.getAppBadge());
if (mDotSuppressionFlags.contains(SuppressionFlag.BEHIND_STACK)) {
hideBadge();
} else {
@@ -348,26 +350,17 @@
}
void showBadge() {
- Bitmap badge = mBubble.getAppBadge();
- if (badge == null) {
+ if (mBubble.getAppBadge() == null) {
mAppIcon.setVisibility(GONE);
return;
}
-
- final int bubbleSize = mBubble.getBubbleIcon().getWidth();
- final int badgeSize = (int) (ICON_BADGE_SCALE * bubbleSize);
-
- FrameLayout.LayoutParams appIconParams = (LayoutParams) mAppIcon.getLayoutParams();
- appIconParams.height = badgeSize;
- appIconParams.width = badgeSize;
+ int translationX;
if (mOnLeft) {
- appIconParams.gravity = Gravity.BOTTOM | Gravity.LEFT;
+ translationX = -(mBubbleIcon.getWidth() - mAppIcon.getWidth());
} else {
- appIconParams.gravity = Gravity.BOTTOM | Gravity.RIGHT;
+ translationX = 0;
}
- mAppIcon.setLayoutParams(appIconParams);
-
- mAppIcon.setImageBitmap(badge);
+ mAppIcon.setTranslationX(translationX);
mAppIcon.setVisibility(VISIBLE);
}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleExpandedView.java b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleExpandedView.java
index af59062..9ae67a9 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleExpandedView.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleExpandedView.java
@@ -386,13 +386,14 @@
final TypedArray ta = mContext.obtainStyledAttributes(new int[] {
android.R.attr.dialogCornerRadius,
android.R.attr.colorBackgroundFloating});
- mCornerRadius = ta.getDimensionPixelSize(0, 0);
+ boolean supportsRoundedCorners = ScreenDecorationsUtils.supportsRoundedCornersOnWindows(
+ mContext.getResources());
+ mCornerRadius = supportsRoundedCorners ? ta.getDimensionPixelSize(0, 0) : 0;
mBackgroundColorFloating = ta.getColor(1, Color.WHITE);
mExpandedViewContainer.setBackgroundColor(mBackgroundColorFloating);
ta.recycle();
- if (mTaskView != null && ScreenDecorationsUtils.supportsRoundedCornersOnWindows(
- mContext.getResources())) {
+ if (mTaskView != null) {
mTaskView.setCornerRadius(mCornerRadius);
}
updatePointerView();
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleStackView.java b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleStackView.java
index b40021e..79b7653 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleStackView.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleStackView.java
@@ -70,6 +70,7 @@
import androidx.dynamicanimation.animation.SpringForce;
import com.android.internal.annotations.VisibleForTesting;
+import com.android.internal.policy.ScreenDecorationsUtils;
import com.android.internal.util.FrameworkStatsLog;
import com.android.wm.shell.R;
import com.android.wm.shell.animation.Interpolators;
@@ -822,7 +823,9 @@
mAnimatingOutSurfaceView = new SurfaceView(getContext());
mAnimatingOutSurfaceView.setUseAlpha();
mAnimatingOutSurfaceView.setZOrderOnTop(true);
- mAnimatingOutSurfaceView.setCornerRadius(mCornerRadius);
+ boolean supportsRoundedCorners = ScreenDecorationsUtils.supportsRoundedCornersOnWindows(
+ mContext.getResources());
+ mAnimatingOutSurfaceView.setCornerRadius(supportsRoundedCorners ? mCornerRadius : 0);
mAnimatingOutSurfaceView.setLayoutParams(new ViewGroup.LayoutParams(0, 0));
mAnimatingOutSurfaceView.getHolder().addCallback(new SurfaceHolder.Callback() {
@Override
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/dagger/WMShellModule.java b/libs/WindowManager/Shell/src/com/android/wm/shell/dagger/WMShellModule.java
index d70857a4..4f01dc6 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/dagger/WMShellModule.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/dagger/WMShellModule.java
@@ -309,10 +309,11 @@
PipAnimationController pipAnimationController, PipBoundsAlgorithm pipBoundsAlgorithm,
PipBoundsState pipBoundsState, PipTransitionState pipTransitionState,
PhonePipMenuController pipMenuController,
- PipSurfaceTransactionHelper pipSurfaceTransactionHelper) {
+ PipSurfaceTransactionHelper pipSurfaceTransactionHelper,
+ Optional<SplitScreenController> splitScreenOptional) {
return new PipTransition(context, pipBoundsState, pipTransitionState, pipMenuController,
pipBoundsAlgorithm, pipAnimationController, transitions, shellTaskOrganizer,
- pipSurfaceTransactionHelper);
+ pipSurfaceTransactionHelper, splitScreenOptional);
}
@WMSingleton
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/onehanded/OneHandedController.java b/libs/WindowManager/Shell/src/com/android/wm/shell/onehanded/OneHandedController.java
index 507204c..c9c73fd 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/onehanded/OneHandedController.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/onehanded/OneHandedController.java
@@ -277,6 +277,7 @@
registerSettingObservers(mUserId);
setupTimeoutListener();
updateSettings();
+ updateDisplayLayout(mContext.getDisplayId());
mAccessibilityManager = AccessibilityManager.getInstance(context);
mAccessibilityManager.addAccessibilityStateChangeListener(
@@ -448,8 +449,13 @@
onShortcutEnabledChanged();
}
- private void updateDisplayLayout(int displayId) {
+ @VisibleForTesting
+ void updateDisplayLayout(int displayId) {
final DisplayLayout newDisplayLayout = mDisplayController.getDisplayLayout(displayId);
+ if (newDisplayLayout == null) {
+ Slog.w(TAG, "Failed to get new DisplayLayout.");
+ return;
+ }
mDisplayAreaOrganizer.setDisplayLayout(newDisplayLayout);
mTutorialHandler.onDisplayChanged(newDisplayLayout);
mBackgroundPanelOrganizer.onDisplayChanged(newDisplayLayout);
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/onehanded/OneHandedDisplayAreaOrganizer.java b/libs/WindowManager/Shell/src/com/android/wm/shell/onehanded/OneHandedDisplayAreaOrganizer.java
index 1b2f476..ec3ef5a 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/onehanded/OneHandedDisplayAreaOrganizer.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/onehanded/OneHandedDisplayAreaOrganizer.java
@@ -123,9 +123,8 @@
OneHandedBackgroundPanelOrganizer oneHandedBackgroundGradientOrganizer,
ShellExecutor mainExecutor) {
super(mainExecutor);
- mDisplayLayout.set(displayLayout);
+ setDisplayLayout(displayLayout);
mOneHandedSettingsUtil = oneHandedSettingsUtil;
- updateDisplayBounds();
mAnimationController = animationController;
final int animationDurationConfig = context.getResources().getInteger(
R.integer.config_one_handed_translate_animation_duration);
@@ -282,6 +281,7 @@
@VisibleForTesting
void setDisplayLayout(@NonNull DisplayLayout displayLayout) {
mDisplayLayout.set(displayLayout);
+ updateDisplayBounds();
}
@VisibleForTesting
@@ -289,6 +289,7 @@
return mDisplayAreaTokenMap;
}
+ @VisibleForTesting
void updateDisplayBounds() {
mDefaultDisplayBounds.set(0, 0, mDisplayLayout.width(), mDisplayLayout.height());
mLastVisualDisplayBounds.set(mDefaultDisplayBounds);
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipTaskOrganizer.java b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipTaskOrganizer.java
index 667d166..ae7b82f 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipTaskOrganizer.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipTaskOrganizer.java
@@ -25,6 +25,8 @@
import static com.android.wm.shell.ShellTaskOrganizer.TASK_LISTENER_TYPE_PIP;
import static com.android.wm.shell.ShellTaskOrganizer.taskListenerTypeToString;
+import static com.android.wm.shell.common.split.SplitScreenConstants.SPLIT_POSITION_BOTTOM_OR_RIGHT;
+import static com.android.wm.shell.common.split.SplitScreenConstants.SPLIT_POSITION_TOP_OR_LEFT;
import static com.android.wm.shell.pip.PipAnimationController.ANIM_TYPE_ALPHA;
import static com.android.wm.shell.pip.PipAnimationController.ANIM_TYPE_BOUNDS;
import static com.android.wm.shell.pip.PipAnimationController.FRACTION_START;
@@ -40,6 +42,10 @@
import static com.android.wm.shell.pip.PipAnimationController.isInPipDirection;
import static com.android.wm.shell.pip.PipAnimationController.isOutPipDirection;
import static com.android.wm.shell.pip.PipAnimationController.isRemovePipDirection;
+import static com.android.wm.shell.transition.Transitions.ENABLE_SHELL_TRANSITIONS;
+import static com.android.wm.shell.transition.Transitions.TRANSIT_EXIT_PIP;
+import static com.android.wm.shell.transition.Transitions.TRANSIT_EXIT_PIP_TO_SPLIT;
+import static com.android.wm.shell.transition.Transitions.TRANSIT_REMOVE_PIP;
import android.animation.Animator;
import android.animation.AnimatorListenerAdapter;
@@ -394,6 +400,18 @@
mPipUiEventLoggerLogger.log(
PipUiEventLogger.PipUiEventEnum.PICTURE_IN_PICTURE_EXPAND_TO_FULLSCREEN);
final WindowContainerTransaction wct = new WindowContainerTransaction();
+
+ if (ENABLE_SHELL_TRANSITIONS) {
+ if (requestEnterSplit && mSplitScreenOptional.isPresent()) {
+ mSplitScreenOptional.get().prepareEnterSplitScreen(wct, mTaskInfo,
+ isPipTopLeft()
+ ? SPLIT_POSITION_TOP_OR_LEFT : SPLIT_POSITION_BOTTOM_OR_RIGHT);
+ mPipTransitionController.startExitTransition(
+ TRANSIT_EXIT_PIP_TO_SPLIT, wct, null /* destinationBounds */);
+ return;
+ }
+ }
+
final Rect destinationBounds = mPipBoundsState.getDisplayBounds();
final int direction = syncWithSplitScreenBounds(destinationBounds, requestEnterSplit)
? TRANSITION_DIRECTION_LEAVE_PIP_TO_SPLIT_SCREEN
@@ -414,7 +432,7 @@
mPipTransitionState.setTransitionState(PipTransitionState.EXITING_PIP);
if (Transitions.ENABLE_SHELL_TRANSITIONS) {
- mPipTransitionController.startTransition(destinationBounds, wct);
+ mPipTransitionController.startExitTransition(TRANSIT_EXIT_PIP, wct, destinationBounds);
return;
}
mSyncTransactionQueue.queue(wct);
@@ -479,7 +497,8 @@
wct.setBounds(mToken, null);
wct.setWindowingMode(mToken, WINDOWING_MODE_UNDEFINED);
wct.reorder(mToken, false);
- mPipTransitionController.startTransition(null, wct);
+ mPipTransitionController.startExitTransition(TRANSIT_REMOVE_PIP, wct,
+ null /* destinationBounds */);
return;
}
@@ -1280,7 +1299,8 @@
public void applyFinishBoundsResize(@NonNull WindowContainerTransaction wct,
@PipAnimationController.TransitionDirection int direction, boolean wasPipTopLeft) {
if (direction == TRANSITION_DIRECTION_LEAVE_PIP_TO_SPLIT_SCREEN) {
- mSplitScreenOptional.get().enterSplitScreen(mTaskInfo.taskId, wasPipTopLeft, wct);
+ mSplitScreenOptional.ifPresent(splitScreenController ->
+ splitScreenController.enterSplitScreen(mTaskInfo.taskId, wasPipTopLeft, wct));
} else {
mTaskOrganizer.applyTransaction(wct);
}
@@ -1415,7 +1435,7 @@
/**
* Fades out and removes an overlay surface.
*/
- private void fadeOutAndRemoveOverlay(SurfaceControl surface, Runnable callback,
+ void fadeOutAndRemoveOverlay(SurfaceControl surface, Runnable callback,
boolean withStartDelay) {
if (surface == null) {
return;
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipTransition.java b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipTransition.java
index 0fcfced..2749bc8 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipTransition.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipTransition.java
@@ -30,8 +30,9 @@
import static com.android.wm.shell.pip.PipAnimationController.TRANSITION_DIRECTION_TO_PIP;
import static com.android.wm.shell.pip.PipAnimationController.isInPipDirection;
import static com.android.wm.shell.pip.PipAnimationController.isOutPipDirection;
-import static com.android.wm.shell.transition.Transitions.TRANSIT_EXIT_PIP;
+import static com.android.wm.shell.transition.Transitions.TRANSIT_EXIT_PIP_TO_SPLIT;
import static com.android.wm.shell.transition.Transitions.TRANSIT_REMOVE_PIP;
+import static com.android.wm.shell.transition.Transitions.isOpeningType;
import android.app.ActivityManager;
import android.app.TaskInfo;
@@ -51,8 +52,11 @@
import com.android.wm.shell.R;
import com.android.wm.shell.ShellTaskOrganizer;
+import com.android.wm.shell.splitscreen.SplitScreenController;
import com.android.wm.shell.transition.Transitions;
+import java.util.Optional;
+
/**
* Implementation of transitions for PiP on phone. Responsible for enter (alpha, bounds) and
* exit animation.
@@ -61,9 +65,11 @@
private static final String TAG = PipTransition.class.getSimpleName();
+ private final Context mContext;
private final PipTransitionState mPipTransitionState;
private final int mEnterExitAnimationDuration;
private final PipSurfaceTransactionHelper mSurfaceTransactionHelper;
+ private final Optional<SplitScreenController> mSplitScreenOptional;
private @PipAnimationController.AnimationType int mOneShotAnimationType = ANIM_TYPE_BOUNDS;
private Transitions.TransitionFinishCallback mFinishCallback;
private Rect mExitDestinationBounds = new Rect();
@@ -77,13 +83,16 @@
PipAnimationController pipAnimationController,
Transitions transitions,
@NonNull ShellTaskOrganizer shellTaskOrganizer,
- PipSurfaceTransactionHelper pipSurfaceTransactionHelper) {
+ PipSurfaceTransactionHelper pipSurfaceTransactionHelper,
+ Optional<SplitScreenController> splitScreenOptional) {
super(pipBoundsState, pipMenuController, pipBoundsAlgorithm,
pipAnimationController, transitions, shellTaskOrganizer);
+ mContext = context;
mPipTransitionState = pipTransitionState;
mEnterExitAnimationDuration = context.getResources()
.getInteger(R.integer.config_pipResizeAnimationDuration);
mSurfaceTransactionHelper = pipSurfaceTransactionHelper;
+ mSplitScreenOptional = splitScreenOptional;
}
@Override
@@ -101,13 +110,12 @@
}
@Override
- public void startTransition(Rect destinationBounds, WindowContainerTransaction out) {
+ public void startExitTransition(int type, WindowContainerTransaction out,
+ @Nullable Rect destinationBounds) {
if (destinationBounds != null) {
mExitDestinationBounds.set(destinationBounds);
- mExitTransition = mTransitions.startTransition(TRANSIT_EXIT_PIP, out, this);
- } else {
- mTransitions.startTransition(TRANSIT_REMOVE_PIP, out, this);
}
+ mExitTransition = mTransitions.startTransition(type, out, this);
}
@Override
@@ -116,9 +124,15 @@
@android.annotation.NonNull SurfaceControl.Transaction startTransaction,
@android.annotation.NonNull SurfaceControl.Transaction finishTransaction,
@android.annotation.NonNull Transitions.TransitionFinishCallback finishCallback) {
-
- if (mExitTransition == transition || info.getType() == TRANSIT_EXIT_PIP) {
+ final int type = info.getType();
+ if (mExitTransition == transition) {
mExitTransition = null;
+
+ if (type == TRANSIT_EXIT_PIP_TO_SPLIT) {
+ return startExitToSplitAnimation(
+ info, startTransaction, finishTransaction, finishCallback);
+ }
+
if (info.getChanges().size() == 1) {
if (mFinishCallback != null) {
mFinishCallback.onTransitionFinished(null, null);
@@ -138,7 +152,7 @@
}
}
- if (info.getType() == TRANSIT_REMOVE_PIP) {
+ if (type == TRANSIT_REMOVE_PIP) {
if (mFinishCallback != null) {
mFinishCallback.onTransitionFinished(null /* wct */, null /* callback */);
mFinishCallback = null;
@@ -154,7 +168,7 @@
// We only support TRANSIT_PIP type (from RootWindowContainer) or TRANSIT_OPEN (from apps
// that enter PiP instantly on opening, mostly from CTS/Flicker tests)
- if (info.getType() != TRANSIT_PIP && info.getType() != TRANSIT_OPEN) {
+ if (type != TRANSIT_PIP && type != TRANSIT_OPEN) {
// In case the PIP window is part of rotation transition, reset the bounds and rounded
// corner.
for (int i = info.getChanges().size() - 1; i >= 0; --i) {
@@ -219,7 +233,6 @@
@NonNull TransitionRequestInfo request) {
if (request.getType() == TRANSIT_PIP) {
WindowContainerTransaction wct = new WindowContainerTransaction();
- mPipTransitionState.setTransitionState(PipTransitionState.ENTRY_SCHEDULED);
if (mOneShotAnimationType == ANIM_TYPE_ALPHA) {
wct.setActivityWindowingMode(request.getTriggerTask().token,
WINDOWING_MODE_UNDEFINED);
@@ -348,6 +361,11 @@
animator = mPipAnimationController.getAnimator(taskInfo, leash, currentBounds,
currentBounds, destinationBounds, sourceHintRect, TRANSITION_DIRECTION_TO_PIP,
0 /* startingAngle */, rotationDelta);
+ if (sourceHintRect == null) {
+ // We use content overlay when there is no source rect hint to enter PiP use bounds
+ // animation.
+ animator.setUseContentOverlay(mContext);
+ }
} else if (mOneShotAnimationType == ANIM_TYPE_ALPHA) {
startTransaction.setAlpha(leash, 0f);
// PiP menu is attached late in the process here to avoid any artifacts on the leash
@@ -369,6 +387,40 @@
return true;
}
+ private boolean startExitToSplitAnimation(TransitionInfo info,
+ SurfaceControl.Transaction startTransaction,
+ SurfaceControl.Transaction finishTransaction,
+ Transitions.TransitionFinishCallback finishCallback) {
+ final int changeSize = info.getChanges().size();
+ if (changeSize < 4) {
+ throw new RuntimeException(
+ "Got an exit-pip-to-split transition with unexpected change-list");
+ }
+ for (int i = changeSize - 1; i >= 0; i--) {
+ final TransitionInfo.Change change = info.getChanges().get(i);
+ final int mode = change.getMode();
+
+ if (mode == TRANSIT_CHANGE && change.getParent() != null) {
+ // TODO: perform resize/expand animation for reparented child task.
+ continue;
+ }
+
+ if (isOpeningType(mode) && change.getParent() == null) {
+ final SurfaceControl leash = change.getLeash();
+ final Rect endBounds = change.getEndAbsBounds();
+ startTransaction
+ .show(leash)
+ .setAlpha(leash, 1f)
+ .setPosition(leash, endBounds.left, endBounds.top)
+ .setWindowCrop(leash, endBounds.width(), endBounds.height());
+ }
+ }
+ mSplitScreenOptional.get().finishEnterSplitScreen(startTransaction);
+ startTransaction.apply();
+ finishCallback.onTransitionFinished(null, null);
+ return true;
+ }
+
private void finishResizeForMenu(Rect destinationBounds) {
mPipMenuController.movePipMenu(null, null, destinationBounds);
mPipMenuController.updateMenuBounds(destinationBounds);
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipTransitionController.java b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipTransitionController.java
index 376f329..22b3ef3 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipTransitionController.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipTransitionController.java
@@ -19,7 +19,9 @@
import static android.app.WindowConfiguration.WINDOWING_MODE_UNDEFINED;
import static com.android.wm.shell.pip.PipAnimationController.TRANSITION_DIRECTION_REMOVE_STACK;
+import static com.android.wm.shell.pip.PipAnimationController.isInPipDirection;
+import android.annotation.Nullable;
import android.app.PictureInPictureParams;
import android.app.TaskInfo;
import android.content.ComponentName;
@@ -68,6 +70,10 @@
if (direction == TRANSITION_DIRECTION_REMOVE_STACK) {
return;
}
+ if (isInPipDirection(direction) && animator.getContentOverlay() != null) {
+ mPipOrganizer.fadeOutAndRemoveOverlay(animator.getContentOverlay(),
+ animator::clearContentOverlay, true /* withStartDelay*/);
+ }
onFinishResize(taskInfo, animator.getDestinationBounds(), direction, tx);
sendOnPipTransitionFinished(direction);
}
@@ -75,6 +81,11 @@
@Override
public void onPipAnimationCancel(TaskInfo taskInfo,
PipAnimationController.PipTransitionAnimator animator) {
+ final int direction = animator.getTransitionDirection();
+ if (isInPipDirection(direction) && animator.getContentOverlay() != null) {
+ mPipOrganizer.fadeOutAndRemoveOverlay(animator.getContentOverlay(),
+ animator::clearContentOverlay, true /* withStartDelay */);
+ }
sendOnPipTransitionCancelled(animator.getTransitionDirection());
}
};
@@ -98,9 +109,10 @@
}
/**
- * Called when the Shell wants to starts a transition/animation.
+ * Called when the Shell wants to start an exit Pip transition/animation.
*/
- public void startTransition(Rect destinationBounds, WindowContainerTransaction out) {
+ public void startExitTransition(int type, WindowContainerTransaction out,
+ @Nullable Rect destinationBounds) {
// Default implementation does nothing.
}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/recents/RecentTasksController.java b/libs/WindowManager/Shell/src/com/android/wm/shell/recents/RecentTasksController.java
index 3de59b4..7decb54 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/recents/RecentTasksController.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/recents/RecentTasksController.java
@@ -292,6 +292,7 @@
* Invalidates this instance, preventing future calls from updating the controller.
*/
void invalidate() {
+ Slog.d("b/206648922", "invalidating controller: " + mController);
mController = null;
}
@@ -317,7 +318,8 @@
(controller) -> out[0] = controller.getRecentTasks(maxNum, flags, userId)
.toArray(new GroupedRecentTaskInfo[0]),
true /* blocking */);
- Slog.d("b/206648922", "getRecentTasks(" + maxNum + "): " + out[0]);
+ Slog.d("b/206648922", "getRecentTasks(" + maxNum + "): " + out[0]
+ + " mController=" + mController);
return out[0];
}
}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/SideStage.java b/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/SideStage.java
index 1ba1d22..122fc9f 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/SideStage.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/SideStage.java
@@ -19,7 +19,6 @@
import android.annotation.Nullable;
import android.app.ActivityManager;
import android.content.Context;
-import android.graphics.Rect;
import android.view.SurfaceSession;
import android.window.WindowContainerToken;
import android.window.WindowContainerTransaction;
@@ -45,11 +44,6 @@
stageTaskUnfoldController);
}
- void moveToTop(Rect rootBounds, WindowContainerTransaction wct) {
- final WindowContainerToken rootToken = mRootTaskInfo.token;
- wct.setBounds(rootToken, rootBounds).reorder(rootToken, true /* onTop */);
- }
-
boolean removeAllTasks(WindowContainerTransaction wct, boolean toTop) {
// No matter if the root task is empty or not, moving the root to bottom because it no
// longer preserves visible child task.
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 1f49a4cc..6921448 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/SplitScreenController.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/SplitScreenController.java
@@ -26,6 +26,7 @@
import static com.android.wm.shell.common.split.SplitScreenConstants.SPLIT_POSITION_TOP_OR_LEFT;
import static com.android.wm.shell.splitscreen.SplitScreen.STAGE_TYPE_SIDE;
import static com.android.wm.shell.splitscreen.SplitScreen.STAGE_TYPE_UNDEFINED;
+import static com.android.wm.shell.transition.Transitions.ENABLE_SHELL_TRANSITIONS;
import android.app.ActivityManager;
import android.app.ActivityTaskManager;
@@ -238,6 +239,15 @@
enterSplitScreen(taskId, leftOrTop, new WindowContainerTransaction());
}
+ public void prepareEnterSplitScreen(WindowContainerTransaction wct,
+ ActivityManager.RunningTaskInfo taskInfo, int startPosition) {
+ mStageCoordinator.prepareEnterSplitScreen(wct, taskInfo, startPosition);
+ }
+
+ public void finishEnterSplitScreen(SurfaceControl.Transaction t) {
+ mStageCoordinator.finishEnterSplitScreen(t);
+ }
+
public void enterSplitScreen(int taskId, boolean leftOrTop, WindowContainerTransaction wct) {
final int stageType = isSplitScreenVisible() ? STAGE_TYPE_UNDEFINED : STAGE_TYPE_SIDE;
final int stagePosition =
@@ -310,7 +320,7 @@
public void startIntent(PendingIntent intent, Intent fillInIntent, @SplitPosition int position,
@Nullable Bundle options) {
- if (!Transitions.ENABLE_SHELL_TRANSITIONS) {
+ if (!ENABLE_SHELL_TRANSITIONS) {
startIntentLegacy(intent, fillInIntent, position, options);
return;
}
@@ -366,7 +376,8 @@
}
RemoteAnimationTarget[] onGoingToRecentsLegacy(boolean cancel, RemoteAnimationTarget[] apps) {
- if (!isSplitScreenVisible()) return null;
+ if (ENABLE_SHELL_TRANSITIONS || apps.length < 2) return null;
+ // TODO(b/206487881): Integrate this with shell transition.
final SurfaceControl.Builder builder = new SurfaceControl.Builder(new SurfaceSession())
.setContainerLayer()
.setName("RecentsAnimationSplitTasks")
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/SplitScreenTransitions.java b/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/SplitScreenTransitions.java
index 54d8ece..0aa8d7e 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/SplitScreenTransitions.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/SplitScreenTransitions.java
@@ -28,7 +28,8 @@
import static com.android.wm.shell.splitscreen.SplitScreenController.exitReasonToString;
import static com.android.wm.shell.transition.Transitions.TRANSIT_SPLIT_DISMISS;
import static com.android.wm.shell.transition.Transitions.TRANSIT_SPLIT_DISMISS_SNAP;
-import static com.android.wm.shell.transition.Transitions.isOpeningType;
+import static com.android.wm.shell.transition.Transitions.TRANSIT_SPLIT_SCREEN_OPEN_TO_SIDE;
+import static com.android.wm.shell.transition.Transitions.TRANSIT_SPLIT_SCREEN_PAIR_OPEN;
import android.animation.Animator;
import android.animation.AnimatorListenerAdapter;
@@ -148,7 +149,7 @@
t.setWindowCrop(leash, change.getEndAbsBounds().width(),
change.getEndAbsBounds().height());
}
- boolean isOpening = isOpeningType(info.getType());
+ boolean isOpening = isOpeningTransition(info);
if (isOpening && (mode == TRANSIT_OPEN || mode == TRANSIT_TO_FRONT)) {
// fade in
startExampleAnimation(leash, true /* show */);
@@ -305,6 +306,12 @@
mTransitions.getAnimExecutor().execute(va::start);
}
+ private boolean isOpeningTransition(TransitionInfo info) {
+ return Transitions.isOpeningType(info.getType())
+ || info.getType() == TRANSIT_SPLIT_SCREEN_OPEN_TO_SIDE
+ || info.getType() == TRANSIT_SPLIT_SCREEN_PAIR_OPEN;
+ }
+
/** Bundled information of dismiss transition. */
static class DismissTransition {
IBinder mTransition;
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/StageCoordinator.java b/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/StageCoordinator.java
index af0ac55..83830ec 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/StageCoordinator.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/StageCoordinator.java
@@ -46,6 +46,7 @@
import static com.android.wm.shell.splitscreen.SplitScreenController.exitReasonToString;
import static com.android.wm.shell.splitscreen.SplitScreenTransitions.FLAG_IS_DIVIDER_BAR;
import static com.android.wm.shell.transition.Transitions.ENABLE_SHELL_TRANSITIONS;
+import static com.android.wm.shell.transition.Transitions.TRANSIT_SPLIT_SCREEN_OPEN_TO_SIDE;
import static com.android.wm.shell.transition.Transitions.TRANSIT_SPLIT_SCREEN_PAIR_OPEN;
import static com.android.wm.shell.transition.Transitions.isClosingType;
import static com.android.wm.shell.transition.Transitions.isOpeningType;
@@ -323,7 +324,14 @@
if (!evictWct.isEmpty()) {
wct.merge(evictWct, true /* transfer */);
}
- mTaskOrganizer.applyTransaction(wct);
+
+ if (ENABLE_SHELL_TRANSITIONS) {
+ prepareEnterSplitScreen(wct);
+ mSplitTransitions.startEnterTransition(TRANSIT_SPLIT_SCREEN_OPEN_TO_SIDE,
+ wct, null, this);
+ } else {
+ mTaskOrganizer.applyTransaction(wct);
+ }
return true;
}
@@ -372,7 +380,14 @@
void startTasksWithLegacyTransition(int mainTaskId, @Nullable Bundle mainOptions,
int sideTaskId, @Nullable Bundle sideOptions, @SplitPosition int sidePosition,
float splitRatio, RemoteAnimationAdapter adapter) {
+ // Init divider first to make divider leash for remote animation target.
+ mSplitLayout.init();
+ // Set false to avoid record new bounds with old task still on top;
+ mShouldUpdateRecents = false;
final WindowContainerTransaction wct = new WindowContainerTransaction();
+ final WindowContainerTransaction evictWct = new WindowContainerTransaction();
+ prepareEvictChildTasks(SPLIT_POSITION_TOP_OR_LEFT, evictWct);
+ prepareEvictChildTasks(SPLIT_POSITION_BOTTOM_OR_RIGHT, evictWct);
// Need to add another wrapper here in shell so that we can inject the divider bar
// and also manage the process elevation via setRunningRemote
IRemoteAnimationRunner wrapper = new IRemoteAnimationRunner.Stub() {
@@ -388,6 +403,17 @@
augmentedNonApps[i] = nonApps[i];
}
augmentedNonApps[augmentedNonApps.length - 1] = getDividerBarLegacyTarget();
+
+ IRemoteAnimationFinishedCallback wrapCallback =
+ new IRemoteAnimationFinishedCallback.Stub() {
+ @Override
+ public void onAnimationFinished() throws RemoteException {
+ mShouldUpdateRecents = true;
+ mSyncQueue.queue(evictWct);
+ mSyncQueue.runInSync(t -> setDividerVisibility(true, t));
+ finishedCallback.onAnimationFinished();
+ }
+ };
try {
try {
ActivityTaskManager.getService().setRunningRemoteTransitionDelegate(
@@ -396,8 +422,8 @@
Slog.e(TAG, "Unable to boost animation thread. This should only happen"
+ " during unit tests");
}
- adapter.getRunner().onAnimationStart(transit, apps, wallpapers, nonApps,
- finishedCallback);
+ adapter.getRunner().onAnimationStart(transit, apps, wallpapers,
+ augmentedNonApps, wrapCallback);
} catch (RemoteException e) {
Slog.e(TAG, "Error starting remote animation", e);
}
@@ -405,6 +431,9 @@
@Override
public void onAnimationCancelled() {
+ mShouldUpdateRecents = true;
+ mSyncQueue.queue(evictWct);
+ mSyncQueue.runInSync(t -> setDividerVisibility(true, t));
try {
adapter.getRunner().onAnimationCancelled();
} catch (RemoteException e) {
@@ -427,10 +456,14 @@
setSideStagePosition(sidePosition, wct);
mSplitLayout.setDivideRatio(splitRatio);
- // Build a request WCT that will launch both apps such that task 0 is on the main stage
- // while task 1 is on the side stage.
- mMainStage.activate(getMainStageBounds(), wct, false /* reparent */);
- mSideStage.setBounds(getSideStageBounds(), wct);
+ if (mMainStage.isActive()) {
+ mMainStage.moveToTop(getMainStageBounds(), wct);
+ } else {
+ // Build a request WCT that will launch both apps such that task 0 is on the main stage
+ // while task 1 is on the side stage.
+ mMainStage.activate(getMainStageBounds(), wct, false /* reparent */);
+ }
+ mSideStage.moveToTop(getSideStageBounds(), wct);
// Make sure the launch options will put tasks in the corresponding split roots
addActivityOptions(mainOptions, mMainStage);
@@ -699,12 +732,47 @@
* an existing WindowContainerTransaction (rather than applying immediately). This is intended
* to be used when exiting split might be bundled with other window operations.
*/
- void prepareExitSplitScreen(@StageType int stageToTop,
+ private void prepareExitSplitScreen(@StageType int stageToTop,
@NonNull WindowContainerTransaction wct) {
+ if (!mMainStage.isActive()) return;
mSideStage.removeAllTasks(wct, stageToTop == STAGE_TYPE_SIDE);
mMainStage.deactivate(wct, stageToTop == STAGE_TYPE_MAIN);
}
+ private void prepareEnterSplitScreen(WindowContainerTransaction wct) {
+ prepareEnterSplitScreen(wct, null /* taskInfo */, SPLIT_POSITION_UNDEFINED);
+ }
+
+ /**
+ * Prepare transaction to active split screen. If there's a task indicated, the task will be put
+ * into side stage.
+ */
+ void prepareEnterSplitScreen(WindowContainerTransaction wct,
+ @Nullable ActivityManager.RunningTaskInfo taskInfo, @SplitPosition int startPosition) {
+ if (mMainStage.isActive()) return;
+
+ if (taskInfo != null) {
+ setSideStagePosition(startPosition, wct);
+ mSideStage.addTask(taskInfo, wct);
+ }
+ mMainStage.activate(getMainStageBounds(), wct, true /* includingTopTask */);
+ mSideStage.moveToTop(getSideStageBounds(), wct);
+ }
+
+ void finishEnterSplitScreen(SurfaceControl.Transaction t) {
+ mSplitLayout.init();
+ setDividerVisibility(true, t);
+ setSplitsVisible(true);
+ mShouldUpdateRecents = true;
+ updateRecentTasksSplitPair();
+ if (!mLogger.hasStartedSession()) {
+ mLogger.logEnter(mSplitLayout.getDividerPositionAsFraction(),
+ getMainStagePosition(), mMainStage.getTopChildTaskUid(),
+ getSideStagePosition(), mSideStage.getTopChildTaskUid(),
+ mSplitLayout.isLandscape());
+ }
+ }
+
void getStageBounds(Rect outTopOrLeftBounds, Rect outBottomOrRightBounds) {
outTopOrLeftBounds.set(mSplitLayout.getBounds1());
outBottomOrRightBounds.set(mSplitLayout.getBounds2());
@@ -770,7 +838,9 @@
mLogger.logSideStageAppChange(getSideStagePosition(), mSideStage.getTopChildTaskUid(),
mSplitLayout.isLandscape());
}
- updateRecentTasksSplitPair();
+ if (present && visible) {
+ updateRecentTasksSplitPair();
+ }
for (int i = mListeners.size() - 1; i >= 0; --i) {
mListeners.get(i).onTaskStageChanged(taskId, stage, visible);
@@ -786,7 +856,6 @@
if (!mShouldUpdateRecents) {
return;
}
-
mRecentTasks.ifPresent(recentTasks -> {
Rect topLeftBounds = mSplitLayout.getBounds1();
Rect bottomRightBounds = mSplitLayout.getBounds2();
@@ -883,12 +952,11 @@
private void applyDividerVisibility(SurfaceControl.Transaction t) {
final SurfaceControl dividerLeash = mSplitLayout.getDividerLeash();
- if (dividerLeash == null) {
- return;
- }
+ if (dividerLeash == null) return;
if (mDividerVisible) {
t.show(dividerLeash);
+ t.setAlpha(dividerLeash, 1);
t.setLayer(dividerLeash, SPLIT_DIVIDER_LAYER);
t.setPosition(dividerLeash,
mSplitLayout.getDividerBounds().left, mSplitLayout.getDividerBounds().top);
@@ -910,11 +978,14 @@
}
} else if (isSideStage) {
final WindowContainerTransaction wct = new WindowContainerTransaction();
+ mSplitLayout.init();
// Make sure the main stage is active.
- mMainStage.activate(getMainStageBounds(), wct, true /* reparent */);
- mSideStage.moveToTop(getSideStageBounds(), wct);
+ prepareEnterSplitScreen(wct);
mSyncQueue.queue(wct);
- mSyncQueue.runInSync(t -> updateSurfaceBounds(mSplitLayout, t));
+ mSyncQueue.runInSync(t -> {
+ updateSurfaceBounds(mSplitLayout, t);
+ setDividerVisibility(true, t);
+ });
}
if (mMainStageListener.mHasChildren && mSideStageListener.mHasChildren) {
mShouldUpdateRecents = true;
@@ -1213,8 +1284,7 @@
if (isOpening && getStageOfTask(triggerTask) != null) {
// One task is appearing into split, prepare to enter split screen.
out = new WindowContainerTransaction();
- mMainStage.activate(getMainStageBounds(), out, true /* includingTopTask */);
- mSideStage.moveToTop(getSideStageBounds(), out);
+ prepareEnterSplitScreen(out);
mSplitTransitions.mPendingEnter = transition;
}
}
@@ -1226,18 +1296,7 @@
// Once the pending enter transition got merged, make sure to bring divider bar visible and
// clear the pending transition from cache to prevent mess-up the following state.
if (transition == mSplitTransitions.mPendingEnter) {
- mSplitLayout.init();
- setDividerVisibility(true, null /* transaction */);
- setSplitsVisible(true);
- mShouldUpdateRecents = true;
- updateRecentTasksSplitPair();
-
- if (!mLogger.hasStartedSession()) {
- mLogger.logEnter(mSplitLayout.getDividerPositionAsFraction(),
- getMainStagePosition(), mMainStage.getTopChildTaskUid(),
- getSideStagePosition(), mSideStage.getTopChildTaskUid(),
- mSplitLayout.isLandscape());
- }
+ finishEnterSplitScreen(null);
mSplitTransitions.mPendingEnter = null;
}
}
@@ -1325,47 +1384,40 @@
sideChild = change;
}
}
- if (mainChild == null || sideChild == null) {
- throw new IllegalStateException("Launched 2 tasks in split, but didn't receive"
- + " 2 tasks in transition. Possibly one of them failed to launch");
- // TODO: fallback logic. Probably start a new transition to exit split before
- // applying anything here. Ideally consolidate with transition-merging.
+
+ // TODO: fallback logic. Probably start a new transition to exit split before applying
+ // anything here. Ideally consolidate with transition-merging.
+ if (info.getType() == TRANSIT_SPLIT_SCREEN_OPEN_TO_SIDE) {
+ if (mainChild == null && sideChild == null) {
+ throw new IllegalStateException("Launched a task in split, but didn't receive any"
+ + " task in transition.");
+ }
+ } else {
+ if (mainChild == null || sideChild == null) {
+ throw new IllegalStateException("Launched 2 tasks in split, but didn't receive"
+ + " 2 tasks in transition. Possibly one of them failed to launch");
+ }
}
- // Update local states (before animating).
- mSplitLayout.init();
- setDividerVisibility(true, t);
- setSplitsVisible(true);
-
- addDividerBarToTransition(info, t, true /* show */);
-
// Make some noise if things aren't totally expected. These states shouldn't effect
// transitions locally, but remotes (like Launcher) may get confused if they were
// depending on listener callbacks. This can happen because task-organizer callbacks
// aren't serialized with transition callbacks.
// TODO(b/184679596): Find a way to either include task-org information in
// the transition, or synchronize task-org callbacks.
- if (!mMainStage.containsTask(mainChild.getTaskInfo().taskId)) {
+ if (mainChild != null && !mMainStage.containsTask(mainChild.getTaskInfo().taskId)) {
Log.w(TAG, "Expected onTaskAppeared on " + mMainStage
+ " to have been called with " + mainChild.getTaskInfo().taskId
+ " before startAnimation().");
}
- if (!mSideStage.containsTask(sideChild.getTaskInfo().taskId)) {
+ if (sideChild != null && !mSideStage.containsTask(sideChild.getTaskInfo().taskId)) {
Log.w(TAG, "Expected onTaskAppeared on " + mSideStage
+ " to have been called with " + sideChild.getTaskInfo().taskId
+ " before startAnimation().");
}
- mShouldUpdateRecents = true;
- updateRecentTasksSplitPair();
-
- if (!mLogger.hasStartedSession()) {
- mLogger.logEnter(mSplitLayout.getDividerPositionAsFraction(),
- getMainStagePosition(), mMainStage.getTopChildTaskUid(),
- getSideStagePosition(), mSideStage.getTopChildTaskUid(),
- mSplitLayout.isLandscape());
- }
-
+ finishEnterSplitScreen(t);
+ addDividerBarToTransition(info, t, true /* show */);
return true;
}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/StageTaskListener.java b/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/StageTaskListener.java
index 2c853c1..83534c1 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/StageTaskListener.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/StageTaskListener.java
@@ -34,6 +34,7 @@
import android.util.SparseArray;
import android.view.SurfaceControl;
import android.view.SurfaceSession;
+import android.window.WindowContainerToken;
import android.window.WindowContainerTransaction;
import androidx.annotation.NonNull;
@@ -301,9 +302,19 @@
}
void addTask(ActivityManager.RunningTaskInfo task, WindowContainerTransaction wct) {
+ // Clear overridden bounds and windowing mode to make sure the child task can inherit
+ // windowing mode and bounds from split root.
+ wct.setWindowingMode(task.token, WINDOWING_MODE_UNDEFINED)
+ .setBounds(task.token, null);
+
wct.reparent(task.token, mRootTaskInfo.token, true /* onTop*/);
}
+ void moveToTop(Rect rootBounds, WindowContainerTransaction wct) {
+ final WindowContainerToken rootToken = mRootTaskInfo.token;
+ wct.setBounds(rootToken, rootBounds).reorder(rootToken, true /* onTop */);
+ }
+
void setBounds(Rect bounds, WindowContainerTransaction wct) {
wct.setBounds(mRootTaskInfo.token, bounds);
}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/StageTaskUnfoldController.java b/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/StageTaskUnfoldController.java
index 0683a25..59eecb5d 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/StageTaskUnfoldController.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/StageTaskUnfoldController.java
@@ -105,6 +105,9 @@
* @param leash surface leash for the appeared task
*/
public void onTaskAppeared(ActivityManager.RunningTaskInfo taskInfo, SurfaceControl leash) {
+ // Only handle child task surface here.
+ if (!taskInfo.hasParentTask()) return;
+
AnimationContext context = new AnimationContext(leash);
mAnimationContextByTaskId.put(taskInfo.taskId, context);
}
@@ -114,6 +117,8 @@
* @param taskInfo info for the vanished task
*/
public void onTaskVanished(ActivityManager.RunningTaskInfo taskInfo) {
+ if (!taskInfo.hasParentTask()) return;
+
AnimationContext context = mAnimationContextByTaskId.get(taskInfo.taskId);
if (context != null) {
final SurfaceControl.Transaction transaction = mTransactionPool.acquire();
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/transition/Transitions.java b/libs/WindowManager/Shell/src/com/android/wm/shell/transition/Transitions.java
index b8cbfd9..33a98b2 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/transition/Transitions.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/transition/Transitions.java
@@ -74,23 +74,25 @@
public static final boolean ENABLE_SHELL_TRANSITIONS =
SystemProperties.getBoolean("persist.debug.shell_transit", false);
- /** Transition type for dismissing split-screen via dragging the divider off the screen. */
- public static final int TRANSIT_SPLIT_DISMISS_SNAP = TRANSIT_FIRST_CUSTOM + 1;
-
- /** Transition type for launching 2 tasks simultaneously. */
- public static final int TRANSIT_SPLIT_SCREEN_PAIR_OPEN = TRANSIT_FIRST_CUSTOM + 2;
-
/** Transition type for exiting PIP via the Shell, via pressing the expand button. */
- public static final int TRANSIT_EXIT_PIP = TRANSIT_FIRST_CUSTOM + 3;
+ public static final int TRANSIT_EXIT_PIP = TRANSIT_FIRST_CUSTOM + 1;
+
+ public static final int TRANSIT_EXIT_PIP_TO_SPLIT = TRANSIT_FIRST_CUSTOM + 2;
/** Transition type for removing PIP via the Shell, either via Dismiss bubble or Close. */
- public static final int TRANSIT_REMOVE_PIP = TRANSIT_FIRST_CUSTOM + 4;
+ public static final int TRANSIT_REMOVE_PIP = TRANSIT_FIRST_CUSTOM + 3;
+
+ /** Transition type for launching 2 tasks simultaneously. */
+ public static final int TRANSIT_SPLIT_SCREEN_PAIR_OPEN = TRANSIT_FIRST_CUSTOM + 4;
/** Transition type for entering split by opening an app into side-stage. */
public static final int TRANSIT_SPLIT_SCREEN_OPEN_TO_SIDE = TRANSIT_FIRST_CUSTOM + 5;
+ /** Transition type for dismissing split-screen via dragging the divider off the screen. */
+ public static final int TRANSIT_SPLIT_DISMISS_SNAP = TRANSIT_FIRST_CUSTOM + 6;
+
/** Transition type for dismissing split-screen. */
- public static final int TRANSIT_SPLIT_DISMISS = TRANSIT_FIRST_CUSTOM + 6;
+ public static final int TRANSIT_SPLIT_DISMISS = TRANSIT_FIRST_CUSTOM + 7;
private final WindowOrganizer mOrganizer;
private final Context mContext;
@@ -357,6 +359,28 @@
return;
}
+ // apply transfer starting window directly if there is no other task change.
+ final int changeSize = info.getChanges().size();
+ if (changeSize == 2) {
+ boolean nonTaskChange = true;
+ boolean transferStartingWindow = false;
+ for (int i = changeSize - 1; i >= 0; --i) {
+ final TransitionInfo.Change change = info.getChanges().get(i);
+ if (change.getTaskInfo() != null) {
+ nonTaskChange = false;
+ break;
+ }
+ if ((change.getFlags() & FLAG_STARTING_WINDOW_TRANSFER_RECIPIENT) != 0) {
+ transferStartingWindow = true;
+ }
+ }
+ if (nonTaskChange && transferStartingWindow) {
+ t.apply();
+ onFinish(transitionToken, null /* wct */, null /* wctCB */);
+ return;
+ }
+ }
+
final ActiveTransition active = mActiveTransitions.get(activeIdx);
active.mInfo = info;
active.mStartT = t;
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/CommonAssertions.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/CommonAssertions.kt
index c4be785..68b0b4e 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/CommonAssertions.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/CommonAssertions.kt
@@ -17,11 +17,11 @@
@file:JvmName("CommonAssertions")
package com.android.wm.shell.flicker
-import android.graphics.Region
import android.view.Surface
import com.android.server.wm.flicker.FlickerTestParameter
import com.android.server.wm.flicker.helpers.WindowUtils
import com.android.server.wm.traces.common.FlickerComponentName
+import com.android.server.wm.traces.common.region.Region
fun FlickerTestParameter.appPairsDividerIsVisibleAtEnd() {
assertLayersEnd {
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/helpers/AppPairsHelper.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/helpers/AppPairsHelper.kt
index 623055f6..efae207 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/helpers/AppPairsHelper.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/helpers/AppPairsHelper.kt
@@ -17,23 +17,23 @@
package com.android.wm.shell.flicker.helpers
import android.app.Instrumentation
-import android.graphics.Region
import com.android.server.wm.flicker.Flicker
import com.android.server.wm.flicker.helpers.WindowUtils
import com.android.server.wm.traces.common.FlickerComponentName
+import com.android.server.wm.traces.common.region.Region
class AppPairsHelper(
instrumentation: Instrumentation,
activityLabel: String,
component: FlickerComponentName
) : BaseAppHelper(instrumentation, activityLabel, component) {
- fun getPrimaryBounds(dividerBounds: Region): android.graphics.Region {
+ fun getPrimaryBounds(dividerBounds: Region): Region {
val primaryAppBounds = Region(0, 0, dividerBounds.bounds.right,
dividerBounds.bounds.bottom + WindowUtils.dockedStackDividerInset)
return primaryAppBounds
}
- fun getSecondaryBounds(dividerBounds: Region): android.graphics.Region {
+ fun getSecondaryBounds(dividerBounds: Region): Region {
val displayBounds = WindowUtils.displayBounds
val secondaryAppBounds = Region(0,
dividerBounds.bounds.bottom - WindowUtils.dockedStackDividerInset,
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/helpers/PipAppHelper.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/helpers/PipAppHelper.kt
index 2357b0d..8e6fa5f 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/helpers/PipAppHelper.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/helpers/PipAppHelper.kt
@@ -17,7 +17,6 @@
package com.android.wm.shell.flicker.helpers
import android.app.Instrumentation
-import android.graphics.Rect
import android.media.session.MediaController
import android.media.session.MediaSessionManager
import android.os.SystemClock
@@ -26,6 +25,7 @@
import androidx.test.uiautomator.Until
import com.android.server.wm.flicker.helpers.FIND_TIMEOUT
import com.android.server.wm.flicker.helpers.SYSTEMUI_PACKAGE
+import com.android.server.wm.traces.common.Rect
import com.android.server.wm.traces.parser.toFlickerComponent
import com.android.server.wm.traces.parser.windowmanager.WindowManagerStateHelper
import com.android.wm.shell.flicker.pip.tv.closeTvPipWindow
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 0448ec8..7d7add4 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
@@ -16,7 +16,6 @@
package com.android.wm.shell.flicker.legacysplitscreen
-import android.graphics.Region
import android.util.Rational
import android.view.Surface
import androidx.test.filters.FlakyTest
@@ -41,6 +40,7 @@
import com.android.server.wm.flicker.statusBarLayerIsVisible
import com.android.server.wm.flicker.statusBarLayerRotatesScales
import com.android.server.wm.flicker.statusBarWindowIsVisible
+import com.android.server.wm.traces.common.region.Region
import com.android.server.wm.traces.parser.toFlickerComponent
import com.android.wm.shell.flicker.DOCKED_STACK_DIVIDER_COMPONENT
import com.android.wm.shell.flicker.helpers.SimpleAppHelper
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 0cbfc92..d3bb008 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
@@ -128,8 +128,8 @@
@Presubmit
@Test
fun pipWindowRemainInsideVisibleBounds() {
- testSpec.assertWm {
- coversAtMost(displayBounds, pipApp.component)
+ testSpec.assertWmVisibleRegion(pipApp.component) {
+ coversAtMost(displayBounds)
}
}
@@ -140,8 +140,8 @@
@Presubmit
@Test
fun pipLayerRemainInsideVisibleBounds() {
- testSpec.assertLayers {
- coversAtMost(displayBounds, pipApp.component)
+ testSpec.assertLayersVisibleRegion(pipApp.component) {
+ coversAtMost(displayBounds)
}
}
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 3e7e2f5..f8a3aff 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
@@ -45,8 +45,8 @@
@Presubmit
@Test
open fun pipAppWindowRemainInsideVisibleBounds() {
- testSpec.assertWm {
- coversAtMost(displayBounds, pipApp.component)
+ testSpec.assertWmVisibleRegion(pipApp.component) {
+ coversAtMost(displayBounds)
}
}
@@ -57,8 +57,8 @@
@Presubmit
@Test
open fun pipAppLayerRemainInsideVisibleBounds() {
- testSpec.assertLayers {
- coversAtMost(displayBounds, pipApp.component)
+ testSpec.assertLayersVisibleRegion(pipApp.component) {
+ coversAtMost(displayBounds)
}
}
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 cba677b..52177c2 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
@@ -78,8 +78,8 @@
@Presubmit
@Test
fun pipWindowRemainInsideVisibleBounds() {
- testSpec.assertWm {
- coversAtMost(displayBounds, pipApp.component)
+ testSpec.assertWmVisibleRegion(pipApp.component) {
+ coversAtMost(displayBounds)
}
}
@@ -90,8 +90,8 @@
@Presubmit
@Test
fun pipLayerRemainInsideVisibleBounds() {
- testSpec.assertLayers {
- coversAtMost(displayBounds, pipApp.component)
+ testSpec.assertLayersVisibleRegion(pipApp.component) {
+ coversAtMost(displayBounds)
}
}
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 7ed0c49..f9e180e 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
@@ -25,8 +25,8 @@
import com.android.server.wm.flicker.annotation.Group3
import com.android.server.wm.flicker.dsl.FlickerBuilder
import com.android.server.wm.flicker.helpers.isShellTransitionsEnabled
-import com.android.server.wm.flicker.traces.RegionSubject
import org.junit.Assume.assumeFalse
+import com.android.server.wm.flicker.traces.region.RegionSubject
import org.junit.FixMethodOrder
import org.junit.Test
import org.junit.runner.RunWith
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/MovePipShelfHeightTransition.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/MovePipShelfHeightTransition.kt
index 6e0324c..0499e7d 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/MovePipShelfHeightTransition.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/MovePipShelfHeightTransition.kt
@@ -19,7 +19,7 @@
import android.platform.test.annotations.Presubmit
import com.android.launcher3.tapl.LauncherInstrumentation
import com.android.server.wm.flicker.FlickerTestParameter
-import com.android.server.wm.flicker.traces.RegionSubject
+import com.android.server.wm.flicker.traces.region.RegionSubject
import com.android.wm.shell.flicker.helpers.FixedAppHelper
import org.junit.Test
@@ -66,8 +66,8 @@
@Presubmit
@Test
open fun pipWindowRemainInsideVisibleBounds() {
- testSpec.assertWm {
- coversAtMost(displayBounds, pipApp.component)
+ testSpec.assertWmVisibleRegion(pipApp.component) {
+ coversAtMost(displayBounds)
}
}
@@ -78,8 +78,8 @@
@Presubmit
@Test
open fun pipLayerRemainInsideVisibleBounds() {
- testSpec.assertLayers {
- coversAtMost(displayBounds, pipApp.component)
+ testSpec.assertLayersVisibleRegion(pipApp.component) {
+ coversAtMost(displayBounds)
}
}
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 56c0949..b7bfa1b 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
@@ -24,8 +24,8 @@
import com.android.server.wm.flicker.FlickerTestParameterFactory
import com.android.server.wm.flicker.annotation.Group3
import com.android.server.wm.flicker.dsl.FlickerBuilder
+import com.android.server.wm.flicker.traces.region.RegionSubject
import com.android.server.wm.flicker.helpers.isShellTransitionsEnabled
-import com.android.server.wm.flicker.traces.RegionSubject
import org.junit.Assume.assumeFalse
import org.junit.FixMethodOrder
import org.junit.Test
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 62e230f..c36dfda 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
@@ -88,9 +88,9 @@
@Presubmit
@Test
fun pipInVisibleBounds() {
- testSpec.assertWm {
+ testSpec.assertWmVisibleRegion(pipApp.component) {
val displayBounds = WindowUtils.getDisplayBounds(testSpec.startRotation)
- coversAtMost(displayBounds, pipApp.component)
+ coversAtMost(displayBounds)
}
}
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 e3f544a..df58194 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
@@ -101,8 +101,8 @@
@FlakyTest(bugId = 161435597)
@Test
fun pipWindowInsideDisplayBounds() {
- testSpec.assertWm {
- coversAtMost(displayBounds, pipApp.component)
+ testSpec.assertWmVisibleRegion(pipApp.component) {
+ coversAtMost(displayBounds)
}
}
@@ -119,8 +119,8 @@
@FlakyTest(bugId = 161435597)
@Test
fun pipLayerInsideDisplayBounds() {
- testSpec.assertLayers {
- coversAtMost(displayBounds, pipApp.component)
+ testSpec.assertLayersVisibleRegion(pipApp.component) {
+ coversAtMost(displayBounds)
}
}
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/onehanded/OneHandedControllerTest.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/onehanded/OneHandedControllerTest.java
index 0a3a849..16bc5075 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/onehanded/OneHandedControllerTest.java
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/onehanded/OneHandedControllerTest.java
@@ -109,6 +109,7 @@
mSpiedTransitionState = spy(new OneHandedState());
when(mMockDisplayController.getDisplay(anyInt())).thenReturn(mDisplay);
+ when(mMockDisplayController.getDisplayLayout(anyInt())).thenReturn(null);
when(mMockDisplayAreaOrganizer.getDisplayAreaTokenMap()).thenReturn(new ArrayMap<>());
when(mMockDisplayAreaOrganizer.isReady()).thenReturn(true);
when(mMockBackgroundOrganizer.isRegistered()).thenReturn(true);
@@ -153,6 +154,13 @@
}
@Test
+ public void testNullDisplayLayout() {
+ mSpiedOneHandedController.updateDisplayLayout(0);
+
+ verify(mMockDisplayAreaOrganizer, never()).setDisplayLayout(any());
+ }
+
+ @Test
public void testStartOneHandedShouldTriggerScheduleOffset() {
mSpiedTransitionState.setState(STATE_NONE);
mSpiedOneHandedController.setOneHandedEnabled(true);
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/onehanded/OneHandedDisplayAreaOrganizerTest.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/onehanded/OneHandedDisplayAreaOrganizerTest.java
index ef16fd3..1d92a48 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/onehanded/OneHandedDisplayAreaOrganizerTest.java
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/onehanded/OneHandedDisplayAreaOrganizerTest.java
@@ -432,4 +432,11 @@
assertThat(testSpiedDisplayAreaOrganizer.isReady()).isFalse();
}
+
+ @Test
+ public void testDisplayArea_setDisplayLayout_should_updateDisplayBounds() {
+ mSpiedDisplayAreaOrganizer.setDisplayLayout(mDisplayLayout);
+
+ verify(mSpiedDisplayAreaOrganizer).updateDisplayBounds();
+ }
}
diff --git a/libs/hwui/jni/RenderEffect.cpp b/libs/hwui/jni/RenderEffect.cpp
index a48d7f7..213f35a 100644
--- a/libs/hwui/jni/RenderEffect.cpp
+++ b/libs/hwui/jni/RenderEffect.cpp
@@ -127,6 +127,32 @@
return reinterpret_cast<jlong>(shaderFilter.release());
}
+static inline int ThrowIAEFmt(JNIEnv* env, const char* fmt, ...) {
+ va_list args;
+ va_start(args, fmt);
+ int ret = jniThrowExceptionFmt(env, "java/lang/IllegalArgumentException", fmt, args);
+ va_end(args);
+ return ret;
+}
+
+static jlong createRuntimeShaderEffect(JNIEnv* env, jobject, jlong shaderBuilderHandle,
+ jstring inputShaderName) {
+ SkRuntimeShaderBuilder* builder =
+ reinterpret_cast<SkRuntimeShaderBuilder*>(shaderBuilderHandle);
+ ScopedUtfChars name(env, inputShaderName);
+
+ if (builder->child(name.c_str()).fChild == nullptr) {
+ ThrowIAEFmt(env,
+ "unable to find a uniform with the name '%s' of the correct "
+ "type defined by the provided RuntimeShader",
+ name.c_str());
+ return 0;
+ }
+
+ sk_sp<SkImageFilter> filter = SkImageFilters::RuntimeShader(*builder, name.c_str(), nullptr);
+ return reinterpret_cast<jlong>(filter.release());
+}
+
static void RenderEffect_safeUnref(SkImageFilter* filter) {
SkSafeUnref(filter);
}
@@ -136,15 +162,16 @@
}
static const JNINativeMethod gRenderEffectMethods[] = {
- {"nativeGetFinalizer", "()J", (void*)getRenderEffectFinalizer},
- {"nativeCreateOffsetEffect", "(FFJ)J", (void*)createOffsetEffect},
- {"nativeCreateBlurEffect", "(FFJI)J", (void*)createBlurEffect},
- {"nativeCreateBitmapEffect", "(JFFFFFFFF)J", (void*)createBitmapEffect},
- {"nativeCreateColorFilterEffect", "(JJ)J", (void*)createColorFilterEffect},
- {"nativeCreateBlendModeEffect", "(JJI)J", (void*)createBlendModeEffect},
- {"nativeCreateChainEffect", "(JJ)J", (void*)createChainEffect},
- {"nativeCreateShaderEffect", "(J)J", (void*)createShaderEffect}
-};
+ {"nativeGetFinalizer", "()J", (void*)getRenderEffectFinalizer},
+ {"nativeCreateOffsetEffect", "(FFJ)J", (void*)createOffsetEffect},
+ {"nativeCreateBlurEffect", "(FFJI)J", (void*)createBlurEffect},
+ {"nativeCreateBitmapEffect", "(JFFFFFFFF)J", (void*)createBitmapEffect},
+ {"nativeCreateColorFilterEffect", "(JJ)J", (void*)createColorFilterEffect},
+ {"nativeCreateBlendModeEffect", "(JJI)J", (void*)createBlendModeEffect},
+ {"nativeCreateChainEffect", "(JJ)J", (void*)createChainEffect},
+ {"nativeCreateShaderEffect", "(J)J", (void*)createShaderEffect},
+ {"nativeCreateRuntimeShaderEffect", "(JLjava/lang/String;)J",
+ (void*)createRuntimeShaderEffect}};
int register_android_graphics_RenderEffect(JNIEnv* env) {
android::RegisterMethodsOrDie(env, "android/graphics/RenderEffect",
diff --git a/libs/hwui/jni/android_graphics_HardwareRenderer.cpp b/libs/hwui/jni/android_graphics_HardwareRenderer.cpp
index bd93a4f..27865b3 100644
--- a/libs/hwui/jni/android_graphics_HardwareRenderer.cpp
+++ b/libs/hwui/jni/android_graphics_HardwareRenderer.cpp
@@ -609,10 +609,19 @@
LOG_ALWAYS_FATAL_IF(env->GetJavaVM(&vm) != JNI_OK, "Unable to get Java VM");
auto globalCallbackRef = std::make_shared<JGlobalRefHolder>(vm,
env->NewGlobalRef(frameCallback));
- proxy->setFrameCallback([globalCallbackRef](int64_t frameNr) {
+ proxy->setFrameCallback([globalCallbackRef](int32_t syncResult,
+ int64_t frameNr) -> std::function<void(bool)> {
JNIEnv* env = getenv(globalCallbackRef->vm());
- env->CallVoidMethod(globalCallbackRef->object(), gFrameDrawingCallback.onFrameDraw,
- static_cast<jlong>(frameNr));
+ ScopedLocalRef<jobject> frameCommitCallback(
+ env, env->CallObjectMethod(
+ globalCallbackRef->object(), gFrameDrawingCallback.onFrameDraw,
+ static_cast<jint>(syncResult), static_cast<jlong>(frameNr)));
+ if (frameCommitCallback == nullptr) {
+ return nullptr;
+ }
+ sp<FrameCommitWrapper> wrapper =
+ sp<FrameCommitWrapper>::make(env, frameCommitCallback.get());
+ return [wrapper](bool didProduceBuffer) { wrapper->onFrameCommit(didProduceBuffer); };
});
}
}
@@ -623,7 +632,7 @@
if (!callback) {
proxy->setFrameCommitCallback(nullptr);
} else {
- sp<FrameCommitWrapper> wrapper = new FrameCommitWrapper{env, callback};
+ sp<FrameCommitWrapper> wrapper = sp<FrameCommitWrapper>::make(env, callback);
proxy->setFrameCommitCallback(
[wrapper](bool didProduceBuffer) { wrapper->onFrameCommit(didProduceBuffer); });
}
@@ -1003,8 +1012,9 @@
jclass frameCallbackClass = FindClassOrDie(env,
"android/graphics/HardwareRenderer$FrameDrawingCallback");
- gFrameDrawingCallback.onFrameDraw = GetMethodIDOrDie(env, frameCallbackClass,
- "onFrameDraw", "(J)V");
+ gFrameDrawingCallback.onFrameDraw =
+ GetMethodIDOrDie(env, frameCallbackClass, "onFrameDraw",
+ "(IJ)Landroid/graphics/HardwareRenderer$FrameCommitCallback;");
jclass frameCommitClass =
FindClassOrDie(env, "android/graphics/HardwareRenderer$FrameCommitCallback");
diff --git a/libs/hwui/renderthread/DrawFrameTask.cpp b/libs/hwui/renderthread/DrawFrameTask.cpp
index 94aedd0..8c98c72 100644
--- a/libs/hwui/renderthread/DrawFrameTask.cpp
+++ b/libs/hwui/renderthread/DrawFrameTask.cpp
@@ -158,7 +158,8 @@
// Grab a copy of everything we need
CanvasContext* context = mContext;
- std::function<void(int64_t)> frameCallback = std::move(mFrameCallback);
+ std::function<std::function<void(bool)>(int32_t, int64_t)> frameCallback =
+ std::move(mFrameCallback);
std::function<void()> frameCompleteCallback = std::move(mFrameCompleteCallback);
mFrameCallback = nullptr;
mFrameCompleteCallback = nullptr;
@@ -173,8 +174,13 @@
// Even if we aren't drawing this vsync pulse the next frame number will still be accurate
if (CC_UNLIKELY(frameCallback)) {
- context->enqueueFrameWork(
- [frameCallback, frameNr = context->getFrameNumber()]() { frameCallback(frameNr); });
+ context->enqueueFrameWork([frameCallback, context, syncResult = mSyncResult,
+ frameNr = context->getFrameNumber()]() {
+ auto frameCommitCallback = std::move(frameCallback(syncResult, frameNr));
+ if (frameCommitCallback) {
+ context->addFrameCommitListener(std::move(frameCommitCallback));
+ }
+ });
}
nsecs_t dequeueBufferDuration = 0;
diff --git a/libs/hwui/renderthread/DrawFrameTask.h b/libs/hwui/renderthread/DrawFrameTask.h
index e3ea802..8ad8abc 100644
--- a/libs/hwui/renderthread/DrawFrameTask.h
+++ b/libs/hwui/renderthread/DrawFrameTask.h
@@ -77,7 +77,7 @@
void run();
- void setFrameCallback(std::function<void(int64_t)>&& callback) {
+ void setFrameCallback(std::function<std::function<void(bool)>(int32_t, int64_t)>&& callback) {
mFrameCallback = std::move(callback);
}
@@ -126,7 +126,7 @@
int64_t mFrameInfo[UI_THREAD_FRAME_INFO_SIZE];
- std::function<void(int64_t)> mFrameCallback;
+ std::function<std::function<void(bool)>(int32_t, int64_t)> mFrameCallback;
std::function<void(bool)> mFrameCommitCallback;
std::function<void()> mFrameCompleteCallback;
diff --git a/libs/hwui/renderthread/RenderProxy.cpp b/libs/hwui/renderthread/RenderProxy.cpp
index 430c4d3..026699c 100644
--- a/libs/hwui/renderthread/RenderProxy.cpp
+++ b/libs/hwui/renderthread/RenderProxy.cpp
@@ -327,7 +327,8 @@
[this, cb = callback]() { mContext->setPrepareSurfaceControlForWebviewCallback(cb); });
}
-void RenderProxy::setFrameCallback(std::function<void(int64_t)>&& callback) {
+void RenderProxy::setFrameCallback(
+ std::function<std::function<void(bool)>(int32_t, int64_t)>&& callback) {
mDrawFrameTask.setFrameCallback(std::move(callback));
}
diff --git a/libs/hwui/renderthread/RenderProxy.h b/libs/hwui/renderthread/RenderProxy.h
index 6d46be4..491dbd7 100644
--- a/libs/hwui/renderthread/RenderProxy.h
+++ b/libs/hwui/renderthread/RenderProxy.h
@@ -124,7 +124,7 @@
void setASurfaceTransactionCallback(
const std::function<bool(int64_t, int64_t, int64_t)>& callback);
void setPrepareSurfaceControlForWebviewCallback(const std::function<void()>& callback);
- void setFrameCallback(std::function<void(int64_t)>&& callback);
+ void setFrameCallback(std::function<std::function<void(bool)>(int32_t, int64_t)>&& callback);
void setFrameCommitCallback(std::function<void(bool)>&& callback);
void setFrameCompleteCallback(std::function<void()>&& callback);
diff --git a/location/java/android/location/GnssMeasurementRequest.java b/location/java/android/location/GnssMeasurementRequest.java
index f509252..71cb0e3 100644
--- a/location/java/android/location/GnssMeasurementRequest.java
+++ b/location/java/android/location/GnssMeasurementRequest.java
@@ -16,10 +16,14 @@
package android.location;
+import android.annotation.IntRange;
import android.annotation.NonNull;
import android.annotation.SystemApi;
import android.os.Parcel;
import android.os.Parcelable;
+import android.util.TimeUtils;
+
+import com.android.internal.util.Preconditions;
import java.util.Objects;
@@ -29,13 +33,16 @@
public final class GnssMeasurementRequest implements Parcelable {
private final boolean mCorrelationVectorOutputsEnabled;
private final boolean mFullTracking;
+ private final int mIntervalMillis;
/**
* Creates a {@link GnssMeasurementRequest} with a full list of parameters.
*/
- private GnssMeasurementRequest(boolean fullTracking, boolean correlationVectorOutputsEnabled) {
+ private GnssMeasurementRequest(boolean fullTracking, boolean correlationVectorOutputsEnabled,
+ int intervalMillis) {
mFullTracking = fullTracking;
mCorrelationVectorOutputsEnabled = correlationVectorOutputsEnabled;
+ mIntervalMillis = intervalMillis;
}
/**
@@ -68,13 +75,26 @@
return mFullTracking;
}
+ /**
+ * Represents the requested time interval between the reported measurements in milliseconds.
+ *
+ * <p>If the time interval is not set, the default value is 0, which means the fastest rate the
+ * GNSS chipset can report.
+ *
+ * <p>The GNSS chipset may report measurements with a rate faster than requested.
+ */
+ public @IntRange(from = 0) int getIntervalMillis() {
+ return mIntervalMillis;
+ }
+
@NonNull
public static final Creator<GnssMeasurementRequest> CREATOR =
new Creator<GnssMeasurementRequest>() {
@Override
@NonNull
public GnssMeasurementRequest createFromParcel(@NonNull Parcel parcel) {
- return new GnssMeasurementRequest(parcel.readBoolean(), parcel.readBoolean());
+ return new GnssMeasurementRequest(parcel.readBoolean(), parcel.readBoolean(),
+ parcel.readInt());
}
@Override
@@ -87,6 +107,7 @@
public void writeToParcel(@NonNull Parcel parcel, int flags) {
parcel.writeBoolean(mFullTracking);
parcel.writeBoolean(mCorrelationVectorOutputsEnabled);
+ parcel.writeInt(mIntervalMillis);
}
@NonNull
@@ -94,11 +115,13 @@
public String toString() {
StringBuilder s = new StringBuilder();
s.append("GnssMeasurementRequest[");
+ s.append("@");
+ TimeUtils.formatDuration(mIntervalMillis, s);
if (mFullTracking) {
- s.append("FullTracking");
+ s.append(", FullTracking");
}
if (mCorrelationVectorOutputsEnabled) {
- s.append(", CorrelationVectorOutPuts");
+ s.append(", CorrelationVectorOutputs");
}
s.append(']');
return s.toString();
@@ -115,12 +138,15 @@
if (mCorrelationVectorOutputsEnabled != other.mCorrelationVectorOutputsEnabled) {
return false;
}
+ if (mIntervalMillis != other.mIntervalMillis) {
+ return false;
+ }
return true;
}
@Override
public int hashCode() {
- return Objects.hash(mFullTracking, mCorrelationVectorOutputsEnabled);
+ return Objects.hash(mFullTracking, mCorrelationVectorOutputsEnabled, mIntervalMillis);
}
@Override
@@ -132,6 +158,7 @@
public static final class Builder {
private boolean mCorrelationVectorOutputsEnabled;
private boolean mFullTracking;
+ private int mIntervalMillis;
/**
* Constructs a {@link Builder} instance.
@@ -145,6 +172,7 @@
public Builder(@NonNull GnssMeasurementRequest request) {
mCorrelationVectorOutputsEnabled = request.isCorrelationVectorOutputsEnabled();
mFullTracking = request.isFullTracking();
+ mIntervalMillis = request.getIntervalMillis();
}
/**
@@ -183,10 +211,25 @@
return this;
}
+ /**
+ * Set the time interval between the reported measurements in milliseconds, which is 0 by
+ * default.
+ *
+ * <p>An interval of 0 milliseconds means the fastest rate the chipset can report.
+ *
+ * <p>The GNSS chipset may report measurements with a rate faster than requested.
+ */
+ @NonNull public Builder setIntervalMillis(@IntRange(from = 0) int value) {
+ mIntervalMillis = Preconditions.checkArgumentInRange(value, 0, Integer.MAX_VALUE,
+ "intervalMillis");
+ return this;
+ }
+
/** Builds a {@link GnssMeasurementRequest} instance as specified by this builder. */
@NonNull
public GnssMeasurementRequest build() {
- return new GnssMeasurementRequest(mFullTracking, mCorrelationVectorOutputsEnabled);
+ return new GnssMeasurementRequest(mFullTracking, mCorrelationVectorOutputsEnabled,
+ mIntervalMillis);
}
}
}
diff --git a/media/java/android/media/AudioAttributes.java b/media/java/android/media/AudioAttributes.java
index 9993ce9..85e49cc 100644
--- a/media/java/android/media/AudioAttributes.java
+++ b/media/java/android/media/AudioAttributes.java
@@ -1187,6 +1187,9 @@
case AudioSystem.STREAM_ACCESSIBILITY:
mContentType = CONTENT_TYPE_SPEECH;
break;
+ case AudioSystem.STREAM_ASSISTANT:
+ mContentType = CONTENT_TYPE_SPEECH;
+ break;
default:
Log.e(TAG, "Invalid stream type " + streamType + " for AudioAttributes");
}
@@ -1611,6 +1614,8 @@
return USAGE_VOICE_COMMUNICATION_SIGNALLING;
case AudioSystem.STREAM_ACCESSIBILITY:
return USAGE_ASSISTANCE_ACCESSIBILITY;
+ case AudioSystem.STREAM_ASSISTANT:
+ return USAGE_ASSISTANT;
case AudioSystem.STREAM_TTS:
default:
return USAGE_UNKNOWN;
diff --git a/media/java/android/media/audiopolicy/AudioMixingRule.java b/media/java/android/media/audiopolicy/AudioMixingRule.java
index c912759..1f89f99 100644
--- a/media/java/android/media/audiopolicy/AudioMixingRule.java
+++ b/media/java/android/media/audiopolicy/AudioMixingRule.java
@@ -145,10 +145,8 @@
final int match_rule = mRule & ~RULE_EXCLUSION_MASK;
switch (match_rule) {
case RULE_MATCH_ATTRIBUTE_USAGE:
- dest.writeInt(mAttr.getSystemUsage());
- break;
case RULE_MATCH_ATTRIBUTE_CAPTURE_PRESET:
- dest.writeInt(mAttr.getCapturePreset());
+ mAttr.writeToParcel(dest, AudioAttributes.FLATTEN_TAGS/*flags*/);
break;
case RULE_MATCH_UID:
case RULE_MATCH_USERID:
@@ -266,12 +264,14 @@
public boolean isForCallRedirection() {
for (AudioMixMatchCriterion criterion : mCriteria) {
if (criterion.mAttr != null
- && (criterion.mRule == RULE_MATCH_ATTRIBUTE_USAGE
- && criterion.mAttr.getUsage() == AudioAttributes.USAGE_VOICE_COMMUNICATION)
+ && criterion.mAttr.isForCallRedirection()
+ && ((criterion.mRule == RULE_MATCH_ATTRIBUTE_USAGE
+ && (criterion.mAttr.getUsage() == AudioAttributes.USAGE_VOICE_COMMUNICATION
+ || criterion.mAttr.getUsage()
+ == AudioAttributes.USAGE_VOICE_COMMUNICATION_SIGNALLING))
|| (criterion.mRule == RULE_MATCH_ATTRIBUTE_CAPTURE_PRESET
- && criterion.mAttr.getCapturePreset()
- == MediaRecorder.AudioSource.VOICE_COMMUNICATION)
- && criterion.mAttr.isForCallRedirection()) {
+ && (criterion.mAttr.getCapturePreset()
+ == MediaRecorder.AudioSource.VOICE_COMMUNICATION)))) {
return true;
}
}
@@ -713,19 +713,8 @@
Integer intProp = null;
switch (match_rule) {
case RULE_MATCH_ATTRIBUTE_USAGE:
- int usage = in.readInt();
- if (AudioAttributes.isSystemUsage(usage)) {
- attr = new AudioAttributes.Builder()
- .setSystemUsage(usage).build();
- } else {
- attr = new AudioAttributes.Builder()
- .setUsage(usage).build();
- }
- break;
case RULE_MATCH_ATTRIBUTE_CAPTURE_PRESET:
- int preset = in.readInt();
- attr = new AudioAttributes.Builder()
- .setInternalCapturePreset(preset).build();
+ attr = AudioAttributes.CREATOR.createFromParcel(in);
break;
case RULE_MATCH_UID:
case RULE_MATCH_USERID:
diff --git a/media/java/android/media/tv/AdRequest.aidl b/media/java/android/media/tv/AdRequest.aidl
new file mode 100644
index 0000000..ebfd748
--- /dev/null
+++ b/media/java/android/media/tv/AdRequest.aidl
@@ -0,0 +1,19 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.media.tv;
+
+parcelable AdRequest;
diff --git a/media/java/android/media/tv/AdRequest.java b/media/java/android/media/tv/AdRequest.java
new file mode 100644
index 0000000..536baf2
--- /dev/null
+++ b/media/java/android/media/tv/AdRequest.java
@@ -0,0 +1,135 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.media.tv;
+
+import android.annotation.NonNull;
+import android.annotation.StringDef;
+import android.os.Bundle;
+import android.os.Parcel;
+import android.os.ParcelFileDescriptor;
+import android.os.Parcelable;
+
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+
+/** @hide */
+public final class AdRequest implements Parcelable {
+ @Retention(RetentionPolicy.SOURCE)
+ @StringDef(prefix = "REQUEST_TYPE_", value = {
+ REQUEST_TYPE_START,
+ REQUEST_TYPE_STOP
+ })
+ public @interface RequestType {}
+
+ public static final String REQUEST_TYPE_START = "START";
+ public static final String REQUEST_TYPE_STOP = "STOP";
+
+ public static final @NonNull Parcelable.Creator<AdRequest> CREATOR =
+ new Parcelable.Creator<AdRequest>() {
+ @Override
+ public AdRequest createFromParcel(Parcel source) {
+ return new AdRequest(source);
+ }
+
+ @Override
+ public AdRequest[] newArray(int size) {
+ return new AdRequest[size];
+ }
+ };
+
+ private final int mId;
+ private final @RequestType String mRequestType;
+ private final ParcelFileDescriptor mFileDescriptor;
+ private final long mStartTime;
+ private final long mStopTime;
+ private final long mEchoInterval;
+ private final String mMediaFileType;
+ private final Bundle mMetadata;
+
+ public AdRequest(int id, @RequestType String requestType, ParcelFileDescriptor fileDescriptor,
+ long startTime, long stopTime, long echoInterval, String mediaFileType,
+ Bundle metadata) {
+ mId = id;
+ mRequestType = requestType;
+ mFileDescriptor = fileDescriptor;
+ mStartTime = startTime;
+ mStopTime = stopTime;
+ mEchoInterval = echoInterval;
+ mMediaFileType = mediaFileType;
+ mMetadata = metadata;
+ }
+
+ private AdRequest(Parcel source) {
+ mId = source.readInt();
+ mRequestType = source.readString();
+ mFileDescriptor = source.readFileDescriptor();
+ mStartTime = source.readLong();
+ mStopTime = source.readLong();
+ mEchoInterval = source.readLong();
+ mMediaFileType = source.readString();
+ mMetadata = source.readBundle();
+ }
+
+ public int getId() {
+ return mId;
+ }
+
+ public @RequestType String getRequestType() {
+ return mRequestType;
+ }
+
+ public ParcelFileDescriptor getFileDescriptor() {
+ return mFileDescriptor;
+ }
+
+ public long getStartTime() {
+ return mStartTime;
+ }
+
+ public long getStopTime() {
+ return mStopTime;
+ }
+
+ public long getEchoInterval() {
+ return mEchoInterval;
+ }
+
+ public String getMediaFileType() {
+ return mMediaFileType;
+ }
+
+ public Bundle getMetadata() {
+ return mMetadata;
+ }
+
+ @Override
+ public int describeContents() {
+ return 0;
+ }
+
+ @Override
+ public void writeToParcel(@NonNull Parcel dest, int flags) {
+ dest.writeInt(mId);
+ dest.writeString(mRequestType);
+ mFileDescriptor.writeToParcel(dest, flags);
+ dest.writeLong(mStartTime);
+ dest.writeLong(mStopTime);
+ dest.writeLong(mEchoInterval);
+ dest.writeString(mMediaFileType);
+ dest.writeBundle(mMetadata);
+ }
+}
diff --git a/media/java/android/media/tv/AdResponse.aidl b/media/java/android/media/tv/AdResponse.aidl
new file mode 100644
index 0000000..9c09a0a
--- /dev/null
+++ b/media/java/android/media/tv/AdResponse.aidl
@@ -0,0 +1,19 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.media.tv;
+
+parcelable AdResponse;
diff --git a/media/java/android/media/tv/AdResponse.java b/media/java/android/media/tv/AdResponse.java
new file mode 100644
index 0000000..28cf5ac
--- /dev/null
+++ b/media/java/android/media/tv/AdResponse.java
@@ -0,0 +1,96 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.media.tv;
+
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.annotation.StringDef;
+import android.os.Parcel;
+import android.os.Parcelable;
+
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+
+/** @hide */
+public final class AdResponse implements Parcelable {
+ @Retention(RetentionPolicy.SOURCE)
+ @StringDef(prefix = "RESPONSE_TYPE_", value = {
+ RESPONSE_TYPE_PLAYING,
+ RESPONSE_TYPE_FINISHED,
+ RESPONSE_TYPE_STOPPED,
+ RESPONSE_TYPE_ERROR
+ })
+ public @interface ResponseType {}
+
+ public static final String RESPONSE_TYPE_PLAYING = "PLAYING";
+ public static final String RESPONSE_TYPE_FINISHED = "FINISHED";
+ public static final String RESPONSE_TYPE_STOPPED = "STOPPED";
+ public static final String RESPONSE_TYPE_ERROR = "ERROR";
+
+ public static final @NonNull Parcelable.Creator<AdResponse> CREATOR =
+ new Parcelable.Creator<AdResponse>() {
+ @Override
+ public AdResponse createFromParcel(Parcel source) {
+ return new AdResponse(source);
+ }
+
+ @Override
+ public AdResponse[] newArray(int size) {
+ return new AdResponse[size];
+ }
+ };
+
+ private final int mId;
+ private final @ResponseType String mResponseType;
+ private final Long mElapsedTime;
+
+ public AdResponse(int id, @ResponseType String responseType, @Nullable Long elapsedTime) {
+ mId = id;
+ mResponseType = responseType;
+ mElapsedTime = elapsedTime;
+ }
+
+ private AdResponse(Parcel source) {
+ mId = source.readInt();
+ mResponseType = source.readString();
+ mElapsedTime = (Long) source.readValue(Long.class.getClassLoader());
+ }
+
+ public int getId() {
+ return mId;
+ }
+
+ public @ResponseType String getResponseType() {
+ return mResponseType;
+ }
+
+ public Long getElapsedTime() {
+ return mElapsedTime;
+ }
+
+ @Override
+ public int describeContents() {
+ return 0;
+ }
+
+ @Override
+ public void writeToParcel(@NonNull Parcel dest, int flags) {
+ dest.writeInt(mId);
+ dest.writeString(mResponseType);
+ dest.writeValue(mElapsedTime);
+ }
+}
diff --git a/media/java/android/media/tv/ITvInputClient.aidl b/media/java/android/media/tv/ITvInputClient.aidl
index de13fc1..f4f55e4 100644
--- a/media/java/android/media/tv/ITvInputClient.aidl
+++ b/media/java/android/media/tv/ITvInputClient.aidl
@@ -17,6 +17,7 @@
package android.media.tv;
import android.content.ComponentName;
+import android.media.tv.AdResponse;
import android.media.tv.AitInfo;
import android.media.tv.BroadcastInfoResponse;
import android.media.tv.ITvInputSession;
@@ -54,4 +55,7 @@
// For broadcast info
void onBroadcastInfoResponse(in BroadcastInfoResponse response, int seq);
+
+ // For ad response
+ void onAdResponse(in AdResponse response, int seq);
}
diff --git a/media/java/android/media/tv/ITvInputManager.aidl b/media/java/android/media/tv/ITvInputManager.aidl
index a6e0217..d34e636 100644
--- a/media/java/android/media/tv/ITvInputManager.aidl
+++ b/media/java/android/media/tv/ITvInputManager.aidl
@@ -20,6 +20,7 @@
import android.content.Intent;
import android.graphics.Rect;
import android.media.PlaybackParams;
+import android.media.tv.AdRequest;
import android.media.tv.BroadcastInfoRequest;
import android.media.tv.DvbDeviceInfo;
import android.media.tv.ITvInputClient;
@@ -106,6 +107,9 @@
void requestBroadcastInfo(in IBinder sessionToken, in BroadcastInfoRequest request, int userId);
void removeBroadcastInfo(in IBinder sessionToken, int id, int userId);
+ // For ad request
+ void requestAd(in IBinder sessionToken, in AdRequest request, int userId);
+
// For TV input hardware binding
List<TvInputHardwareInfo> getHardwareList();
ITvInputHardware acquireTvInputHardware(int deviceId, in ITvInputHardwareCallback callback,
diff --git a/media/java/android/media/tv/ITvInputSession.aidl b/media/java/android/media/tv/ITvInputSession.aidl
index 025e6f1c..f427501 100644
--- a/media/java/android/media/tv/ITvInputSession.aidl
+++ b/media/java/android/media/tv/ITvInputSession.aidl
@@ -18,6 +18,7 @@
import android.graphics.Rect;
import android.media.PlaybackParams;
+import android.media.tv.AdRequest;
import android.media.tv.BroadcastInfoRequest;
import android.media.tv.TvTrackInfo;
import android.net.Uri;
@@ -67,4 +68,7 @@
// For broadcast info
void requestBroadcastInfo(in BroadcastInfoRequest request);
void removeBroadcastInfo(int id);
+
+ // For ad request
+ void requestAd(in AdRequest request);
}
diff --git a/media/java/android/media/tv/ITvInputSessionCallback.aidl b/media/java/android/media/tv/ITvInputSessionCallback.aidl
index 9a0aaa3..9830e78 100644
--- a/media/java/android/media/tv/ITvInputSessionCallback.aidl
+++ b/media/java/android/media/tv/ITvInputSessionCallback.aidl
@@ -16,6 +16,7 @@
package android.media.tv;
+import android.media.tv.AdResponse;
import android.media.tv.AitInfo;
import android.media.tv.BroadcastInfoResponse;
import android.media.tv.ITvInputSession;
@@ -51,4 +52,7 @@
// For broadcast info
void onBroadcastInfoResponse(in BroadcastInfoResponse response);
+
+ // For ad response
+ void onAdResponse(in AdResponse response);
}
diff --git a/media/java/android/media/tv/ITvInputSessionWrapper.java b/media/java/android/media/tv/ITvInputSessionWrapper.java
index ef6ef91..418ab2c 100644
--- a/media/java/android/media/tv/ITvInputSessionWrapper.java
+++ b/media/java/android/media/tv/ITvInputSessionWrapper.java
@@ -73,6 +73,7 @@
private static final int DO_REQUEST_BROADCAST_INFO = 24;
private static final int DO_REMOVE_BROADCAST_INFO = 25;
private static final int DO_SET_IAPP_NOTIFICATION_ENABLED = 26;
+ private static final int DO_REQUEST_AD = 27;
private final boolean mIsRecordingSession;
private final HandlerCaller mCaller;
@@ -249,6 +250,10 @@
mTvInputSessionImpl.setIAppNotificationEnabled((Boolean) msg.obj);
break;
}
+ case DO_REQUEST_AD: {
+ mTvInputSessionImpl.requestAd((AdRequest) msg.obj);
+ break;
+ }
default: {
Log.w(TAG, "Unhandled message code: " + msg.what);
break;
@@ -414,6 +419,11 @@
mCaller.executeOrSendMessage(mCaller.obtainMessageI(DO_REMOVE_BROADCAST_INFO, requestId));
}
+ @Override
+ public void requestAd(AdRequest request) {
+ mCaller.executeOrSendMessage(mCaller.obtainMessageO(DO_REQUEST_AD, request));
+ }
+
private final class TvInputEventReceiver extends InputEventReceiver {
public TvInputEventReceiver(InputChannel inputChannel, Looper looper) {
super(inputChannel, looper);
diff --git a/media/java/android/media/tv/TvInputManager.java b/media/java/android/media/tv/TvInputManager.java
index cde2f5a..ad86002 100644
--- a/media/java/android/media/tv/TvInputManager.java
+++ b/media/java/android/media/tv/TvInputManager.java
@@ -894,6 +894,19 @@
});
}
}
+
+ void postAdResponse(final AdResponse response) {
+ if (mSession.mIAppNotificationEnabled) {
+ mHandler.post(new Runnable() {
+ @Override
+ public void run() {
+ if (mSession.getIAppSession() != null) {
+ mSession.getIAppSession().notifyAdResponse(response);
+ }
+ }
+ });
+ }
+ }
}
/**
@@ -1317,6 +1330,18 @@
record.postBroadcastInfoResponse(response);
}
}
+
+ @Override
+ public void onAdResponse(AdResponse response, int seq) {
+ synchronized (mSessionCallbackRecordMap) {
+ SessionCallbackRecord record = mSessionCallbackRecordMap.get(seq);
+ if (record == null) {
+ Log.e(TAG, "Callback not found for seq " + seq);
+ return;
+ }
+ record.postAdResponse(response);
+ }
+ }
};
ITvInputManagerCallback managerCallback = new ITvInputManagerCallback.Stub() {
@Override
@@ -3033,6 +3058,18 @@
}
}
+ public void requestAd(AdRequest request) {
+ if (mToken == null) {
+ Log.w(TAG, "The session has been already released");
+ return;
+ }
+ try {
+ mService.requestAd(mToken, request, mUserId);
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
+ }
+ }
+
private final class InputEventHandler extends Handler {
public static final int MSG_SEND_INPUT_EVENT = 1;
public static final int MSG_TIMEOUT_INPUT_EVENT = 2;
diff --git a/media/java/android/media/tv/TvInputService.java b/media/java/android/media/tv/TvInputService.java
index ee33e5c..bd5a343 100755
--- a/media/java/android/media/tv/TvInputService.java
+++ b/media/java/android/media/tv/TvInputService.java
@@ -849,7 +849,7 @@
/**
* Notifies response for broadcast info.
*
- * @param response
+ * @param response broadcast info response.
* @hide
*/
public void notifyBroadcastInfoResponse(@NonNull final BroadcastInfoResponse response) {
@@ -869,6 +869,29 @@
});
}
+ /**
+ * Notifies response for advertisement.
+ *
+ * @param response advertisement response.
+ * @hide
+ */
+ public void notifyAdResponse(@NonNull final AdResponse response) {
+ executeOrPostRunnableOnMainThread(new Runnable() {
+ @MainThread
+ @Override
+ public void run() {
+ try {
+ if (DEBUG) Log.d(TAG, "notifyAdResponse");
+ if (mSessionCallback != null) {
+ mSessionCallback.onAdResponse(response);
+ }
+ } catch (RemoteException e) {
+ Log.w(TAG, "error in notifyAdResponse", e);
+ }
+ }
+ });
+ }
+
private void notifyTimeShiftStartPositionChanged(final long timeMs) {
executeOrPostRunnableOnMainThread(new Runnable() {
@MainThread
@@ -1038,6 +1061,8 @@
public abstract void onSetStreamVolume(@FloatRange(from = 0.0, to = 1.0) float volume);
/**
+ * called when broadcast info is requested.
+ *
* @param request broadcast info request
* @hide
*/
@@ -1051,6 +1076,15 @@
}
/**
+ * called when advertisement is requested.
+ *
+ * @param request advertisement request
+ * @hide
+ */
+ public void onRequestAd(@NonNull AdRequest request) {
+ }
+
+ /**
* Tunes to a given channel.
*
* <p>No video will be displayed until {@link #notifyVideoAvailable()} is called.
@@ -1663,6 +1697,10 @@
onRemoveBroadcastInfo(requestId);
}
+ void requestAd(AdRequest request) {
+ onRequestAd(request);
+ }
+
/**
* Takes care of dispatching incoming input events and tells whether the event was handled.
*/
diff --git a/media/java/android/media/tv/interactive/ITvIAppClient.aidl b/media/java/android/media/tv/interactive/ITvIAppClient.aidl
index 00c464f..892a800 100644
--- a/media/java/android/media/tv/interactive/ITvIAppClient.aidl
+++ b/media/java/android/media/tv/interactive/ITvIAppClient.aidl
@@ -17,6 +17,7 @@
package android.media.tv.interactive;
import android.graphics.Rect;
+import android.media.tv.AdRequest;
import android.media.tv.BroadcastInfoRequest;
import android.net.Uri;
import android.os.Bundle;
@@ -41,4 +42,5 @@
void onRequestCurrentChannelLcn(int seq);
void onRequestStreamVolume(int seq);
void onRequestTrackInfoList(int seq);
+ void onAdRequest(in AdRequest request, int Seq);
}
diff --git a/media/java/android/media/tv/interactive/ITvIAppManager.aidl b/media/java/android/media/tv/interactive/ITvIAppManager.aidl
index 15a2eace..23201fa 100644
--- a/media/java/android/media/tv/interactive/ITvIAppManager.aidl
+++ b/media/java/android/media/tv/interactive/ITvIAppManager.aidl
@@ -17,6 +17,7 @@
package android.media.tv.interactive;
import android.graphics.Rect;
+import android.media.tv.AdResponse;
import android.media.tv.BroadcastInfoResponse;
import android.media.tv.TvTrackInfo;
import android.media.tv.interactive.ITvIAppClient;
@@ -55,6 +56,7 @@
int userId);
void notifyBroadcastInfoResponse(in IBinder sessionToken, in BroadcastInfoResponse response,
int UserId);
+ void notifyAdResponse(in IBinder sessionToken, in AdResponse response, int UserId);
void createMediaView(in IBinder sessionToken, in IBinder windowToken, in Rect frame,
int userId);
@@ -63,4 +65,4 @@
void registerCallback(in ITvIAppManagerCallback callback, int userId);
void unregisterCallback(in ITvIAppManagerCallback callback, int userId);
-}
\ No newline at end of file
+}
diff --git a/media/java/android/media/tv/interactive/ITvIAppSession.aidl b/media/java/android/media/tv/interactive/ITvIAppSession.aidl
index 4a85fef..52f9a87 100644
--- a/media/java/android/media/tv/interactive/ITvIAppSession.aidl
+++ b/media/java/android/media/tv/interactive/ITvIAppSession.aidl
@@ -19,6 +19,7 @@
import android.graphics.Rect;
import android.media.tv.BroadcastInfoResponse;
import android.net.Uri;
+import android.media.tv.AdResponse;
import android.media.tv.BroadcastInfoResponse;
import android.media.tv.TvTrackInfo;
import android.os.Bundle;
@@ -44,8 +45,9 @@
void setSurface(in Surface surface);
void dispatchSurfaceChanged(int format, int width, int height);
void notifyBroadcastInfoResponse(in BroadcastInfoResponse response);
+ void notifyAdResponse(in AdResponse response);
void createMediaView(in IBinder windowToken, in Rect frame);
void relayoutMediaView(in Rect frame);
void removeMediaView();
-}
\ No newline at end of file
+}
diff --git a/media/java/android/media/tv/interactive/ITvIAppSessionCallback.aidl b/media/java/android/media/tv/interactive/ITvIAppSessionCallback.aidl
index b09f787..9b9e6af 100644
--- a/media/java/android/media/tv/interactive/ITvIAppSessionCallback.aidl
+++ b/media/java/android/media/tv/interactive/ITvIAppSessionCallback.aidl
@@ -17,9 +17,9 @@
package android.media.tv.interactive;
import android.graphics.Rect;
+import android.media.tv.AdRequest;
import android.media.tv.BroadcastInfoRequest;
import android.media.tv.interactive.ITvIAppSession;
-import android.media.tv.BroadcastInfoRequest;
import android.net.Uri;
import android.os.Bundle;
@@ -41,4 +41,5 @@
void onRequestCurrentChannelLcn();
void onRequestStreamVolume();
void onRequestTrackInfoList();
+ void onAdRequest(in AdRequest request);
}
diff --git a/media/java/android/media/tv/interactive/TvIAppInfo.java b/media/java/android/media/tv/interactive/TvIAppInfo.java
index 6d5e561..79d94cc 100644
--- a/media/java/android/media/tv/interactive/TvIAppInfo.java
+++ b/media/java/android/media/tv/interactive/TvIAppInfo.java
@@ -17,6 +17,7 @@
package android.media.tv.interactive;
import android.annotation.NonNull;
+import android.annotation.StringDef;
import android.content.ComponentName;
import android.content.Context;
import android.content.Intent;
@@ -35,6 +36,8 @@
import org.xmlpull.v1.XmlPullParserException;
import java.io.IOException;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
import java.util.ArrayList;
import java.util.List;
@@ -46,6 +49,21 @@
private static final boolean DEBUG = false;
private static final String TAG = "TvIAppInfo";
+ @Retention(RetentionPolicy.SOURCE)
+ @StringDef(prefix = { "INTERACTIVE_APP_TYPE_" }, value = {
+ INTERACTIVE_APP_TYPE_HBBTV,
+ INTERACTIVE_APP_TYPE_ATSC,
+ INTERACTIVE_APP_TYPE_GINGA,
+ })
+ @interface InteractiveAppType {}
+
+ /** HbbTV interactive app type */
+ public static final String INTERACTIVE_APP_TYPE_HBBTV = "hbbtv";
+ /** ATSC interactive app type */
+ public static final String INTERACTIVE_APP_TYPE_ATSC = "atsc";
+ /** Ginga interactive app type */
+ public static final String INTERACTIVE_APP_TYPE_GINGA = "ginga";
+
private final ResolveInfo mService;
private final String mId;
private List<String> mTypes = new ArrayList<>();
@@ -107,6 +125,13 @@
}
/**
+ * Gets supported interactive app types
+ */
+ public List<String> getSupportedTypes() {
+ return new ArrayList<>(mTypes);
+ }
+
+ /**
* A convenience builder for creating {@link TvIAppInfo} objects.
*/
public static final class Builder {
@@ -186,7 +211,7 @@
CharSequence[] types = sa.getTextArray(
com.android.internal.R.styleable.TvIAppService_supportedTypes);
for (CharSequence cs : types) {
- mTypes.add(cs.toString());
+ mTypes.add(cs.toString().toLowerCase());
}
sa.recycle();
diff --git a/media/java/android/media/tv/interactive/TvIAppManager.java b/media/java/android/media/tv/interactive/TvIAppManager.java
index 4c4dffb..d1fd1df 100644
--- a/media/java/android/media/tv/interactive/TvIAppManager.java
+++ b/media/java/android/media/tv/interactive/TvIAppManager.java
@@ -22,6 +22,8 @@
import android.annotation.SystemService;
import android.content.Context;
import android.graphics.Rect;
+import android.media.tv.AdRequest;
+import android.media.tv.AdResponse;
import android.media.tv.BroadcastInfoRequest;
import android.media.tv.BroadcastInfoResponse;
import android.media.tv.TvInputManager;
@@ -282,6 +284,18 @@
}
@Override
+ public void onAdRequest(AdRequest request, int seq) {
+ synchronized (mSessionCallbackRecordMap) {
+ SessionCallbackRecord record = mSessionCallbackRecordMap.get(seq);
+ if (record == null) {
+ Log.e(TAG, "Callback not found for seq " + seq);
+ return;
+ }
+ record.postAdRequest(request);
+ }
+ }
+
+ @Override
public void onRequestCurrentChannelUri(int seq) {
synchronized (mSessionCallbackRecordMap) {
SessionCallbackRecord record = mSessionCallbackRecordMap.get(seq);
@@ -945,6 +959,23 @@
}
/**
+ * Notifies of any advertisement response passed in from TIS.
+ *
+ * @param response response passed in from TIS.
+ */
+ public void notifyAdResponse(AdResponse response) {
+ if (mToken == null) {
+ Log.w(TAG, "The session has been already released");
+ return;
+ }
+ try {
+ mService.notifyAdResponse(mToken, response, mUserId);
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
+ }
+ }
+
+ /**
* Releases this session.
*/
public void release() {
@@ -1322,6 +1353,17 @@
});
}
+ void postAdRequest(final AdRequest request) {
+ mHandler.post(new Runnable() {
+ @Override
+ public void run() {
+ if (mSession.getInputSession() != null) {
+ mSession.getInputSession().requestAd(request);
+ }
+ }
+ });
+ }
+
void postSessionStateChanged(int state) {
mHandler.post(new Runnable() {
@Override
diff --git a/media/java/android/media/tv/interactive/TvIAppService.java b/media/java/android/media/tv/interactive/TvIAppService.java
index a0f8413..1480ff64 100644
--- a/media/java/android/media/tv/interactive/TvIAppService.java
+++ b/media/java/android/media/tv/interactive/TvIAppService.java
@@ -27,6 +27,8 @@
import android.content.Intent;
import android.graphics.PixelFormat;
import android.graphics.Rect;
+import android.media.tv.AdRequest;
+import android.media.tv.AdResponse;
import android.media.tv.BroadcastInfoRequest;
import android.media.tv.BroadcastInfoResponse;
import android.media.tv.TvTrackInfo;
@@ -434,6 +436,13 @@
}
/**
+ * Called when an advertisement response is received.
+ * @hide
+ */
+ public void onAdResponse(AdResponse response) {
+ }
+
+ /**
* TODO: JavaDoc of APIs related to input events.
* @hide
*/
@@ -703,6 +712,29 @@
});
}
+ /**
+ * requests an advertisement request to be processed by the related TV input.
+ * @param request advertisement request
+ */
+ public void requestAd(@NonNull final AdRequest request) {
+ executeOrPostRunnableOnMainThread(new Runnable() {
+ @MainThread
+ @Override
+ public void run() {
+ try {
+ if (DEBUG) {
+ Log.d(TAG, "requestAd (id=" + request.getId() + ")");
+ }
+ if (mSessionCallback != null) {
+ mSessionCallback.onAdRequest(request);
+ }
+ } catch (RemoteException e) {
+ Log.w(TAG, "error in requestAd", e);
+ }
+ }
+ });
+ }
+
void startIApp() {
onStartIApp();
}
@@ -784,6 +816,16 @@
}
/**
+ * Calls {@link #onAdResponse}.
+ */
+ void notifyAdResponse(AdResponse response) {
+ if (DEBUG) {
+ Log.d(TAG, "notifyAdResponse (requestId=" + response.getId() + ")");
+ }
+ onAdResponse(response);
+ }
+
+ /**
* Notifies when the session state is changed.
* @param state the current state.
*/
@@ -1142,6 +1184,11 @@
}
@Override
+ public void notifyAdResponse(AdResponse response) {
+ mSessionImpl.notifyAdResponse(response);
+ }
+
+ @Override
public void createMediaView(IBinder windowToken, Rect frame) {
mSessionImpl.createMediaView(windowToken, frame);
}
diff --git a/media/java/android/media/tv/interactive/TvIAppView.java b/media/java/android/media/tv/interactive/TvIAppView.java
index 62ec4e3..1ce14ae 100644
--- a/media/java/android/media/tv/interactive/TvIAppView.java
+++ b/media/java/android/media/tv/interactive/TvIAppView.java
@@ -113,8 +113,12 @@
this(context, null, 0);
}
+ public TvIAppView(@NonNull Context context, @Nullable AttributeSet attrs) {
+ this(context, attrs, 0);
+ }
+
public TvIAppView(@NonNull Context context, @Nullable AttributeSet attrs, int defStyleAttr) {
- super(context, /* attrs = */null, /* defStyleAttr = */0);
+ super(context, attrs, defStyleAttr);
int sourceResId = Resources.getAttributeSetSourceResId(attrs);
if (sourceResId != Resources.ID_NULL) {
Log.d(TAG, "Build local AttributeSet");
@@ -127,7 +131,7 @@
}
mDefStyleAttr = defStyleAttr;
resetSurfaceView();
- mTvIAppManager = (TvIAppManager) getContext().getSystemService("tv_interactive_app");
+ mTvIAppManager = (TvIAppManager) getContext().getSystemService(Context.TV_IAPP_SERVICE);
}
/**
diff --git a/media/java/android/media/tv/tuner/Tuner.java b/media/java/android/media/tv/tuner/Tuner.java
index 7c08913..300aa15 100644
--- a/media/java/android/media/tv/tuner/Tuner.java
+++ b/media/java/android/media/tv/tuner/Tuner.java
@@ -19,6 +19,7 @@
import android.annotation.BytesLong;
import android.annotation.CallbackExecutor;
import android.annotation.IntDef;
+import android.annotation.IntRange;
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.annotation.RequiresPermission;
@@ -688,6 +689,8 @@
private native Filter nativeOpenFilter(int type, int subType, long bufferSize);
private native TimeFilter nativeOpenTimeFilter();
private native String nativeGetFrontendHardwareInfo();
+ private native int nativeSetMaxNumberOfFrontends(int frontendType, int maxNumber);
+ private native int nativeGetMaxNumberOfFrontends(int frontendType);
private native Lnb nativeOpenLnbByHandle(int handle);
private native Lnb nativeOpenLnbByName(String name);
@@ -1307,6 +1310,55 @@
}
}
+ /**
+ * Sets the maximum usable frontends number of a given frontend type. It is used to enable or
+ * disable frontends when cable connection status is changed by user.
+ *
+ * <p>This API is only supported by Tuner HAL 2.0 or higher. Unsupported version would return
+ * {@link RESULT_UNAVAILABLE}. Use {@link TunerVersionChecker#getTunerVersion()} to check the
+ * version.
+ *
+ * @param frontendType the {@link android.media.tv.tuner.frontend.FrontendSettings.Type} which
+ * the maximum usable number will be set.
+ * @param maxNumber the new maximum usable number.
+ * @return result status of the operation.
+ */
+ @Result
+ public int setMaxNumberOfFrontends(
+ @FrontendSettings.Type int frontendType, @IntRange(from = 0) int maxNumber) {
+ if (!TunerVersionChecker.checkHigherOrEqualVersionTo(
+ TunerVersionChecker.TUNER_VERSION_2_0, "Set maximum Frontends")) {
+ return RESULT_UNAVAILABLE;
+ }
+ if (maxNumber < 0) {
+ return RESULT_INVALID_ARGUMENT;
+ }
+ int res = nativeSetMaxNumberOfFrontends(frontendType, maxNumber);
+ if (res == RESULT_SUCCESS) {
+ // TODO: b/211778848 Update Tuner Resource Manager.
+ }
+ return res;
+ }
+
+ /**
+ * Get the maximum usable frontends number of a given frontend type.
+ *
+ * <p>This API is only supported by Tuner HAL 2.0 or higher. Unsupported version would return
+ * {@code -1}. Use {@link TunerVersionChecker#getTunerVersion()} to check the version.
+ *
+ * @param frontendType the {@link android.media.tv.tuner.frontend.FrontendSettings.Type} which
+ * the maximum usable number will be queried.
+ * @return the maximum usable number of the queried frontend type.
+ */
+ @IntRange(from = -1)
+ public int getMaxNumberOfFrontends(@FrontendSettings.Type int frontendType) {
+ if (!TunerVersionChecker.checkHigherOrEqualVersionTo(
+ TunerVersionChecker.TUNER_VERSION_2_0, "Set maximum Frontends")) {
+ return -1;
+ }
+ return nativeGetMaxNumberOfFrontends(frontendType);
+ }
+
/** @hide */
public FrontendInfo getFrontendInfoById(int id) {
mFrontendLock.lock();
diff --git a/media/jni/android_media_tv_Tuner.cpp b/media/jni/android_media_tv_Tuner.cpp
index f5606bc..8ccc4fb 100644
--- a/media/jni/android_media_tv_Tuner.cpp
+++ b/media/jni/android_media_tv_Tuner.cpp
@@ -1577,6 +1577,24 @@
return mFeClient->getHardwareInfo(info);
}
+jint JTuner::setMaxNumberOfFrontends(int32_t type, int32_t maxNumber) {
+ if (mTunerClient == nullptr) {
+ ALOGE("tuner is not initialized");
+ return (jint)Result::INVALID_STATE;
+ }
+
+ return (jint)mTunerClient->setMaxNumberOfFrontends(static_cast<FrontendType>(type), maxNumber);
+}
+
+int32_t JTuner::getMaxNumberOfFrontends(int32_t type) {
+ if (mTunerClient == nullptr) {
+ ALOGE("tuner is not initialized");
+ return -1;
+ }
+
+ return mTunerClient->getMaxNumberOfFrontends(static_cast<FrontendType>(type));
+}
+
jobject JTuner::openLnbByHandle(int handle) {
if (mTunerClient == nullptr) {
return nullptr;
@@ -4281,6 +4299,17 @@
return env->NewStringUTF(info.data());
}
+static jint android_media_tv_Tuner_set_maximum_frontends(JNIEnv *env, jobject thiz, jint type,
+ jint maxNumber) {
+ sp<JTuner> tuner = getTuner(env, thiz);
+ return tuner->setMaxNumberOfFrontends(type, maxNumber);
+}
+
+static jint android_media_tv_Tuner_get_maximum_frontends(JNIEnv *env, jobject thiz, jint type) {
+ sp<JTuner> tuner = getTuner(env, thiz);
+ return tuner->getMaxNumberOfFrontends(type);
+}
+
static jint android_media_tv_Tuner_close_frontend(JNIEnv* env, jobject thiz, jint /* handle */) {
sp<JTuner> tuner = getTuner(env, thiz);
return tuner->closeFrontend();
@@ -4592,6 +4621,10 @@
(void *)android_media_tv_Tuner_open_shared_filter},
{ "nativeGetFrontendHardwareInfo","()Ljava/lang/String;",
(void *)android_media_tv_Tuner_get_frontend_hardware_info },
+ { "nativeSetMaxNumberOfFrontends", "(II)I",
+ (void *)android_media_tv_Tuner_set_maximum_frontends },
+ { "nativeGetMaxNumberOfFrontends", "(I)I",
+ (void *)android_media_tv_Tuner_get_maximum_frontends },
};
static const JNINativeMethod gFilterMethods[] = {
diff --git a/media/jni/android_media_tv_Tuner.h b/media/jni/android_media_tv_Tuner.h
index 4cad92b..f1b31e3 100644
--- a/media/jni/android_media_tv_Tuner.h
+++ b/media/jni/android_media_tv_Tuner.h
@@ -201,6 +201,8 @@
jint closeFrontend();
jint closeDemux();
Result getFrontendHardwareInfo(string& info);
+ jint setMaxNumberOfFrontends(int32_t frontendType, int32_t maxNumber);
+ int32_t getMaxNumberOfFrontends(int32_t frontendType);
jweak getObject();
diff --git a/media/jni/tuner/TunerClient.cpp b/media/jni/tuner/TunerClient.cpp
index f917f01..3c8fdfe6 100644
--- a/media/jni/tuner/TunerClient.cpp
+++ b/media/jni/tuner/TunerClient.cpp
@@ -194,4 +194,23 @@
return Result::INVALID_STATE;
}
+Result TunerClient::setMaxNumberOfFrontends(FrontendType frontendType, int32_t maxNumber) {
+ if (mTunerService != nullptr) {
+ Status s = mTunerService->setMaxNumberOfFrontends(frontendType, maxNumber);
+ return ClientHelper::getServiceSpecificErrorCode(s);
+ }
+
+ return Result::INVALID_STATE;
+}
+
+int TunerClient::getMaxNumberOfFrontends(FrontendType frontendType) {
+ if (mTunerService != nullptr) {
+ int32_t maxNumber;
+ mTunerService->getMaxNumberOfFrontends(frontendType, &maxNumber);
+ return maxNumber;
+ }
+
+ return -1;
+}
+
} // namespace android
diff --git a/media/jni/tuner/TunerClient.h b/media/jni/tuner/TunerClient.h
index 37b8ee1..a9f37e6 100644
--- a/media/jni/tuner/TunerClient.h
+++ b/media/jni/tuner/TunerClient.h
@@ -32,6 +32,7 @@
using ::aidl::android::hardware::tv::tuner::DemuxCapabilities;
using ::aidl::android::hardware::tv::tuner::FrontendInfo;
+using ::aidl::android::hardware::tv::tuner::FrontendType;
using ::aidl::android::hardware::tv::tuner::Result;
using ::aidl::android::media::tv::tuner::ITunerService;
@@ -132,6 +133,21 @@
*/
Result setLna(bool bEnable);
+ /**
+ * Set the maximum frontend number of a given frontend type.
+ *
+ * @param frontendType the frontend type which maximum number will be set.
+ * @param maxNumber the new maximum number.
+ */
+ Result setMaxNumberOfFrontends(FrontendType frontendType, int32_t maxNumber);
+
+ /**
+ * Get the maximum frontend number of a given frontend type.
+ *
+ * @param frontendType the frontend type which maximum number will be queried.
+ */
+ int getMaxNumberOfFrontends(FrontendType frontendType);
+
private:
/**
* An AIDL Tuner Service Singleton assigned at the first time the Tuner Client
diff --git a/packages/ConnectivityT/framework-t/src/android/app/usage/NetworkStats.java b/packages/ConnectivityT/framework-t/src/android/app/usage/NetworkStats.java
index 216a4a0..f684a4d 100644
--- a/packages/ConnectivityT/framework-t/src/android/app/usage/NetworkStats.java
+++ b/packages/ConnectivityT/framework-t/src/android/app/usage/NetworkStats.java
@@ -24,13 +24,15 @@
import android.net.NetworkTemplate;
import android.net.TrafficStats;
import android.os.RemoteException;
-import android.util.IntArray;
import android.util.Log;
+import com.android.net.module.util.CollectionUtils;
+
import dalvik.system.CloseGuard;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
+import java.util.ArrayList;
/**
* Class providing enumeration over buckets of network usage statistics. {@link NetworkStats} objects
@@ -568,7 +570,7 @@
// the filtering logic below can be removed.
int[] uids = mSession.getRelevantUids();
// Filtering of uids with empty history.
- IntArray filteredUids = new IntArray(uids.length);
+ final ArrayList<Integer> filteredUids = new ArrayList<>();
for (int uid : uids) {
try {
NetworkStatsHistory history = mSession.getHistoryIntervalForUid(mTemplate, uid,
@@ -581,7 +583,7 @@
Log.w(TAG, "Error while getting history of uid " + uid, e);
}
}
- mUids = filteredUids.toArray();
+ mUids = CollectionUtils.toIntArray(filteredUids);
mUidOrUidIndex = -1;
stepHistory();
}
diff --git a/packages/ConnectivityT/framework-t/src/android/app/usage/NetworkStatsManager.java b/packages/ConnectivityT/framework-t/src/android/app/usage/NetworkStatsManager.java
index 2b9560e..a316b8a 100644
--- a/packages/ConnectivityT/framework-t/src/android/app/usage/NetworkStatsManager.java
+++ b/packages/ConnectivityT/framework-t/src/android/app/usage/NetworkStatsManager.java
@@ -47,7 +47,6 @@
import android.os.RemoteException;
import android.telephony.TelephonyManager;
import android.text.TextUtils;
-import android.util.DataUnit;
import android.util.Log;
import com.android.internal.annotations.VisibleForTesting;
@@ -119,7 +118,7 @@
* is reached.
* @hide
*/
- public static final long MIN_THRESHOLD_BYTES = DataUnit.MEBIBYTES.toBytes(2);
+ public static final long MIN_THRESHOLD_BYTES = 2 * 1_048_576L; // 2MiB
private final Context mContext;
private final INetworkStatsService mService;
diff --git a/packages/ConnectivityT/framework-t/src/android/net/NetworkIdentity.java b/packages/ConnectivityT/framework-t/src/android/net/NetworkIdentity.java
index eb8f43e..8f1115e 100644
--- a/packages/ConnectivityT/framework-t/src/android/net/NetworkIdentity.java
+++ b/packages/ConnectivityT/framework-t/src/android/net/NetworkIdentity.java
@@ -21,7 +21,6 @@
import android.annotation.Nullable;
import android.content.Context;
import android.net.wifi.WifiInfo;
-import android.net.wifi.WifiManager;
import android.service.NetworkIdentityProto;
import android.telephony.Annotation.NetworkType;
import android.util.proto.ProtoOutputStream;
@@ -228,11 +227,11 @@
final int oemManaged = getOemBitfield(snapshot.getNetworkCapabilities());
if (legacyType == TYPE_WIFI) {
- networkId = snapshot.getNetworkCapabilities().getSsid();
- if (networkId == null) {
- final WifiManager wifi = context.getSystemService(WifiManager.class);
- final WifiInfo info = wifi.getConnectionInfo();
- networkId = info != null ? info.getSSID() : null;
+ final TransportInfo transportInfo = snapshot.getNetworkCapabilities()
+ .getTransportInfo();
+ if (transportInfo instanceof WifiInfo) {
+ final WifiInfo info = (WifiInfo) transportInfo;
+ networkId = info != null ? info.getCurrentNetworkKey() : null;
}
}
diff --git a/packages/ConnectivityT/framework-t/src/android/net/NetworkStats.java b/packages/ConnectivityT/framework-t/src/android/net/NetworkStats.java
index 1986b83..181a594 100644
--- a/packages/ConnectivityT/framework-t/src/android/net/NetworkStats.java
+++ b/packages/ConnectivityT/framework-t/src/android/net/NetworkStats.java
@@ -31,7 +31,7 @@
import android.util.SparseBooleanArray;
import com.android.internal.annotations.VisibleForTesting;
-import com.android.internal.util.ArrayUtils;
+import com.android.net.module.util.CollectionUtils;
import libcore.util.EmptyArray;
@@ -1185,7 +1185,7 @@
* @hide
*/
public void removeUids(int[] uids) {
- filter(e -> !ArrayUtils.contains(uids, e.uid));
+ filter(e -> !CollectionUtils.contains(uids, e.uid));
}
/**
@@ -1218,7 +1218,7 @@
filter(e -> (limitUid == UID_ALL || limitUid == e.uid)
&& (limitTag == TAG_ALL || limitTag == e.tag)
&& (limitIfaces == INTERFACES_ALL
- || ArrayUtils.contains(limitIfaces, e.iface)));
+ || CollectionUtils.contains(limitIfaces, e.iface)));
}
/**
diff --git a/packages/ConnectivityT/framework-t/src/android/net/NetworkStatsAccess.java b/packages/ConnectivityT/framework-t/src/android/net/NetworkStatsAccess.java
index 591605d9..b64fbdb 100644
--- a/packages/ConnectivityT/framework-t/src/android/net/NetworkStatsAccess.java
+++ b/packages/ConnectivityT/framework-t/src/android/net/NetworkStatsAccess.java
@@ -17,6 +17,7 @@
package android.net;
import static android.Manifest.permission.READ_NETWORK_USAGE_HISTORY;
+import static android.content.pm.PackageManager.PERMISSION_GRANTED;
import static android.net.NetworkStats.UID_ALL;
import static android.net.TrafficStats.UID_REMOVED;
import static android.net.TrafficStats.UID_TETHERING;
@@ -106,7 +107,7 @@
/** Returns the {@link NetworkStatsAccess.Level} for the given caller. */
public static @NetworkStatsAccess.Level int checkAccessLevel(
- Context context, int callingUid, String callingPackage) {
+ Context context, int callingPid, int callingUid, String callingPackage) {
final DevicePolicyManager mDpm = context.getSystemService(DevicePolicyManager.class);
final TelephonyManager tm = (TelephonyManager)
context.getSystemService(Context.TELEPHONY_SERVICE);
@@ -123,8 +124,12 @@
final boolean isDeviceOwner = mDpm != null && mDpm.isDeviceOwnerApp(callingPackage);
final int appId = UserHandle.getAppId(callingUid);
+ final boolean isNetworkStack = context.checkPermission(
+ android.Manifest.permission.NETWORK_STACK, callingPid, callingUid)
+ == PERMISSION_GRANTED;
+
if (hasCarrierPrivileges || isDeviceOwner
- || appId == Process.SYSTEM_UID || appId == Process.NETWORK_STACK_UID) {
+ || appId == Process.SYSTEM_UID || isNetworkStack) {
// Carrier-privileged apps and device owners, and the system (including the
// network stack) can access data usage for all apps on the device.
return NetworkStatsAccess.Level.DEVICE;
@@ -155,6 +160,8 @@
*/
public static boolean isAccessibleToUser(int uid, int callerUid,
@NetworkStatsAccess.Level int accessLevel) {
+ final int userId = UserHandle.getUserHandleForUid(uid).getIdentifier();
+ final int callerUserId = UserHandle.getUserHandleForUid(callerUid).getIdentifier();
switch (accessLevel) {
case NetworkStatsAccess.Level.DEVICE:
// Device-level access - can access usage for any uid.
@@ -165,13 +172,13 @@
// anonymized uids
return uid == android.os.Process.SYSTEM_UID || uid == UID_REMOVED
|| uid == UID_TETHERING || uid == UID_ALL
- || UserHandle.getUserId(uid) == UserHandle.getUserId(callerUid);
+ || userId == callerUserId;
case NetworkStatsAccess.Level.USER:
// User-level access - can access usage for any app running in the same user, along
// with some special uids (system, removed, or tethering).
return uid == android.os.Process.SYSTEM_UID || uid == UID_REMOVED
|| uid == UID_TETHERING
- || UserHandle.getUserId(uid) == UserHandle.getUserId(callerUid);
+ || userId == callerUserId;
case NetworkStatsAccess.Level.DEFAULT:
default:
// Default access level - can only access one's own usage.
@@ -185,8 +192,8 @@
AppOpsManager appOps = (AppOpsManager) context.getSystemService(
Context.APP_OPS_SERVICE);
- final int mode = appOps.noteOp(AppOpsManager.OP_GET_USAGE_STATS,
- callingUid, callingPackage);
+ final int mode = appOps.noteOp(AppOpsManager.OPSTR_GET_USAGE_STATS,
+ callingUid, callingPackage, null /* attributionTag */, null /* message */);
if (mode == AppOpsManager.MODE_DEFAULT) {
// The default behavior here is to check if PackageManager has given the app
// permission.
diff --git a/packages/ConnectivityT/framework-t/src/android/net/NetworkStatsCollection.java b/packages/ConnectivityT/framework-t/src/android/net/NetworkStatsCollection.java
index 8d1347e..7935d28 100644
--- a/packages/ConnectivityT/framework-t/src/android/net/NetworkStatsCollection.java
+++ b/packages/ConnectivityT/framework-t/src/android/net/NetworkStatsCollection.java
@@ -40,21 +40,17 @@
import android.text.format.DateUtils;
import android.util.ArrayMap;
import android.util.AtomicFile;
-import android.util.IntArray;
-import android.util.MathUtils;
+import android.util.IndentingPrintWriter;
+import android.util.Log;
import android.util.Range;
-import android.util.Slog;
import android.util.proto.ProtoOutputStream;
import com.android.internal.annotations.VisibleForTesting;
-import com.android.internal.util.ArrayUtils;
import com.android.internal.util.FastDataInput;
import com.android.internal.util.FastDataOutput;
import com.android.internal.util.FileRotator;
-import com.android.internal.util.IndentingPrintWriter;
-
-import com.google.android.collect.Lists;
-import com.google.android.collect.Maps;
+import com.android.net.module.util.CollectionUtils;
+import com.android.net.module.util.NetworkStatsUtils;
import libcore.io.IoUtils;
@@ -196,11 +192,11 @@
public int[] getRelevantUids(@NetworkStatsAccess.Level int accessLevel,
final int callerUid) {
- IntArray uids = new IntArray();
+ final ArrayList<Integer> uids = new ArrayList<>();
for (int i = 0; i < mStats.size(); i++) {
final Key key = mStats.keyAt(i);
if (NetworkStatsAccess.isAccessibleToUser(key.uid, callerUid, accessLevel)) {
- int j = uids.binarySearch(key.uid);
+ int j = Collections.binarySearch(uids, new Integer(key.uid));
if (j < 0) {
j = ~j;
@@ -208,7 +204,7 @@
}
}
}
- return uids.toArray();
+ return CollectionUtils.toIntArray(uids);
}
/**
@@ -225,7 +221,8 @@
// 180 days of history should be enough for anyone; if we end up needing
// more, we'll dynamically grow the history object.
- final int bucketEstimate = (int) MathUtils.constrain(((end - start) / mBucketDuration), 0,
+ final int bucketEstimate = (int) NetworkStatsUtils.constrain(
+ ((end - start) / mBucketDuration), 0,
(180 * DateUtils.DAY_IN_MILLIS) / mBucketDuration);
final NetworkStatsHistory combined = new NetworkStatsHistory(
mBucketDuration, bucketEstimate, fields);
@@ -316,7 +313,7 @@
final long deltaTotal = combined.getTotalBytes() - beforeTotal;
if (deltaTotal != 0) {
- Slog.d(TAG, "Augmented network usage by " + deltaTotal + " bytes");
+ Log.d(TAG, "Augmented network usage by " + deltaTotal + " bytes");
}
// Finally we can slice data as originally requested
@@ -489,11 +486,11 @@
private void write(DataOutput out) throws IOException {
// cluster key lists grouped by ident
- final HashMap<NetworkIdentitySet, ArrayList<Key>> keysByIdent = Maps.newHashMap();
+ final HashMap<NetworkIdentitySet, ArrayList<Key>> keysByIdent = new HashMap<>();
for (Key key : mStats.keySet()) {
ArrayList<Key> keys = keysByIdent.get(key.ident);
if (keys == null) {
- keys = Lists.newArrayList();
+ keys = new ArrayList<>();
keysByIdent.put(key.ident, keys);
}
keys.add(key);
@@ -640,12 +637,12 @@
* {@link TrafficStats#UID_REMOVED}.
*/
public void removeUids(int[] uids) {
- final ArrayList<Key> knownKeys = Lists.newArrayList();
+ final ArrayList<Key> knownKeys = new ArrayList<>();
knownKeys.addAll(mStats.keySet());
// migrate all UID stats into special "removed" bucket
for (Key key : knownKeys) {
- if (ArrayUtils.contains(uids, key.uid)) {
+ if (CollectionUtils.contains(uids, key.uid)) {
// only migrate combined TAG_NONE history
if (key.tag == TAG_NONE) {
final NetworkStatsHistory uidHistory = mStats.get(key);
@@ -672,7 +669,7 @@
}
private ArrayList<Key> getSortedKeys() {
- final ArrayList<Key> keys = Lists.newArrayList();
+ final ArrayList<Key> keys = new ArrayList<>();
keys.addAll(mStats.keySet());
Collections.sort(keys);
return keys;
diff --git a/packages/ConnectivityT/framework-t/src/android/net/NetworkStatsHistory.java b/packages/ConnectivityT/framework-t/src/android/net/NetworkStatsHistory.java
index 3eef4ee..428bc6d 100644
--- a/packages/ConnectivityT/framework-t/src/android/net/NetworkStatsHistory.java
+++ b/packages/ConnectivityT/framework-t/src/android/net/NetworkStatsHistory.java
@@ -28,7 +28,6 @@
import static android.net.NetworkStatsHistory.ParcelUtils.writeLongArray;
import static android.text.format.DateUtils.SECOND_IN_MILLIS;
-import static com.android.internal.util.ArrayUtils.total;
import static com.android.net.module.util.NetworkStatsUtils.multiplySafeByRational;
import android.compat.annotation.UnsupportedAppUsage;
@@ -37,10 +36,11 @@
import android.os.Parcelable;
import android.service.NetworkStatsHistoryBucketProto;
import android.service.NetworkStatsHistoryProto;
-import android.util.MathUtils;
+import android.util.IndentingPrintWriter;
import android.util.proto.ProtoOutputStream;
-import com.android.internal.util.IndentingPrintWriter;
+import com.android.net.module.util.CollectionUtils;
+import com.android.net.module.util.NetworkStatsUtils;
import libcore.util.EmptyArray;
@@ -174,7 +174,7 @@
txPackets = new long[bucketStart.length];
operations = new long[bucketStart.length];
bucketCount = bucketStart.length;
- totalBytes = total(rxBytes) + total(txBytes);
+ totalBytes = CollectionUtils.total(rxBytes) + CollectionUtils.total(txBytes);
break;
}
case VERSION_ADD_PACKETS:
@@ -189,7 +189,7 @@
txPackets = readVarLongArray(in);
operations = readVarLongArray(in);
bucketCount = bucketStart.length;
- totalBytes = total(rxBytes) + total(txBytes);
+ totalBytes = CollectionUtils.total(rxBytes) + CollectionUtils.total(txBytes);
break;
}
default: {
@@ -267,7 +267,7 @@
} else {
index -= 1;
}
- return MathUtils.constrain(index, 0, bucketCount - 1);
+ return NetworkStatsUtils.constrain(index, 0, bucketCount - 1);
}
/**
@@ -281,7 +281,7 @@
} else {
index += 1;
}
- return MathUtils.constrain(index, 0, bucketCount - 1);
+ return NetworkStatsUtils.constrain(index, 0, bucketCount - 1);
}
/**
@@ -349,6 +349,9 @@
// create any buckets needed by this range
ensureBuckets(start, end);
+ // Return fast if there is still no entry. This would typically happen when the start,
+ // end or duration are not valid values, e.g. start > end, negative duration value, etc.
+ if (bucketCount == 0) return;
// distribute data usage into buckets
long duration = end - start;
@@ -560,6 +563,9 @@
entry.txPackets = txPackets != null ? 0 : UNKNOWN;
entry.operations = operations != null ? 0 : UNKNOWN;
+ // Return fast if there is no entry.
+ if (bucketCount == 0) return entry;
+
final int startIndex = getIndexAfter(end);
for (int i = startIndex; i >= 0; i--) {
final long curStart = bucketStart[i];
diff --git a/packages/ConnectivityT/framework-t/src/android/net/NetworkTemplate.java b/packages/ConnectivityT/framework-t/src/android/net/NetworkTemplate.java
index 5da8e25..659ad06 100644
--- a/packages/ConnectivityT/framework-t/src/android/net/NetworkTemplate.java
+++ b/packages/ConnectivityT/framework-t/src/android/net/NetworkTemplate.java
@@ -35,7 +35,6 @@
import static android.net.NetworkStats.ROAMING_ALL;
import static android.net.NetworkStats.ROAMING_NO;
import static android.net.NetworkStats.ROAMING_YES;
-import static android.net.wifi.WifiInfo.sanitizeSsid;
import android.annotation.IntDef;
import android.annotation.NonNull;
@@ -49,7 +48,7 @@
import android.text.TextUtils;
import android.util.ArraySet;
-import com.android.internal.util.ArrayUtils;
+import com.android.net.module.util.CollectionUtils;
import com.android.net.module.util.NetworkIdentityUtils;
import com.android.net.module.util.NetworkStatsUtils;
@@ -57,6 +56,7 @@
import java.lang.annotation.RetentionPolicy;
import java.util.Arrays;
import java.util.Collection;
+import java.util.Comparator;
import java.util.HashSet;
import java.util.List;
import java.util.Objects;
@@ -213,11 +213,14 @@
public static NetworkTemplate buildTemplateMobileWithRatType(@Nullable String subscriberId,
@NetworkType int ratType, int metered) {
if (TextUtils.isEmpty(subscriberId)) {
- return new NetworkTemplate(MATCH_MOBILE_WILDCARD, null, null, null,
- metered, ROAMING_ALL, DEFAULT_NETWORK_ALL, ratType, OEM_MANAGED_ALL,
+ return new NetworkTemplate(MATCH_MOBILE_WILDCARD, null /* subscriberId */,
+ null /* matchSubscriberIds */,
+ new String[0] /* matchWifiNetworkKeys */, metered, ROAMING_ALL,
+ DEFAULT_NETWORK_ALL, ratType, OEM_MANAGED_ALL,
NetworkStatsUtils.SUBSCRIBER_ID_MATCH_RULE_EXACT);
}
- return new NetworkTemplate(MATCH_MOBILE, subscriberId, new String[]{subscriberId}, null,
+ return new NetworkTemplate(MATCH_MOBILE, subscriberId, new String[] { subscriberId },
+ new String[0] /* matchWifiNetworkKeys */,
metered, ROAMING_ALL, DEFAULT_NETWORK_ALL, ratType, OEM_MANAGED_ALL,
NetworkStatsUtils.SUBSCRIBER_ID_MATCH_RULE_EXACT);
}
@@ -235,7 +238,7 @@
/**
* Template to match all metered {@link ConnectivityManager#TYPE_WIFI} networks,
- * regardless of SSID.
+ * regardless of key of the wifi network.
*
* @hide
*/
@@ -255,33 +258,40 @@
/**
* Template to match {@link ConnectivityManager#TYPE_WIFI} networks with the
- * given SSID.
+ * given key of the wifi network.
*
+ * @param wifiNetworkKey key of the wifi network. see {@link WifiInfo#getCurrentNetworkKey()}
+ * to know details about the key.
* @hide
*/
- public static NetworkTemplate buildTemplateWifi(@NonNull String networkId) {
- Objects.requireNonNull(networkId);
+ public static NetworkTemplate buildTemplateWifi(@NonNull String wifiNetworkKey) {
+ Objects.requireNonNull(wifiNetworkKey);
return new NetworkTemplate(MATCH_WIFI, null /* subscriberId */,
new String[] { null } /* matchSubscriberIds */,
- networkId, METERED_ALL, ROAMING_ALL,
+ new String[] { wifiNetworkKey }, METERED_ALL, ROAMING_ALL,
DEFAULT_NETWORK_ALL, NETWORK_TYPE_ALL, OEM_MANAGED_ALL,
NetworkStatsUtils.SUBSCRIBER_ID_MATCH_RULE_ALL);
}
/**
- * Template to match all {@link ConnectivityManager#TYPE_WIFI} networks with the given SSID,
- * and IMSI.
+ * Template to match all {@link ConnectivityManager#TYPE_WIFI} networks with the given
+ * key of the wifi network and IMSI.
*
- * Call with {@link #WIFI_NETWORK_KEY_ALL} for {@code networkId} to get result regardless
- * of SSID.
+ * Call with {@link #WIFI_NETWORK_KEY_ALL} for {@code wifiNetworkKey} to get result regardless
+ * of key of the wifi network.
+ *
+ * @param wifiNetworkKey key of the wifi network. see {@link WifiInfo#getCurrentNetworkKey()}
+ * to know details about the key.
+ * @param subscriberId the IMSI associated to this wifi network.
*
* @hide
*/
- public static NetworkTemplate buildTemplateWifi(@Nullable String networkId,
+ public static NetworkTemplate buildTemplateWifi(@Nullable String wifiNetworkKey,
@Nullable String subscriberId) {
return new NetworkTemplate(MATCH_WIFI, subscriberId, new String[] { subscriberId },
- networkId, METERED_ALL, ROAMING_ALL,
- DEFAULT_NETWORK_ALL, NETWORK_TYPE_ALL, OEM_MANAGED_ALL,
+ wifiNetworkKey != null
+ ? new String[] { wifiNetworkKey } : new String[0],
+ METERED_ALL, ROAMING_ALL, DEFAULT_NETWORK_ALL, NETWORK_TYPE_ALL, OEM_MANAGED_ALL,
NetworkStatsUtils.SUBSCRIBER_ID_MATCH_RULE_EXACT);
}
@@ -324,7 +334,9 @@
public static NetworkTemplate buildTemplateCarrierMetered(@NonNull String subscriberId) {
Objects.requireNonNull(subscriberId);
return new NetworkTemplate(MATCH_CARRIER, subscriberId,
- new String[] { subscriberId }, null /* networkId */, METERED_YES, ROAMING_ALL,
+ new String[] { subscriberId },
+ new String[0] /* matchWifiNetworkKeys */,
+ METERED_YES, ROAMING_ALL,
DEFAULT_NETWORK_ALL, NETWORK_TYPE_ALL, OEM_MANAGED_ALL,
NetworkStatsUtils.SUBSCRIBER_ID_MATCH_RULE_EXACT);
}
@@ -342,8 +354,8 @@
*/
private final String[] mMatchSubscriberIds;
- // TODO: Change variable name to match the Api surface.
- private final String mNetworkId;
+ @NonNull
+ private final String[] mMatchWifiNetworkKeys;
// Matches for the NetworkStats constants METERED_*, ROAMING_* and DEFAULT_NETWORK_*.
private final int mMetered;
@@ -377,18 +389,19 @@
/** @hide */
// TODO: Deprecate this constructor, mark it @UnsupportedAppUsage(maxTargetSdk = S)
@UnsupportedAppUsage
- public NetworkTemplate(int matchRule, String subscriberId, String networkId) {
- this(matchRule, subscriberId, new String[] { subscriberId }, networkId);
+ public NetworkTemplate(int matchRule, String subscriberId, String wifiNetworkKey) {
+ this(matchRule, subscriberId, new String[] { subscriberId }, wifiNetworkKey);
}
/** @hide */
public NetworkTemplate(int matchRule, String subscriberId, String[] matchSubscriberIds,
- String networkId) {
+ String wifiNetworkKey) {
// Older versions used to only match MATCH_MOBILE and MATCH_MOBILE_WILDCARD templates
// to metered networks. It is now possible to match mobile with any meteredness, but
// in order to preserve backward compatibility of @UnsupportedAppUsage methods, this
//constructor passes METERED_YES for these types.
- this(matchRule, subscriberId, matchSubscriberIds, networkId,
+ this(matchRule, subscriberId, matchSubscriberIds,
+ wifiNetworkKey != null ? new String[] { wifiNetworkKey } : new String[0],
(matchRule == MATCH_MOBILE || matchRule == MATCH_MOBILE_WILDCARD) ? METERED_YES
: METERED_ALL , ROAMING_ALL, DEFAULT_NETWORK_ALL, NETWORK_TYPE_ALL,
OEM_MANAGED_ALL, NetworkStatsUtils.SUBSCRIBER_ID_MATCH_RULE_EXACT);
@@ -397,23 +410,25 @@
/** @hide */
// TODO: Remove it after updating all of the caller.
public NetworkTemplate(int matchRule, String subscriberId, String[] matchSubscriberIds,
- String networkId, int metered, int roaming, int defaultNetwork, int subType,
+ String wifiNetworkKey, int metered, int roaming, int defaultNetwork, int subType,
int oemManaged) {
- this(matchRule, subscriberId, matchSubscriberIds, networkId, metered, roaming,
- defaultNetwork, subType, oemManaged,
+ this(matchRule, subscriberId, matchSubscriberIds,
+ wifiNetworkKey != null ? new String[] { wifiNetworkKey } : new String[0],
+ metered, roaming, defaultNetwork, subType, oemManaged,
NetworkStatsUtils.SUBSCRIBER_ID_MATCH_RULE_EXACT);
}
/** @hide */
public NetworkTemplate(int matchRule, String subscriberId, String[] matchSubscriberIds,
- String networkId, int metered, int roaming, int defaultNetwork, int subType,
- int oemManaged, int subscriberIdMatchRule) {
+ String[] matchWifiNetworkKeys, int metered, int roaming,
+ int defaultNetwork, int subType, int oemManaged, int subscriberIdMatchRule) {
+ Objects.requireNonNull(matchWifiNetworkKeys);
mMatchRule = matchRule;
mSubscriberId = subscriberId;
// TODO: Check whether mMatchSubscriberIds = null or mMatchSubscriberIds = {null} when
// mSubscriberId is null
mMatchSubscriberIds = matchSubscriberIds;
- mNetworkId = networkId;
+ mMatchWifiNetworkKeys = matchWifiNetworkKeys;
mMetered = metered;
mRoaming = roaming;
mDefaultNetwork = defaultNetwork;
@@ -431,7 +446,7 @@
mMatchRule = in.readInt();
mSubscriberId = in.readString();
mMatchSubscriberIds = in.createStringArray();
- mNetworkId = in.readString();
+ mMatchWifiNetworkKeys = in.createStringArray();
mMetered = in.readInt();
mRoaming = in.readInt();
mDefaultNetwork = in.readInt();
@@ -445,7 +460,7 @@
dest.writeInt(mMatchRule);
dest.writeString(mSubscriberId);
dest.writeStringArray(mMatchSubscriberIds);
- dest.writeString(mNetworkId);
+ dest.writeStringArray(mMatchWifiNetworkKeys);
dest.writeInt(mMetered);
dest.writeInt(mRoaming);
dest.writeInt(mDefaultNetwork);
@@ -471,9 +486,7 @@
builder.append(", matchSubscriberIds=").append(
Arrays.toString(NetworkIdentityUtils.scrubSubscriberIds(mMatchSubscriberIds)));
}
- if (mNetworkId != null) {
- builder.append(", networkId=").append(mNetworkId);
- }
+ builder.append(", matchWifiNetworkKeys=").append(Arrays.toString(mMatchWifiNetworkKeys));
if (mMetered != METERED_ALL) {
builder.append(", metered=").append(NetworkStats.meteredToString(mMetered));
}
@@ -497,8 +510,8 @@
@Override
public int hashCode() {
- return Objects.hash(mMatchRule, mSubscriberId, mNetworkId, mMetered, mRoaming,
- mDefaultNetwork, mSubType, mOemManaged, mSubscriberIdMatchRule);
+ return Objects.hash(mMatchRule, mSubscriberId, Arrays.hashCode(mMatchWifiNetworkKeys),
+ mMetered, mRoaming, mDefaultNetwork, mSubType, mOemManaged, mSubscriberIdMatchRule);
}
@Override
@@ -507,13 +520,13 @@
final NetworkTemplate other = (NetworkTemplate) obj;
return mMatchRule == other.mMatchRule
&& Objects.equals(mSubscriberId, other.mSubscriberId)
- && Objects.equals(mNetworkId, other.mNetworkId)
&& mMetered == other.mMetered
&& mRoaming == other.mRoaming
&& mDefaultNetwork == other.mDefaultNetwork
&& mSubType == other.mSubType
&& mOemManaged == other.mOemManaged
- && mSubscriberIdMatchRule == other.mSubscriberIdMatchRule;
+ && mSubscriberIdMatchRule == other.mSubscriberIdMatchRule
+ && Arrays.equals(mMatchWifiNetworkKeys, other.mMatchWifiNetworkKeys);
}
return false;
}
@@ -579,14 +592,22 @@
*/
@Nullable
public String getWifiNetworkKey() {
- return mNetworkId;
+ return CollectionUtils.isEmpty(mMatchWifiNetworkKeys) ? null : mMatchWifiNetworkKeys[0];
+ }
+
+ /**
+ * Get set of Wifi Network Keys of the template.
+ */
+ @Nullable
+ public Set<String> getWifiNetworkKeys() {
+ return new ArraySet<>(Arrays.asList(mMatchWifiNetworkKeys));
}
/** @hide */
// TODO: Remove this and replace all callers with {@link #getWifiNetworkKey()}.
@Nullable
public String getNetworkId() {
- return mNetworkId;
+ return getWifiNetworkKey();
}
/**
@@ -703,28 +724,33 @@
*/
public boolean matchesSubscriberId(@Nullable String subscriberId) {
return mSubscriberIdMatchRule == NetworkStatsUtils.SUBSCRIBER_ID_MATCH_RULE_ALL
- || ArrayUtils.contains(mMatchSubscriberIds, subscriberId);
+ || CollectionUtils.contains(mMatchSubscriberIds, subscriberId);
}
/**
- * Check if network with matching SSID. Returns true when the SSID matches, or when
- * {@code mNetworkId} is {@code WIFI_NETWORK_KEY_ALL}.
+ * Check if network matches key of the wifi network.
+ * Returns true when the key matches, or when {@code mMatchWifiNetworkKeys} is
+ * empty.
+ *
+ * @param wifiNetworkKey key of the wifi network. see {@link WifiInfo#getCurrentNetworkKey()}
+ * to know details about the key.
*/
- private boolean matchesWifiNetworkId(@Nullable String networkId) {
- return Objects.equals(mNetworkId, WIFI_NETWORK_KEY_ALL)
- || Objects.equals(sanitizeSsid(mNetworkId), sanitizeSsid(networkId));
+ private boolean matchesWifiNetworkKey(@NonNull String wifiNetworkKey) {
+ Objects.requireNonNull(wifiNetworkKey);
+ return CollectionUtils.isEmpty(mMatchWifiNetworkKeys)
+ || CollectionUtils.contains(mMatchWifiNetworkKeys, wifiNetworkKey);
}
/**
- * Check if mobile network with matching IMSI.
+ * Check if mobile network matches IMSI.
*/
private boolean matchesMobile(NetworkIdentity ident) {
if (ident.mType == TYPE_WIMAX) {
// TODO: consider matching against WiMAX subscriber identity
return true;
} else {
- return ident.mType == TYPE_MOBILE && !ArrayUtils.isEmpty(mMatchSubscriberIds)
- && ArrayUtils.contains(mMatchSubscriberIds, ident.mSubscriberId)
+ return ident.mType == TYPE_MOBILE && !CollectionUtils.isEmpty(mMatchSubscriberIds)
+ && CollectionUtils.contains(mMatchSubscriberIds, ident.mSubscriberId)
&& matchesCollapsedRatType(ident);
}
}
@@ -814,7 +840,7 @@
switch (ident.mType) {
case TYPE_WIFI:
return matchesSubscriberId(ident.mSubscriberId)
- && matchesWifiNetworkId(ident.mNetworkId);
+ && matchesWifiNetworkKey(ident.mNetworkId);
default:
return false;
}
@@ -835,8 +861,8 @@
*/
private boolean matchesCarrier(NetworkIdentity ident) {
return ident.mSubscriberId != null
- && !ArrayUtils.isEmpty(mMatchSubscriberIds)
- && ArrayUtils.contains(mMatchSubscriberIds, ident.mSubscriberId);
+ && !CollectionUtils.isEmpty(mMatchSubscriberIds)
+ && CollectionUtils.contains(mMatchSubscriberIds, ident.mSubscriberId);
}
private boolean matchesMobileWildcard(NetworkIdentity ident) {
@@ -953,11 +979,13 @@
if (template.mSubscriberId == null) return template;
for (String[] merged : mergedList) {
- if (ArrayUtils.contains(merged, template.mSubscriberId)) {
+ if (CollectionUtils.contains(merged, template.mSubscriberId)) {
// Requested template subscriber is part of the merge group; return
// a template that matches all merged subscribers.
+ final String[] matchWifiNetworkKeys = template.mMatchWifiNetworkKeys;
return new NetworkTemplate(template.mMatchRule, merged[0], merged,
- template.mNetworkId);
+ CollectionUtils.isEmpty(matchWifiNetworkKeys)
+ ? null : matchWifiNetworkKeys[0]);
}
}
@@ -984,9 +1012,10 @@
private final int mMatchRule;
// Use a SortedSet to provide a deterministic order when fetching the first one.
@NonNull
- private final SortedSet<String> mMatchSubscriberIds = new TreeSet<>();
- @Nullable
- private String mWifiNetworkKey;
+ private final SortedSet<String> mMatchSubscriberIds =
+ new TreeSet<>(Comparator.nullsFirst(Comparator.naturalOrder()));
+ @NonNull
+ private final SortedSet<String> mMatchWifiNetworkKeys = new TreeSet<>();
// Matches for the NetworkStats constants METERED_*, ROAMING_* and DEFAULT_NETWORK_*.
private int mMetered;
@@ -1006,7 +1035,6 @@
assertRequestableMatchRule(matchRule);
// Initialize members with default values.
mMatchRule = matchRule;
- mWifiNetworkKey = WIFI_NETWORK_KEY_ALL;
mMetered = METERED_ALL;
mRoaming = ROAMING_ALL;
mDefaultNetwork = DEFAULT_NETWORK_ALL;
@@ -1030,15 +1058,28 @@
}
/**
- * Set the Wifi Network Key.
+ * Set the Wifi Network Keys. Calling this function with an empty set represents
+ * the intention of matching any Wifi Network Key.
*
- * @param wifiNetworkKey the Wifi Network Key, see {@link WifiInfo#getCurrentNetworkKey()}.
- * Or null to match all networks.
+ * @param wifiNetworkKeys the list of Wifi Network Key,
+ * see {@link WifiInfo#getCurrentNetworkKey()}.
+ * Or an empty list to match all networks.
+ * Note that {@code getCurrentNetworkKey()} might get null key
+ * when wifi disconnects. However, the caller should never invoke
+ * this function with a null Wifi Network Key since such statistics
+ * never exists.
* @return this builder.
*/
@NonNull
- public Builder setWifiNetworkKey(@Nullable String wifiNetworkKey) {
- mWifiNetworkKey = wifiNetworkKey;
+ public Builder setWifiNetworkKeys(@NonNull Set<String> wifiNetworkKeys) {
+ Objects.requireNonNull(wifiNetworkKeys);
+ for (String key : wifiNetworkKeys) {
+ if (key == null) {
+ throw new IllegalArgumentException("Null is not a valid key");
+ }
+ }
+ mMatchWifiNetworkKeys.clear();
+ mMatchWifiNetworkKeys.addAll(wifiNetworkKeys);
return this;
}
@@ -1122,9 +1163,17 @@
}
private void assertRequestableParameters() {
+ validateWifiNetworkKeys();
// TODO: Check all the input are legitimate.
}
+ private void validateWifiNetworkKeys() {
+ if (mMatchRule != MATCH_WIFI && !mMatchWifiNetworkKeys.isEmpty()) {
+ throw new IllegalArgumentException("Trying to build non wifi match rule: "
+ + mMatchRule + " with wifi network keys");
+ }
+ }
+
/**
* For backward compatibility, deduce match rule to a wildcard match rule
* if the Subscriber Ids are empty.
@@ -1133,7 +1182,7 @@
if (mMatchRule == MATCH_MOBILE && mMatchSubscriberIds.isEmpty()) {
return MATCH_MOBILE_WILDCARD;
} else if (mMatchRule == MATCH_WIFI && mMatchSubscriberIds.isEmpty()
- && mWifiNetworkKey == WIFI_NETWORK_KEY_ALL) {
+ && mMatchWifiNetworkKeys.isEmpty()) {
return MATCH_WIFI_WILDCARD;
}
return mMatchRule;
@@ -1153,8 +1202,8 @@
return new NetworkTemplate(getWildcardDeducedMatchRule(),
mMatchSubscriberIds.isEmpty() ? null : mMatchSubscriberIds.iterator().next(),
mMatchSubscriberIds.toArray(new String[0]),
- mWifiNetworkKey, mMetered, mRoaming, mDefaultNetwork, mRatType, mOemManaged,
- subscriberIdMatchRule);
+ mMatchWifiNetworkKeys.toArray(new String[0]), mMetered, mRoaming,
+ mDefaultNetwork, mRatType, mOemManaged, subscriberIdMatchRule);
}
}
}
diff --git a/packages/ConnectivityT/framework-t/src/android/net/TrafficStats.java b/packages/ConnectivityT/framework-t/src/android/net/TrafficStats.java
index fa65061..d8feb88 100644
--- a/packages/ConnectivityT/framework-t/src/android/net/TrafficStats.java
+++ b/packages/ConnectivityT/framework-t/src/android/net/TrafficStats.java
@@ -29,7 +29,6 @@
import android.os.Build;
import android.os.RemoteException;
import android.os.ServiceManager;
-import android.util.DataUnit;
import com.android.server.NetworkManagementSocketTagger;
@@ -59,19 +58,19 @@
*/
public final static int UNSUPPORTED = -1;
- /** @hide @deprecated use {@link DataUnit} instead to clarify SI-vs-IEC */
+ /** @hide @deprecated use {@code DataUnit} instead to clarify SI-vs-IEC */
@Deprecated
public static final long KB_IN_BYTES = 1024;
- /** @hide @deprecated use {@link DataUnit} instead to clarify SI-vs-IEC */
+ /** @hide @deprecated use {@code DataUnit} instead to clarify SI-vs-IEC */
@Deprecated
public static final long MB_IN_BYTES = KB_IN_BYTES * 1024;
- /** @hide @deprecated use {@link DataUnit} instead to clarify SI-vs-IEC */
+ /** @hide @deprecated use {@code DataUnit} instead to clarify SI-vs-IEC */
@Deprecated
public static final long GB_IN_BYTES = MB_IN_BYTES * 1024;
- /** @hide @deprecated use {@link DataUnit} instead to clarify SI-vs-IEC */
+ /** @hide @deprecated use {@code DataUnit} instead to clarify SI-vs-IEC */
@Deprecated
public static final long TB_IN_BYTES = GB_IN_BYTES * 1024;
- /** @hide @deprecated use {@link DataUnit} instead to clarify SI-vs-IEC */
+ /** @hide @deprecated use {@code DataUnit} instead to clarify SI-vs-IEC */
@Deprecated
public static final long PB_IN_BYTES = TB_IN_BYTES * 1024;
diff --git a/packages/ConnectivityT/service/src/com/android/server/net/NetworkStatsFactory.java b/packages/ConnectivityT/service/src/com/android/server/net/NetworkStatsFactory.java
index e6433db..c7c8893 100644
--- a/packages/ConnectivityT/service/src/com/android/server/net/NetworkStatsFactory.java
+++ b/packages/ConnectivityT/service/src/com/android/server/net/NetworkStatsFactory.java
@@ -35,8 +35,8 @@
import com.android.internal.annotations.GuardedBy;
import com.android.internal.annotations.VisibleForTesting;
-import com.android.internal.util.ArrayUtils;
import com.android.internal.util.ProcFileReader;
+import com.android.net.module.util.CollectionUtils;
import libcore.io.IoUtils;
@@ -434,7 +434,7 @@
entry.txBytes = reader.nextLong();
entry.txPackets = reader.nextLong();
- if ((limitIfaces == null || ArrayUtils.contains(limitIfaces, entry.iface))
+ if ((limitIfaces == null || CollectionUtils.contains(limitIfaces, entry.iface))
&& (limitUid == UID_ALL || limitUid == entry.uid)
&& (limitTag == TAG_ALL || limitTag == entry.tag)) {
stats.insertEntry(entry);
diff --git a/packages/ConnectivityT/service/src/com/android/server/net/NetworkStatsObservers.java b/packages/ConnectivityT/service/src/com/android/server/net/NetworkStatsObservers.java
index 1a0866d..b57a4f9 100644
--- a/packages/ConnectivityT/service/src/com/android/server/net/NetworkStatsObservers.java
+++ b/packages/ConnectivityT/service/src/com/android/server/net/NetworkStatsObservers.java
@@ -18,8 +18,6 @@
import static android.app.usage.NetworkStatsManager.MIN_THRESHOLD_BYTES;
-import static com.android.internal.util.Preconditions.checkArgument;
-
import android.app.usage.NetworkStatsManager;
import android.net.DataUsageRequest;
import android.net.NetworkIdentitySet;
@@ -38,7 +36,7 @@
import android.os.Process;
import android.os.RemoteException;
import android.util.ArrayMap;
-import android.util.Slog;
+import android.util.Log;
import android.util.SparseArray;
import com.android.internal.annotations.VisibleForTesting;
@@ -83,7 +81,7 @@
RequestInfo requestInfo = buildRequestInfo(request, messenger, binder, callingUid,
accessLevel);
- if (LOGV) Slog.v(TAG, "Registering observer for " + request);
+ if (LOGV) Log.v(TAG, "Registering observer for " + request);
getHandler().sendMessage(mHandler.obtainMessage(MSG_REGISTER, requestInfo));
return request;
}
@@ -116,7 +114,7 @@
if (mHandler == null) {
synchronized (this) {
if (mHandler == null) {
- if (LOGV) Slog.v(TAG, "Creating handler");
+ if (LOGV) Log.v(TAG, "Creating handler");
mHandler = new Handler(getHandlerLooperLocked(), mHandlerCallback);
}
}
@@ -172,15 +170,15 @@
RequestInfo requestInfo;
requestInfo = mDataUsageRequests.get(request.requestId);
if (requestInfo == null) {
- if (LOGV) Slog.v(TAG, "Trying to unregister unknown request " + request);
+ if (LOGV) Log.v(TAG, "Trying to unregister unknown request " + request);
return;
}
if (Process.SYSTEM_UID != callingUid && requestInfo.mCallingUid != callingUid) {
- Slog.w(TAG, "Caller uid " + callingUid + " is not owner of " + request);
+ Log.w(TAG, "Caller uid " + callingUid + " is not owner of " + request);
return;
}
- if (LOGV) Slog.v(TAG, "Unregistering " + request);
+ if (LOGV) Log.v(TAG, "Unregistering " + request);
mDataUsageRequests.remove(request.requestId);
requestInfo.unlinkDeathRecipient();
requestInfo.callCallback(NetworkStatsManager.CALLBACK_RELEASED);
@@ -201,7 +199,7 @@
// Cap the minimum threshold to a safe default to avoid too many callbacks
long thresholdInBytes = Math.max(MIN_THRESHOLD_BYTES, request.thresholdInBytes);
if (thresholdInBytes < request.thresholdInBytes) {
- Slog.w(TAG, "Threshold was too low for " + request
+ Log.w(TAG, "Threshold was too low for " + request
+ ". Overriding to a safer default of " + thresholdInBytes + " bytes");
}
return new DataUsageRequest(mNextDataUsageRequestId.incrementAndGet(),
@@ -216,7 +214,10 @@
accessLevel);
} else {
// Safety check in case a new access level is added and we forgot to update this
- checkArgument(accessLevel >= NetworkStatsAccess.Level.DEVICESUMMARY);
+ if (accessLevel < NetworkStatsAccess.Level.DEVICESUMMARY) {
+ throw new IllegalArgumentException(
+ "accessLevel " + accessLevel + " is less than DEVICESUMMARY.");
+ }
return new NetworkUsageRequestInfo(this, request, messenger, binder, callingUid,
accessLevel);
}
@@ -255,8 +256,9 @@
@Override
public void binderDied() {
- if (LOGV) Slog.v(TAG, "RequestInfo binderDied("
- + mRequest + ", " + mBinder + ")");
+ if (LOGV) {
+ Log.v(TAG, "RequestInfo binderDied(" + mRequest + ", " + mBinder + ")");
+ }
mStatsObserver.unregister(mRequest, Process.SYSTEM_UID);
callCallback(NetworkStatsManager.CALLBACK_RELEASED);
}
@@ -299,13 +301,13 @@
msg.setData(bundle);
try {
if (LOGV) {
- Slog.v(TAG, "sending notification " + callbackTypeToName(callbackType)
+ Log.v(TAG, "sending notification " + callbackTypeToName(callbackType)
+ " for " + mRequest);
}
mMessenger.send(msg);
} catch (RemoteException e) {
// May occur naturally in the race of binder death.
- Slog.w(TAG, "RemoteException caught trying to send a callback msg for " + mRequest);
+ Log.w(TAG, "RemoteException caught trying to send a callback msg for " + mRequest);
}
}
@@ -341,7 +343,7 @@
protected boolean checkStats() {
long bytesSoFar = getTotalBytesForNetwork(mRequest.template);
if (LOGV) {
- Slog.v(TAG, bytesSoFar + " bytes so far since notification for "
+ Log.v(TAG, bytesSoFar + " bytes so far since notification for "
+ mRequest.template);
}
if (bytesSoFar > mRequest.thresholdInBytes) {
@@ -416,7 +418,7 @@
return history.getTotalBytes();
} catch (SecurityException e) {
if (LOGV) {
- Slog.w(TAG, "CallerUid " + mCallingUid + " may have lost access to uid "
+ Log.w(TAG, "CallerUid " + mCallingUid + " may have lost access to uid "
+ uid);
}
return 0;
diff --git a/packages/ConnectivityT/service/src/com/android/server/net/NetworkStatsRecorder.java b/packages/ConnectivityT/service/src/com/android/server/net/NetworkStatsRecorder.java
index 5e27c77..c371f08 100644
--- a/packages/ConnectivityT/service/src/com/android/server/net/NetworkStatsRecorder.java
+++ b/packages/ConnectivityT/service/src/com/android/server/net/NetworkStatsRecorder.java
@@ -32,15 +32,12 @@
import android.os.Binder;
import android.os.DropBoxManager;
import android.service.NetworkStatsRecorderProto;
+import android.util.IndentingPrintWriter;
import android.util.Log;
-import android.util.MathUtils;
-import android.util.Slog;
import android.util.proto.ProtoOutputStream;
import com.android.internal.util.FileRotator;
-import com.android.internal.util.IndentingPrintWriter;
-
-import com.google.android.collect.Sets;
+import com.android.net.module.util.NetworkStatsUtils;
import libcore.io.IoUtils;
@@ -132,8 +129,8 @@
}
public void setPersistThreshold(long thresholdBytes) {
- if (LOGV) Slog.v(TAG, "setPersistThreshold() with " + thresholdBytes);
- mPersistThresholdBytes = MathUtils.constrain(
+ if (LOGV) Log.v(TAG, "setPersistThreshold() with " + thresholdBytes);
+ mPersistThresholdBytes = NetworkStatsUtils.constrain(
thresholdBytes, 1 * KB_IN_BYTES, 100 * MB_IN_BYTES);
}
@@ -185,7 +182,7 @@
}
private NetworkStatsCollection loadLocked(long start, long end) {
- if (LOGD) Slog.d(TAG, "loadLocked() reading from disk for " + mCookie);
+ if (LOGD) Log.d(TAG, "loadLocked() reading from disk for " + mCookie);
final NetworkStatsCollection res = new NetworkStatsCollection(mBucketDuration);
try {
mRotator.readMatching(res, start, end);
@@ -207,7 +204,7 @@
*/
public void recordSnapshotLocked(NetworkStats snapshot,
Map<String, NetworkIdentitySet> ifaceIdent, long currentTimeMillis) {
- final HashSet<String> unknownIfaces = Sets.newHashSet();
+ final HashSet<String> unknownIfaces = new HashSet<>();
// skip recording when snapshot missing
if (snapshot == null) return;
@@ -272,7 +269,7 @@
mLastSnapshot = snapshot;
if (LOGV && unknownIfaces.size() > 0) {
- Slog.w(TAG, "unknown interfaces " + unknownIfaces + ", ignoring those stats");
+ Log.w(TAG, "unknown interfaces " + unknownIfaces + ", ignoring those stats");
}
}
@@ -296,7 +293,7 @@
public void forcePersistLocked(long currentTimeMillis) {
Objects.requireNonNull(mRotator, "missing FileRotator");
if (mPending.isDirty()) {
- if (LOGD) Slog.d(TAG, "forcePersistLocked() writing for " + mCookie);
+ if (LOGD) Log.d(TAG, "forcePersistLocked() writing for " + mCookie);
try {
mRotator.rewriteActive(mPendingRewriter, currentTimeMillis);
mRotator.maybeRotate(currentTimeMillis);
diff --git a/packages/ConnectivityT/service/src/com/android/server/net/NetworkStatsService.java b/packages/ConnectivityT/service/src/com/android/server/net/NetworkStatsService.java
index df066c21..a3de9e4 100644
--- a/packages/ConnectivityT/service/src/com/android/server/net/NetworkStatsService.java
+++ b/packages/ConnectivityT/service/src/com/android/server/net/NetworkStatsService.java
@@ -25,7 +25,6 @@
import static android.content.Intent.EXTRA_UID;
import static android.content.pm.PackageManager.PERMISSION_GRANTED;
import static android.net.NetworkIdentity.SUBTYPE_COMBINED;
-import static android.net.NetworkStack.checkNetworkStackPermission;
import static android.net.NetworkStats.DEFAULT_NETWORK_ALL;
import static android.net.NetworkStats.IFACE_ALL;
import static android.net.NetworkStats.IFACE_VT;
@@ -141,28 +140,29 @@
import android.util.ArrayMap;
import android.util.ArraySet;
import android.util.EventLog;
+import android.util.IndentingPrintWriter;
import android.util.Log;
-import android.util.MathUtils;
-import android.util.Slog;
import android.util.SparseIntArray;
import android.util.proto.ProtoOutputStream;
import com.android.internal.annotations.GuardedBy;
import com.android.internal.annotations.VisibleForTesting;
-import com.android.internal.util.ArrayUtils;
-import com.android.internal.util.DumpUtils;
import com.android.internal.util.FileRotator;
-import com.android.internal.util.IndentingPrintWriter;
import com.android.net.module.util.BinderUtils;
+import com.android.net.module.util.CollectionUtils;
+import com.android.net.module.util.NetworkStatsUtils;
+import com.android.net.module.util.PermissionUtils;
import com.android.server.EventLogTags;
import com.android.server.LocalServices;
import java.io.File;
import java.io.FileDescriptor;
+import java.io.FileOutputStream;
import java.io.IOException;
import java.io.PrintWriter;
import java.time.Clock;
import java.time.ZoneOffset;
+import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashSet;
import java.util.List;
@@ -646,7 +646,7 @@
try {
mNetworkManager.setGlobalAlert(mGlobalAlertBytes);
} catch (IllegalStateException e) {
- Slog.w(TAG, "problem registering for global alert: " + e);
+ Log.w(TAG, "problem registering for global alert: " + e);
} catch (RemoteException e) {
// ignored; service lives in system_server
}
@@ -764,7 +764,7 @@
return stats;
} catch (NullPointerException e) {
// TODO: Track down and fix the cause of this crash and remove this catch block.
- Slog.wtf(TAG, "NullPointerException in getSummaryForAllUid", e);
+ Log.wtf(TAG, "NullPointerException in getSummaryForAllUid", e);
throw e;
}
}
@@ -809,7 +809,7 @@
private @NetworkStatsAccess.Level int checkAccessLevel(String callingPackage) {
return NetworkStatsAccess.checkAccessLevel(
- mContext, Binder.getCallingUid(), callingPackage);
+ mContext, Binder.getCallingPid(), Binder.getCallingUid(), callingPackage);
}
/**
@@ -821,7 +821,7 @@
SubscriptionPlan plan = null;
if ((flags & NetworkStatsManager.FLAG_AUGMENT_WITH_SUBSCRIPTION_PLAN) != 0
&& mSettings.getAugmentEnabled()) {
- if (LOGD) Slog.d(TAG, "Resolving plan for " + template);
+ if (LOGD) Log.d(TAG, "Resolving plan for " + template);
final long token = Binder.clearCallingIdentity();
try {
plan = LocalServices.getService(NetworkPolicyManagerInternal.class)
@@ -829,7 +829,7 @@
} finally {
Binder.restoreCallingIdentity(token);
}
- if (LOGD) Slog.d(TAG, "Resolved to plan " + plan);
+ if (LOGD) Log.d(TAG, "Resolved to plan " + plan);
}
return plan;
}
@@ -932,7 +932,7 @@
@Override
public String[] getMobileIfaces() {
// TODO (b/192758557): Remove debug log.
- if (ArrayUtils.contains(mMobileIfaces, null)) {
+ if (CollectionUtils.contains(mMobileIfaces, null)) {
throw new NullPointerException(
"null element in mMobileIfaces: " + Arrays.toString(mMobileIfaces));
}
@@ -981,7 +981,7 @@
@NonNull NetworkStateSnapshot[] networkStates,
@Nullable String activeIface,
@NonNull UnderlyingNetworkInfo[] underlyingNetworkInfos) {
- checkNetworkStackPermission(mContext);
+ PermissionUtils.enforceNetworkStackPermission(mContext);
final long token = Binder.clearCallingIdentity();
try {
@@ -1010,9 +1010,10 @@
private void advisePersistThreshold(long thresholdBytes) {
// clamp threshold into safe range
- mPersistThreshold = MathUtils.constrain(thresholdBytes, 128 * KB_IN_BYTES, 2 * MB_IN_BYTES);
+ mPersistThreshold = NetworkStatsUtils.constrain(thresholdBytes,
+ 128 * KB_IN_BYTES, 2 * MB_IN_BYTES);
if (LOGV) {
- Slog.v(TAG, "advisePersistThreshold() given " + thresholdBytes + ", clamped to "
+ Log.v(TAG, "advisePersistThreshold() given " + thresholdBytes + ", clamped to "
+ mPersistThreshold);
}
@@ -1196,13 +1197,13 @@
// On background handler thread, and USER_REMOVED is protected
// broadcast.
- final int userId = intent.getIntExtra(Intent.EXTRA_USER_HANDLE, -1);
- if (userId == -1) return;
+ final UserHandle userHandle = intent.getParcelableExtra(Intent.EXTRA_USER);
+ if (userHandle == null) return;
synchronized (mStatsLock) {
mWakeLock.acquire();
try {
- removeUserLocked(userId);
+ removeUserLocked(userHandle);
} finally {
mWakeLock.release();
}
@@ -1227,7 +1228,7 @@
@Override
public void limitReached(String limitName, String iface) {
// only someone like NMS should be calling us
- NetworkStack.checkNetworkStackPermission(mContext);
+ PermissionUtils.enforceNetworkStackPermission(mContext);
if (LIMIT_GLOBAL_ALERT.equals(limitName)) {
// kick off background poll to collect network stats unless there is already
@@ -1275,7 +1276,7 @@
private void handleNotifyNetworkStatusLocked(@NonNull Network[] defaultNetworks,
@NonNull NetworkStateSnapshot[] snapshots) {
if (!mSystemReady) return;
- if (LOGV) Slog.v(TAG, "handleNotifyNetworkStatusLocked()");
+ if (LOGV) Log.v(TAG, "handleNotifyNetworkStatusLocked()");
// take one last stats snapshot before updating iface mapping. this
// isn't perfect, since the kernel may already be counting traffic from
@@ -1299,7 +1300,8 @@
final int displayTransport =
getDisplayTransport(snapshot.getNetworkCapabilities().getTransportTypes());
final boolean isMobile = (NetworkCapabilities.TRANSPORT_CELLULAR == displayTransport);
- final boolean isDefault = ArrayUtils.contains(mDefaultNetworks, snapshot.getNetwork());
+ final boolean isDefault = CollectionUtils.contains(
+ mDefaultNetworks, snapshot.getNetwork());
final int subType = combineSubtypeEnabled ? SUBTYPE_COMBINED
: getSubTypeForStateSnapshot(snapshot);
final NetworkIdentity ident = NetworkIdentity.buildNetworkIdentity(mContext, snapshot,
@@ -1382,7 +1384,7 @@
mMobileIfaces = mobileIfaces.toArray(new String[0]);
// TODO (b/192758557): Remove debug log.
- if (ArrayUtils.contains(mMobileIfaces, null)) {
+ if (CollectionUtils.contains(mMobileIfaces, null)) {
throw new NullPointerException(
"null element in mMobileIfaces: " + Arrays.toString(mMobileIfaces));
}
@@ -1397,7 +1399,7 @@
if (spec instanceof TelephonyNetworkSpecifier) {
return ((TelephonyNetworkSpecifier) spec).getSubscriptionId();
} else {
- Slog.wtf(TAG, "getSubIdForState invalid NetworkSpecifier");
+ Log.wtf(TAG, "getSubIdForState invalid NetworkSpecifier");
return INVALID_SUBSCRIPTION_ID;
}
}
@@ -1481,7 +1483,7 @@
try {
recordSnapshotLocked(currentTime);
} catch (IllegalStateException e) {
- Slog.w(TAG, "problem reading network stats: " + e);
+ Log.w(TAG, "problem reading network stats: " + e);
} catch (RemoteException e) {
// ignored; service lives in system_server
}
@@ -1506,7 +1508,7 @@
@GuardedBy("mStatsLock")
private void performPollLocked(int flags) {
if (!mSystemReady) return;
- if (LOGV) Slog.v(TAG, "performPollLocked(flags=0x" + Integer.toHexString(flags) + ")");
+ if (LOGV) Log.v(TAG, "performPollLocked(flags=0x" + Integer.toHexString(flags) + ")");
Trace.traceBegin(TRACE_TAG_NETWORK, "performPollLocked");
final boolean persistNetwork = (flags & FLAG_PERSIST_NETWORK) != 0;
@@ -1628,7 +1630,7 @@
*/
@GuardedBy("mStatsLock")
private void removeUidsLocked(int... uids) {
- if (LOGV) Slog.v(TAG, "removeUidsLocked() for UIDs " + Arrays.toString(uids));
+ if (LOGV) Log.v(TAG, "removeUidsLocked() for UIDs " + Arrays.toString(uids));
// Perform one last poll before removing
performPollLocked(FLAG_PERSIST_ALL);
@@ -1646,20 +1648,20 @@
* Clean up {@link #mUidRecorder} after user is removed.
*/
@GuardedBy("mStatsLock")
- private void removeUserLocked(int userId) {
- if (LOGV) Slog.v(TAG, "removeUserLocked() for userId=" + userId);
+ private void removeUserLocked(@NonNull UserHandle userHandle) {
+ if (LOGV) Log.v(TAG, "removeUserLocked() for UserHandle=" + userHandle);
// Build list of UIDs that we should clean up
- int[] uids = new int[0];
+ final ArrayList<Integer> uids = new ArrayList<>();
final List<ApplicationInfo> apps = mContext.getPackageManager().getInstalledApplications(
PackageManager.MATCH_ANY_USER
| PackageManager.MATCH_DISABLED_COMPONENTS);
for (ApplicationInfo app : apps) {
- final int uid = UserHandle.getUid(userId, app.uid);
- uids = ArrayUtils.appendInt(uids, uid);
+ final int uid = userHandle.getUid(app.uid);
+ uids.add(uid);
}
- removeUidsLocked(uids);
+ removeUidsLocked(CollectionUtils.toIntArray(uids));
}
private class NetworkStatsManagerInternalImpl extends NetworkStatsManagerInternal {
@@ -1702,7 +1704,7 @@
public void setStatsProviderWarningAndLimitAsync(
@NonNull String iface, long warning, long limit) {
if (LOGV) {
- Slog.v(TAG, "setStatsProviderWarningAndLimitAsync("
+ Log.v(TAG, "setStatsProviderWarningAndLimitAsync("
+ iface + "," + warning + "," + limit + ")");
}
invokeForAllStatsProviderCallbacks((cb) -> cb.mProvider.onSetWarningAndLimit(iface,
@@ -1712,7 +1714,7 @@
@Override
protected void dump(FileDescriptor fd, PrintWriter rawWriter, String[] args) {
- if (!DumpUtils.checkDumpPermission(mContext, TAG, rawWriter)) return;
+ if (!PermissionUtils.checkDumpPermission(mContext, TAG, rawWriter)) return;
long duration = DateUtils.DAY_IN_MILLIS;
final HashSet<String> argSet = new HashSet<String>();
@@ -1773,15 +1775,15 @@
pw.println("Configs:");
pw.increaseIndent();
- pw.printPair(NETSTATS_COMBINE_SUBTYPE_ENABLED, mSettings.getCombineSubtypeEnabled());
+ pw.print(NETSTATS_COMBINE_SUBTYPE_ENABLED, mSettings.getCombineSubtypeEnabled());
pw.println();
pw.decreaseIndent();
pw.println("Active interfaces:");
pw.increaseIndent();
for (int i = 0; i < mActiveIfaces.size(); i++) {
- pw.printPair("iface", mActiveIfaces.keyAt(i));
- pw.printPair("ident", mActiveIfaces.valueAt(i));
+ pw.print("iface", mActiveIfaces.keyAt(i));
+ pw.print("ident", mActiveIfaces.valueAt(i));
pw.println();
}
pw.decreaseIndent();
@@ -1789,8 +1791,8 @@
pw.println("Active UID interfaces:");
pw.increaseIndent();
for (int i = 0; i < mActiveUidIfaces.size(); i++) {
- pw.printPair("iface", mActiveUidIfaces.keyAt(i));
- pw.printPair("ident", mActiveUidIfaces.valueAt(i));
+ pw.print("iface", mActiveUidIfaces.keyAt(i));
+ pw.print("ident", mActiveUidIfaces.valueAt(i));
pw.println();
}
pw.decreaseIndent();
@@ -1863,7 +1865,7 @@
@GuardedBy("mStatsLock")
private void dumpProtoLocked(FileDescriptor fd) {
- final ProtoOutputStream proto = new ProtoOutputStream(fd);
+ final ProtoOutputStream proto = new ProtoOutputStream(new FileOutputStream(fd));
// TODO Right now it writes all history. Should it limit to the "since-boot" log?
diff --git a/packages/ConnectivityT/service/src/com/android/server/net/NetworkStatsSubscriptionsMonitor.java b/packages/ConnectivityT/service/src/com/android/server/net/NetworkStatsSubscriptionsMonitor.java
index 5646c75..93d0ae7 100644
--- a/packages/ConnectivityT/service/src/com/android/server/net/NetworkStatsSubscriptionsMonitor.java
+++ b/packages/ConnectivityT/service/src/com/android/server/net/NetworkStatsSubscriptionsMonitor.java
@@ -33,7 +33,7 @@
import android.util.Pair;
import com.android.internal.annotations.VisibleForTesting;
-import com.android.internal.util.CollectionUtils;
+import com.android.net.module.util.CollectionUtils;
import java.util.ArrayList;
import java.util.List;
@@ -99,18 +99,19 @@
// prevent binder call to telephony when querying RAT. Keep listener registration with empty
// IMSI is meaningless since the RAT type changed is ambiguous for multi-SIM if reported
// with empty IMSI. So filter the subs w/o a valid IMSI to prevent such registration.
- final List<Pair<Integer, String>> filteredNewSubs =
- CollectionUtils.mapNotNull(newSubs, subId -> {
- final String subscriberId = mTeleManager.getSubscriberId(subId);
- return TextUtils.isEmpty(subscriberId) ? null : new Pair(subId, subscriberId);
- });
+ final List<Pair<Integer, String>> filteredNewSubs = new ArrayList<>();
+ for (final int subId : newSubs) {
+ final String subscriberId = mTeleManager.getSubscriberId(subId);
+ if (!TextUtils.isEmpty(subscriberId)) {
+ filteredNewSubs.add(new Pair(subId, subscriberId));
+ }
+ }
for (final Pair<Integer, String> sub : filteredNewSubs) {
// Fully match listener with subId and IMSI, since in some rare cases, IMSI might be
// suddenly change regardless of subId, such as switch IMSI feature in modem side.
// If that happens, register new listener with new IMSI and remove old one later.
- if (CollectionUtils.find(mRatListeners,
- it -> it.equalsKey(sub.first, sub.second)) != null) {
+ if (CollectionUtils.any(mRatListeners, it -> it.equalsKey(sub.first, sub.second))) {
continue;
}
@@ -126,8 +127,8 @@
for (final RatTypeListener listener : new ArrayList<>(mRatListeners)) {
// If there is no subId and IMSI matched the listener, removes it.
- if (CollectionUtils.find(filteredNewSubs,
- it -> listener.equalsKey(it.first, it.second)) == null) {
+ if (!CollectionUtils.any(filteredNewSubs,
+ it -> listener.equalsKey(it.first, it.second))) {
handleRemoveRatTypeListener(listener);
}
}
@@ -148,9 +149,10 @@
* @return collapsed RatType for the given subscriberId
*/
public int getRatTypeForSubscriberId(@NonNull String subscriberId) {
- final RatTypeListener match = CollectionUtils.find(mRatListeners,
+ final int index = CollectionUtils.indexOf(mRatListeners,
it -> TextUtils.equals(subscriberId, it.mSubscriberId));
- return match != null ? match.mLastCollapsedRatType : TelephonyManager.NETWORK_TYPE_UNKNOWN;
+ return index != -1 ? mRatListeners.get(index).mLastCollapsedRatType
+ : TelephonyManager.NETWORK_TYPE_UNKNOWN;
}
/**
diff --git a/packages/PackageInstaller/src/com/android/packageinstaller/wear/PackageInstallerImpl.java b/packages/PackageInstaller/src/com/android/packageinstaller/wear/PackageInstallerImpl.java
index 7e0490a..063d789 100644
--- a/packages/PackageInstaller/src/com/android/packageinstaller/wear/PackageInstallerImpl.java
+++ b/packages/PackageInstaller/src/com/android/packageinstaller/wear/PackageInstallerImpl.java
@@ -264,7 +264,8 @@
String action = ACTION_INSTALL_COMMIT + "." + packageName;
IntentFilter intentFilter = new IntentFilter();
intentFilter.addAction(action);
- mContext.registerReceiver(broadcastReceiver, intentFilter);
+ mContext.registerReceiver(broadcastReceiver, intentFilter,
+ Context.RECEIVER_EXPORTED_UNAUDITED);
// Create a matching PendingIntent and use it to generate the IntentSender
Intent broadcastIntent = new Intent(action);
diff --git a/packages/SettingsLib/src/com/android/settingslib/dream/DreamBackend.java b/packages/SettingsLib/src/com/android/settingslib/dream/DreamBackend.java
index ab7b54d..c5e1c8d 100644
--- a/packages/SettingsLib/src/com/android/settingslib/dream/DreamBackend.java
+++ b/packages/SettingsLib/src/com/android/settingslib/dream/DreamBackend.java
@@ -20,6 +20,7 @@
import android.content.ComponentName;
import android.content.Context;
import android.content.Intent;
+import android.content.pm.ApplicationInfo;
import android.content.pm.PackageManager;
import android.content.pm.ResolveInfo;
import android.content.pm.ServiceInfo;
@@ -57,6 +58,7 @@
public boolean isActive;
public ComponentName componentName;
public ComponentName settingsComponentName;
+ public CharSequence description;
@Override
public String toString() {
@@ -123,6 +125,7 @@
DreamInfo dreamInfo = new DreamInfo();
dreamInfo.caption = resolveInfo.loadLabel(pm);
dreamInfo.icon = resolveInfo.loadIcon(pm);
+ dreamInfo.description = getDescription(resolveInfo, pm);
dreamInfo.componentName = getDreamComponentName(resolveInfo);
dreamInfo.isActive = dreamInfo.componentName.equals(activeDream);
dreamInfo.settingsComponentName = getSettingsComponentName(pm, resolveInfo);
@@ -132,9 +135,25 @@
return dreamInfos;
}
+ private static CharSequence getDescription(ResolveInfo resolveInfo, PackageManager pm) {
+ String packageName = resolveInfo.resolvePackageName;
+ ApplicationInfo applicationInfo = null;
+ if (packageName == null) {
+ packageName = resolveInfo.serviceInfo.packageName;
+ applicationInfo = resolveInfo.serviceInfo.applicationInfo;
+ }
+ if (resolveInfo.serviceInfo.descriptionRes != 0) {
+ return pm.getText(packageName,
+ resolveInfo.serviceInfo.descriptionRes,
+ applicationInfo);
+ }
+ return null;
+ }
+
public ComponentName getDefaultDream() {
- if (mDreamManager == null)
+ if (mDreamManager == null) {
return null;
+ }
try {
return mDreamManager.getDefaultDreamComponentForUser(mContext.getUserId());
} catch (RemoteException e) {
diff --git a/packages/SettingsLib/src/com/android/settingslib/inputmethod/InputMethodSettingValuesWrapper.java b/packages/SettingsLib/src/com/android/settingslib/inputmethod/InputMethodSettingValuesWrapper.java
index 39e6dce..5860bda 100644
--- a/packages/SettingsLib/src/com/android/settingslib/inputmethod/InputMethodSettingValuesWrapper.java
+++ b/packages/SettingsLib/src/com/android/settingslib/inputmethod/InputMethodSettingValuesWrapper.java
@@ -27,6 +27,7 @@
import android.view.inputmethod.InputMethodManager;
import com.android.internal.annotations.GuardedBy;
+import com.android.internal.inputmethod.DirectBootAwareness;
import java.util.ArrayList;
import java.util.HashMap;
@@ -88,7 +89,8 @@
public void refreshAllInputMethodAndSubtypes() {
mMethodList.clear();
- mMethodList.addAll(mImm.getInputMethodListAsUser(mContentResolver.getUserId()));
+ mMethodList.addAll(mImm.getInputMethodListAsUser(
+ mContentResolver.getUserId(), DirectBootAwareness.ANY));
}
public List<InputMethodInfo> getInputMethodList() {
diff --git a/packages/SettingsLib/src/com/android/settingslib/wifi/WifiEnterpriseRestrictionUtils.java b/packages/SettingsLib/src/com/android/settingslib/wifi/WifiEnterpriseRestrictionUtils.java
index 2423372..21a4ac6 100644
--- a/packages/SettingsLib/src/com/android/settingslib/wifi/WifiEnterpriseRestrictionUtils.java
+++ b/packages/SettingsLib/src/com/android/settingslib/wifi/WifiEnterpriseRestrictionUtils.java
@@ -29,7 +29,7 @@
private static final String TAG = "WifiEntResUtils";
/**
- * Confirm Wi-Fi tethering is available according to whether user restriction is set
+ * Confirm Wi-Fi tethering is allowed according to whether user restriction is set
*
* @param context A context
* @return whether the device is permitted to use Wi-Fi Tethering
@@ -44,6 +44,22 @@
return true;
}
+ /**
+ * Confirm Wi-Fi Direct is allowed according to whether user restriction is set
+ *
+ * @param context A context
+ * @return whether the device is permitted to use Wi-Fi Direct
+ */
+ public static boolean isWifiDirectAllowed(Context context) {
+ final UserManager userManager = context.getSystemService(UserManager.class);
+ final Bundle restrictions = userManager.getUserRestrictions();
+ if (isAtLeastT() && restrictions.getBoolean(UserManager.DISALLOW_WIFI_DIRECT)) {
+ Log.i(TAG, "Wi-Fi Direct isn't available due to user restriction.");
+ return false;
+ }
+ return true;
+ }
+
@ChecksSdkIntAtLeast(api=Build.VERSION_CODES.TIRAMISU)
private static boolean isAtLeastT() {
return Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU;
diff --git a/packages/SettingsLib/src/com/android/settingslib/wifi/WifiRestrictionsCache.java b/packages/SettingsLib/src/com/android/settingslib/wifi/WifiRestrictionsCache.java
new file mode 100644
index 0000000..7ffae40
--- /dev/null
+++ b/packages/SettingsLib/src/com/android/settingslib/wifi/WifiRestrictionsCache.java
@@ -0,0 +1,131 @@
+/*
+ * 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.settingslib.wifi;
+
+import static android.os.UserManager.DISALLOW_CONFIG_WIFI;
+
+import android.annotation.NonNull;
+import android.content.Context;
+import android.os.Bundle;
+import android.os.UserManager;
+import android.util.SparseArray;
+
+import androidx.annotation.VisibleForTesting;
+
+import java.util.HashMap;
+import java.util.Map;
+
+/**
+ * This is a singleton class for Wi-Fi restrictions caching.
+ */
+public class WifiRestrictionsCache {
+ private static final String TAG = "WifiResCache";
+
+ /**
+ * Manages mapping between user ID and corresponding singleton {@link WifiRestrictionsCache}
+ * object.
+ */
+ @VisibleForTesting
+ protected static final SparseArray<WifiRestrictionsCache> sInstances = new SparseArray<>();
+
+ @VisibleForTesting
+ protected UserManager mUserManager;
+ @VisibleForTesting
+ protected Bundle mUserRestrictions;
+ @VisibleForTesting
+ protected final Map<String, Boolean> mRestrictions = new HashMap<>();
+
+ /**
+ * @return an instance of {@link WifiRestrictionsCache} object.
+ */
+ @NonNull
+ public static WifiRestrictionsCache getInstance(@NonNull Context context) {
+ final int requestUserId = context.getUserId();
+ WifiRestrictionsCache cache;
+ synchronized (sInstances) {
+ // We have same user context as request.
+ if (sInstances.indexOfKey(requestUserId) >= 0) {
+ return sInstances.get(requestUserId);
+ }
+ // Request by a new user context.
+ cache = new WifiRestrictionsCache(context);
+ sInstances.put(context.getUserId(), cache);
+ }
+ return cache;
+ }
+
+ /**
+ * Removes all the instances.
+ */
+ public static void clearInstance() {
+ synchronized (sInstances) {
+ for (int i = 0; i < sInstances.size(); i++) {
+ int key = sInstances.keyAt(i);
+ WifiRestrictionsCache cache = sInstances.get(key);
+ cache.clearRestrictions();
+ sInstances.remove(key);
+ }
+ sInstances.clear();
+ }
+ }
+
+ /**
+ * Constructor to create a singleton class for Wi-Fi restrictions cache.
+ *
+ * @param context The Context this is associated with.
+ */
+ protected WifiRestrictionsCache(@NonNull Context context) {
+ mUserManager = context.getSystemService(UserManager.class);
+ if (mUserManager != null) {
+ mUserRestrictions = mUserManager.getUserRestrictions();
+ }
+ }
+
+ /**
+ * @return the boolean value of the restrictions
+ */
+ public Boolean getRestriction(String key) {
+ if (mUserRestrictions == null) {
+ return false;
+ }
+ Boolean restriction;
+ synchronized (mRestrictions) {
+ if (mRestrictions.containsKey(key)) {
+ return mRestrictions.get(key);
+ }
+ restriction = mUserRestrictions.getBoolean(key);
+ mRestrictions.put(key, restriction);
+ }
+ return restriction;
+ }
+
+ /**
+ * Removes all the restrictions.
+ */
+ public void clearRestrictions() {
+ synchronized (mRestrictions) {
+ mRestrictions.clear();
+ }
+ }
+
+ /**
+ * @return Whether the user is allowed to config Wi-Fi.
+ */
+ public Boolean isConfigWifiAllowed() {
+ return !getRestriction(DISALLOW_CONFIG_WIFI);
+ }
+}
diff --git a/packages/SettingsLib/src/com/android/settingslib/wifi/WifiTracker.java b/packages/SettingsLib/src/com/android/settingslib/wifi/WifiTracker.java
index bf5ab1c..426ea42 100644
--- a/packages/SettingsLib/src/com/android/settingslib/wifi/WifiTracker.java
+++ b/packages/SettingsLib/src/com/android/settingslib/wifi/WifiTracker.java
@@ -342,7 +342,8 @@
resumeScanning();
if (!mRegistered) {
- mContext.registerReceiver(mReceiver, mFilter, null /* permission */, mWorkHandler);
+ mContext.registerReceiver(mReceiver, mFilter, null /* permission */, mWorkHandler,
+ Context.RECEIVER_EXPORTED_UNAUDITED);
// NetworkCallback objects cannot be reused. http://b/20701525 .
mNetworkCallback = new WifiTrackerNetworkCallback();
mConnectivityManager.registerNetworkCallback(
diff --git a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/wifi/WifiEnterpriseRestrictionUtilsTest.java b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/wifi/WifiEnterpriseRestrictionUtilsTest.java
index 455dc64..3c339de 100644
--- a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/wifi/WifiEnterpriseRestrictionUtilsTest.java
+++ b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/wifi/WifiEnterpriseRestrictionUtilsTest.java
@@ -77,4 +77,30 @@
assertThat(WifiEnterpriseRestrictionUtils.isWifiTetheringAllowed(mContext)).isTrue();
}
+
+ @Test
+ public void isWifiDirectAllowed_setSDKForS_shouldReturnTrue() {
+ ReflectionHelpers.setStaticField(Build.VERSION.class, "SDK_INT", Build.VERSION_CODES.S);
+ when(mBundle.getBoolean(UserManager.DISALLOW_WIFI_DIRECT)).thenReturn(true);
+
+ assertThat(WifiEnterpriseRestrictionUtils.isWifiDirectAllowed(mContext)).isTrue();
+ }
+
+ @Test
+ public void isWifiDirectAllowed_setSDKForTAndDisallowForRestriction_shouldReturnFalse() {
+ ReflectionHelpers.setStaticField(
+ Build.VERSION.class, "SDK_INT", Build.VERSION_CODES.TIRAMISU);
+ when(mBundle.getBoolean(UserManager.DISALLOW_WIFI_DIRECT)).thenReturn(true);
+
+ assertThat(WifiEnterpriseRestrictionUtils.isWifiDirectAllowed(mContext)).isFalse();
+ }
+
+ @Test
+ public void isWifiDirectAllowed_setSDKForTAndAllowForRestriction_shouldReturnTrue() {
+ ReflectionHelpers.setStaticField(
+ Build.VERSION.class, "SDK_INT", Build.VERSION_CODES.TIRAMISU);
+ when(mBundle.getBoolean(UserManager.DISALLOW_WIFI_DIRECT)).thenReturn(false);
+
+ assertThat(WifiEnterpriseRestrictionUtils.isWifiDirectAllowed(mContext)).isTrue();
+ }
}
diff --git a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/wifi/WifiRestrictionsCacheTest.java b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/wifi/WifiRestrictionsCacheTest.java
new file mode 100644
index 0000000..404e0e8
--- /dev/null
+++ b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/wifi/WifiRestrictionsCacheTest.java
@@ -0,0 +1,172 @@
+/*
+ * 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.settingslib.wifi;
+
+import static android.os.UserManager.DISALLOW_CONFIG_WIFI;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import static org.mockito.ArgumentMatchers.anyString;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.never;
+import static org.mockito.Mockito.reset;
+import static org.mockito.Mockito.spy;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.when;
+
+import android.content.Context;
+import android.os.Bundle;
+import android.os.UserManager;
+
+import androidx.test.core.app.ApplicationProvider;
+
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.Mock;
+import org.mockito.junit.MockitoJUnit;
+import org.mockito.junit.MockitoRule;
+import org.robolectric.RobolectricTestRunner;
+
+@RunWith(RobolectricTestRunner.class)
+public class WifiRestrictionsCacheTest {
+
+ private static final int USER_OWNER = 0;
+ private static final int USER_1 = 1;
+ private static final int USER_2 = 2;
+ private static final int USER_3 = 3;
+ private static final int USER_GUEST = 10;
+
+ @Rule
+ public final MockitoRule mMockitoRule = MockitoJUnit.rule();
+ @Mock
+ UserManager mUserManager;
+ @Mock
+ Bundle mUserRestrictionsOwner;
+ @Mock
+ Bundle mUserRestrictionsGuest;
+
+ private Context mContext;
+ private WifiRestrictionsCache mWifiRestrictionsCacheOwner;
+ private WifiRestrictionsCache mWifiRestrictionsCacheGuest;
+
+ @Before
+ public void setUp() {
+ mContext = spy(ApplicationProvider.getApplicationContext());
+ when(mContext.getSystemService(UserManager.class)).thenReturn(mUserManager);
+
+ when(mContext.getUserId()).thenReturn(USER_OWNER);
+ when(mUserManager.getUserRestrictions()).thenReturn(mUserRestrictionsOwner);
+ when(mUserRestrictionsOwner.getBoolean(anyString())).thenReturn(false);
+ mWifiRestrictionsCacheOwner = WifiRestrictionsCache.getInstance(mContext);
+
+ when(mContext.getUserId()).thenReturn(USER_GUEST);
+ when(mUserManager.getUserRestrictions()).thenReturn(mUserRestrictionsGuest);
+ when(mUserRestrictionsGuest.getBoolean(anyString())).thenReturn(true);
+ mWifiRestrictionsCacheGuest = WifiRestrictionsCache.getInstance(mContext);
+ }
+
+ @After
+ public void tearDown() {
+ WifiRestrictionsCache.clearInstance();
+ }
+
+ @Test
+ public void getInstance_sameUserId_sameInstance() {
+ when(mContext.getUserId()).thenReturn(USER_OWNER);
+ WifiRestrictionsCache instance1 = WifiRestrictionsCache.getInstance(mContext);
+
+ WifiRestrictionsCache instance2 = WifiRestrictionsCache.getInstance(mContext);
+
+ assertThat(instance1).isEqualTo(instance2);
+ }
+
+ @Test
+ public void getInstance_diffUserId_diffInstance() {
+ when(mContext.getUserId()).thenReturn(USER_OWNER);
+ WifiRestrictionsCache instance1 = WifiRestrictionsCache.getInstance(mContext);
+
+ when(mContext.getUserId()).thenReturn(USER_GUEST);
+ WifiRestrictionsCache instance2 = WifiRestrictionsCache.getInstance(mContext);
+
+ assertThat(instance1).isNotEqualTo(instance2);
+ }
+
+ @Test
+ public void clearInstance_instanceShouldBeEmpty() {
+ WifiRestrictionsCache.clearInstance();
+
+ assertThat(WifiRestrictionsCache.sInstances.size()).isEqualTo(0);
+ }
+
+ @Test
+ public void getRestriction_firstTime_getFromSystem() {
+ Bundle userRestrictions = mock(Bundle.class);
+ WifiRestrictionsCache wifiRestrictionsCache = mockInstance(USER_1, userRestrictions);
+
+ wifiRestrictionsCache.getRestriction(DISALLOW_CONFIG_WIFI);
+
+ verify(userRestrictions).getBoolean(DISALLOW_CONFIG_WIFI);
+ }
+
+ @Test
+ public void getRestriction_secondTime_notGetFromSystem() {
+ Bundle userRestrictions = mock(Bundle.class);
+ WifiRestrictionsCache wifiRestrictionsCache = mockInstance(USER_2, userRestrictions);
+ // First time to get the restriction value
+ wifiRestrictionsCache.getRestriction(DISALLOW_CONFIG_WIFI);
+ reset(userRestrictions);
+
+ // Second time to get the restriction value
+ wifiRestrictionsCache.getRestriction(DISALLOW_CONFIG_WIFI);
+
+ verify(userRestrictions, never()).getBoolean(DISALLOW_CONFIG_WIFI);
+ }
+
+ @Test
+ public void clearRestrictions_shouldGetRestrictionFromSystemAgain() {
+ Bundle userRestrictions = mock(Bundle.class);
+ WifiRestrictionsCache wifiRestrictionsCache = mockInstance(USER_3, userRestrictions);
+ // First time to get the restriction value
+ wifiRestrictionsCache.getRestriction(DISALLOW_CONFIG_WIFI);
+ reset(userRestrictions);
+
+ // Clear the cache and then second time to get the restriction value
+ wifiRestrictionsCache.clearRestrictions();
+ wifiRestrictionsCache.getRestriction(DISALLOW_CONFIG_WIFI);
+
+ verify(userRestrictions).getBoolean(DISALLOW_CONFIG_WIFI);
+ }
+
+ @Test
+ public void isConfigWifiAllowed_ownerUser_returnTrue() {
+ assertThat(mWifiRestrictionsCacheOwner.isConfigWifiAllowed()).isTrue();
+ }
+
+ @Test
+ public void isConfigWifiAllowed_guestUser_returnFalse() {
+ assertThat(mWifiRestrictionsCacheGuest.isConfigWifiAllowed()).isFalse();
+ }
+
+ private WifiRestrictionsCache mockInstance(int userId, Bundle userRestrictions) {
+ when(mContext.getUserId()).thenReturn(userId);
+ when(mUserManager.getUserRestrictions()).thenReturn(userRestrictions);
+ return WifiRestrictionsCache.getInstance(mContext);
+ }
+}
diff --git a/packages/SettingsProvider/src/android/provider/settings/backup/SecureSettings.java b/packages/SettingsProvider/src/android/provider/settings/backup/SecureSettings.java
index 4e2111c..16cece9 100644
--- a/packages/SettingsProvider/src/android/provider/settings/backup/SecureSettings.java
+++ b/packages/SettingsProvider/src/android/provider/settings/backup/SecureSettings.java
@@ -188,6 +188,7 @@
Settings.Secure.ACCESSIBILITY_FLOATING_MENU_ICON_TYPE,
Settings.Secure.ACCESSIBILITY_FLOATING_MENU_OPACITY,
Settings.Secure.ACCESSIBILITY_FLOATING_MENU_FADE_ENABLED,
+ Settings.Secure.ODI_CAPTIONS_VOLUME_UI_ENABLED,
Settings.Secure.NOTIFICATION_BUBBLES,
Settings.Secure.LOCATION_TIME_ZONE_DETECTION_ENABLED,
Settings.Secure.LOCKSCREEN_SHOW_CONTROLS,
diff --git a/packages/SettingsProvider/src/android/provider/settings/validators/SecureSettingsValidators.java b/packages/SettingsProvider/src/android/provider/settings/validators/SecureSettingsValidators.java
index dd1cb6b..688c48d 100644
--- a/packages/SettingsProvider/src/android/provider/settings/validators/SecureSettingsValidators.java
+++ b/packages/SettingsProvider/src/android/provider/settings/validators/SecureSettingsValidators.java
@@ -321,5 +321,7 @@
}
return true;
});
+ VALIDATORS.put(Secure.ODI_CAPTIONS_VOLUME_UI_ENABLED, BOOLEAN_VALIDATOR);
+
}
}
diff --git a/packages/SettingsProvider/src/com/android/providers/settings/SettingsProtoDumpUtil.java b/packages/SettingsProvider/src/com/android/providers/settings/SettingsProtoDumpUtil.java
index 38a258f..103e141 100644
--- a/packages/SettingsProvider/src/com/android/providers/settings/SettingsProtoDumpUtil.java
+++ b/packages/SettingsProvider/src/com/android/providers/settings/SettingsProtoDumpUtil.java
@@ -1813,6 +1813,9 @@
dumpSetting(s, p,
Settings.Secure.ACCESSIBILITY_FLOATING_MENU_FADE_ENABLED,
SecureSettingsProto.Accessibility.ACCESSIBILITY_FLOATING_MENU_FADE_ENABLED);
+ dumpSetting(s, p,
+ Settings.Secure.ODI_CAPTIONS_VOLUME_UI_ENABLED,
+ SecureSettingsProto.Accessibility.ODI_CAPTIONS_VOLUME_UI_ENABLED);
p.end(accessibilityToken);
final long adaptiveSleepToken = p.start(SecureSettingsProto.ADAPTIVE_SLEEP);
diff --git a/packages/SettingsProvider/src/com/android/providers/settings/SettingsProvider.java b/packages/SettingsProvider/src/com/android/providers/settings/SettingsProvider.java
index 1e2d4e9..927f11f 100644
--- a/packages/SettingsProvider/src/com/android/providers/settings/SettingsProvider.java
+++ b/packages/SettingsProvider/src/com/android/providers/settings/SettingsProvider.java
@@ -2117,7 +2117,7 @@
}
if ((ai.flags & ApplicationInfo.FLAG_TEST_ONLY) == 0) {
// Skip checking readable annotations for test_only apps
- checkReadableAnnotation(settingsType, settingName, ai.targetSandboxVersion);
+ checkReadableAnnotation(settingsType, settingName, ai.targetSdkVersion);
}
/**
* some settings need additional permission check, this is to have a matching security
diff --git a/packages/SettingsProvider/test/src/android/provider/SettingsBackupTest.java b/packages/SettingsProvider/test/src/android/provider/SettingsBackupTest.java
index ed813a0..368dda1 100644
--- a/packages/SettingsProvider/test/src/android/provider/SettingsBackupTest.java
+++ b/packages/SettingsProvider/test/src/android/provider/SettingsBackupTest.java
@@ -657,6 +657,7 @@
Settings.Global.Wearable.WRIST_ORIENTATION_MODE,
Settings.Global.Wearable.CLOCKWORK_SYSUI_PACKAGE,
Settings.Global.Wearable.CLOCKWORK_SYSUI_MAIN_ACTIVITY,
+ Settings.Global.Wearable.CLOCKWORK_LONG_PRESS_TO_ASSISTANT_ENABLED,
Settings.Global.Wearable.WEAR_ACTIVITY_AUTO_RESUME_TIMEOUT_SET_BY_USER);
private static final Set<String> BACKUP_DENY_LIST_SECURE_SETTINGS =
diff --git a/packages/SystemUI/AndroidManifest.xml b/packages/SystemUI/AndroidManifest.xml
index 1e9a41e..e907efb 100644
--- a/packages/SystemUI/AndroidManifest.xml
+++ b/packages/SystemUI/AndroidManifest.xml
@@ -260,6 +260,8 @@
<uses-permission android:name="android.permission.OBSERVE_GRANT_REVOKE_PERMISSIONS" />
<!-- For handling silent audio recordings -->
<uses-permission android:name="android.permission.MODIFY_AUDIO_ROUTING" />
+ <!-- For asking AudioManager audio information -->
+ <uses-permission android:name="android.permission.QUERY_AUDIO_STATE"/>
<!-- to read and change hvac values in a car -->
<uses-permission android:name="android.car.permission.CONTROL_CAR_CLIMATE" />
diff --git a/packages/SystemUI/res/values-television/config.xml b/packages/SystemUI/res/values-television/config.xml
index 2f0957c..2eff692 100644
--- a/packages/SystemUI/res/values-television/config.xml
+++ b/packages/SystemUI/res/values-television/config.xml
@@ -42,6 +42,7 @@
<item>@string/config_systemUIVendorServiceComponent</item>
<item>com.android.systemui.SliceBroadcastRelayHandler</item>
<item>com.android.systemui.statusbar.notification.InstantAppNotifier</item>
+ <item>com.android.systemui.accessibility.WindowMagnification</item>
<item>com.android.systemui.toast.ToastUI</item>
<item>com.android.systemui.wmshell.WMShell</item>
<item>com.android.systemui.media.systemsounds.HomeSoundEffectController</item>
diff --git a/packages/SystemUI/res/values/strings.xml b/packages/SystemUI/res/values/strings.xml
index b64f81a..8482044 100644
--- a/packages/SystemUI/res/values/strings.xml
+++ b/packages/SystemUI/res/values/strings.xml
@@ -1273,6 +1273,10 @@
<!-- [CHAR LIMIT=NONE] Importance Tuner setting title -->
<string name="tuner_full_importance_settings">Power notification controls</string>
+
+ <!-- [CHAR LIMIT=NONE] Notification camera based rotation enabled description -->
+ <string name="rotation_lock_camera_rotation_on">On - Face-based</string>
+
<string name="power_notification_controls_description">With power notification controls, you can set an importance level from 0 to 5 for an app\'s notifications.
\n\n<b>Level 5</b>
\n- Show at the top of the notification list
diff --git a/packages/SystemUI/src/com/android/systemui/flags/Flags.java b/packages/SystemUI/src/com/android/systemui/flags/Flags.java
index 4e852a8..726f865 100644
--- a/packages/SystemUI/src/com/android/systemui/flags/Flags.java
+++ b/packages/SystemUI/src/com/android/systemui/flags/Flags.java
@@ -43,15 +43,18 @@
public static final BooleanFlag NEW_NOTIFICATION_PIPELINE_RENDERING =
new BooleanFlag(101, false);
- public static final BooleanFlag NOTIFICATION_UPDATES =
- new BooleanFlag(102, true);
-
public static final BooleanFlag NOTIFICATION_PIPELINE_DEVELOPER_LOGGING =
new BooleanFlag(103, false);
public static final ResourceBooleanFlag NOTIFICATION_SHADE_DRAG =
new ResourceBooleanFlag(104, R.bool.config_enableNotificationShadeDrag);
+ public static final BooleanFlag NSSL_DEBUG_LINES =
+ new BooleanFlag(105, false);
+
+ public static final BooleanFlag NSSL_DEBUG_REMOVE_ANIMATION =
+ new BooleanFlag(106, false);
+
/***************************************/
// 200 - keyguard/lockscreen
public static final BooleanFlag KEYGUARD_LAYOUT =
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardService.java b/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardService.java
index 22a69d4..fb31d88 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardService.java
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardService.java
@@ -583,6 +583,18 @@
checkPermission();
mKeyguardViewMediator.onShortPowerPressedGoHome();
}
+
+ @Override
+ public void dismissKeyguardToLaunch(Intent intentToLaunch) {
+ checkPermission();
+ mKeyguardViewMediator.dismissKeyguardToLaunch(intentToLaunch);
+ }
+
+ @Override
+ public void onSystemKeyPressed(int keycode) {
+ checkPermission();
+ mKeyguardViewMediator.onSystemKeyPressed(keycode);
+ }
};
}
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewMediator.java b/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewMediator.java
index 0512d48..8f4a28c 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewMediator.java
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewMediator.java
@@ -2750,6 +2750,14 @@
// do nothing
}
+ public void dismissKeyguardToLaunch(Intent intentToLaunch) {
+ // do nothing
+ }
+
+ public void onSystemKeyPressed(int keycode) {
+ // do nothing
+ }
+
public ViewMediatorCallback getViewMediatorCallback() {
return mViewMediatorCallback;
}
diff --git a/packages/SystemUI/src/com/android/systemui/media/taptotransfer/MediaTttCommandLineHelper.kt b/packages/SystemUI/src/com/android/systemui/media/taptotransfer/MediaTttCommandLineHelper.kt
index 99b283e..5a86723 100644
--- a/packages/SystemUI/src/com/android/systemui/media/taptotransfer/MediaTttCommandLineHelper.kt
+++ b/packages/SystemUI/src/com/android/systemui/media/taptotransfer/MediaTttCommandLineHelper.kt
@@ -71,20 +71,32 @@
when (args[1]) {
MOVE_CLOSER_TO_TRANSFER_COMMAND_NAME -> {
mediaTttChipControllerSender.displayChip(
- MoveCloserToTransfer(appIconDrawable, otherDeviceName)
+ MoveCloserToTransfer(
+ appIconDrawable, APP_ICON_CONTENT_DESCRIPTION, otherDeviceName
+ )
)
}
TRANSFER_INITIATED_COMMAND_NAME -> {
val futureTask = FutureTask { fakeUndoRunnable }
mediaTttChipControllerSender.displayChip(
- TransferInitiated(appIconDrawable, otherDeviceName, futureTask)
+ TransferInitiated(
+ appIconDrawable,
+ APP_ICON_CONTENT_DESCRIPTION,
+ otherDeviceName,
+ futureTask
+ )
)
mainExecutor.executeDelayed({ futureTask.run() }, FUTURE_WAIT_TIME)
}
TRANSFER_SUCCEEDED_COMMAND_NAME -> {
mediaTttChipControllerSender.displayChip(
- TransferSucceeded(appIconDrawable, otherDeviceName, fakeUndoRunnable)
+ TransferSucceeded(
+ appIconDrawable,
+ APP_ICON_CONTENT_DESCRIPTION,
+ otherDeviceName,
+ fakeUndoRunnable
+ )
)
}
else -> {
@@ -118,7 +130,9 @@
/** A command to DISPLAY the media ttt chip on the RECEIVER device. */
inner class AddChipCommandReceiver : Command {
override fun execute(pw: PrintWriter, args: List<String>) {
- mediaTttChipControllerReceiver.displayChip(ChipStateReceiver(appIconDrawable))
+ mediaTttChipControllerReceiver.displayChip(
+ ChipStateReceiver(appIconDrawable, APP_ICON_CONTENT_DESCRIPTION)
+ )
}
override fun help(pw: PrintWriter) {
pw.println("Usage: adb shell cmd statusbar $ADD_CHIP_COMMAND_RECEIVER_TAG")
@@ -156,4 +170,5 @@
val TRANSFER_SUCCEEDED_COMMAND_NAME = TransferSucceeded::class.simpleName!!
private const val FUTURE_WAIT_TIME = 2000L
+private const val APP_ICON_CONTENT_DESCRIPTION = "Fake media app icon"
private const val TAG = "MediaTapToTransferCli"
diff --git a/packages/SystemUI/src/com/android/systemui/media/taptotransfer/common/MediaTttChipControllerCommon.kt b/packages/SystemUI/src/com/android/systemui/media/taptotransfer/common/MediaTttChipControllerCommon.kt
index 3b429c8..67721a5 100644
--- a/packages/SystemUI/src/com/android/systemui/media/taptotransfer/common/MediaTttChipControllerCommon.kt
+++ b/packages/SystemUI/src/com/android/systemui/media/taptotransfer/common/MediaTttChipControllerCommon.kt
@@ -99,6 +99,7 @@
internal fun setIcon(chipState: T, currentChipView: ViewGroup) {
currentChipView.findViewById<CachingIconView>(R.id.app_icon).apply {
this.setImageDrawable(chipState.appIconDrawable)
+ this.contentDescription = chipState.appIconContentDescription
}
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/media/taptotransfer/common/MediaTttChipState.kt b/packages/SystemUI/src/com/android/systemui/media/taptotransfer/common/MediaTttChipState.kt
index 1e475a5..c510cbb 100644
--- a/packages/SystemUI/src/com/android/systemui/media/taptotransfer/common/MediaTttChipState.kt
+++ b/packages/SystemUI/src/com/android/systemui/media/taptotransfer/common/MediaTttChipState.kt
@@ -22,7 +22,9 @@
* A superclass chip state that will be subclassed by the sender chip and receiver chip.
*
* @property appIconDrawable a drawable representing the icon of the app playing the media.
+ * @property appIconContentDescription a string to use as the content description for the icon.
*/
open class MediaTttChipState(
- internal val appIconDrawable: Drawable
+ internal val appIconDrawable: Drawable,
+ internal val appIconContentDescription: String
)
diff --git a/packages/SystemUI/src/com/android/systemui/media/taptotransfer/receiver/ChipStateReceiver.kt b/packages/SystemUI/src/com/android/systemui/media/taptotransfer/receiver/ChipStateReceiver.kt
index 5397235..df6b934 100644
--- a/packages/SystemUI/src/com/android/systemui/media/taptotransfer/receiver/ChipStateReceiver.kt
+++ b/packages/SystemUI/src/com/android/systemui/media/taptotransfer/receiver/ChipStateReceiver.kt
@@ -24,5 +24,6 @@
* the receiver device.
*/
class ChipStateReceiver(
- appIconDrawable: Drawable
-) : MediaTttChipState(appIconDrawable)
+ appIconDrawable: Drawable,
+ appIconContentDescription: String
+) : MediaTttChipState(appIconDrawable, appIconContentDescription)
diff --git a/packages/SystemUI/src/com/android/systemui/media/taptotransfer/sender/ChipStateSender.kt b/packages/SystemUI/src/com/android/systemui/media/taptotransfer/sender/ChipStateSender.kt
index 24943b9..b1f6faa 100644
--- a/packages/SystemUI/src/com/android/systemui/media/taptotransfer/sender/ChipStateSender.kt
+++ b/packages/SystemUI/src/com/android/systemui/media/taptotransfer/sender/ChipStateSender.kt
@@ -34,9 +34,10 @@
*/
sealed class ChipStateSender(
appIconDrawable: Drawable,
+ appIconContentDescription: String,
@StringRes internal val chipText: Int,
internal val otherDeviceName: String,
-) : MediaTttChipState(appIconDrawable)
+) : MediaTttChipState(appIconDrawable, appIconContentDescription)
/**
* A state representing that the two devices are close but not close enough to initiate a transfer.
@@ -44,8 +45,14 @@
*/
class MoveCloserToTransfer(
appIconDrawable: Drawable,
+ appIconContentDescription: String,
otherDeviceName: String,
-) : ChipStateSender(appIconDrawable, R.string.media_move_closer_to_transfer, otherDeviceName)
+) : ChipStateSender(
+ appIconDrawable,
+ appIconContentDescription,
+ R.string.media_move_closer_to_transfer,
+ otherDeviceName
+)
/**
* A state representing that a transfer has been initiated (but not completed).
@@ -57,9 +64,15 @@
*/
class TransferInitiated(
appIconDrawable: Drawable,
+ appIconContentDescription: String,
otherDeviceName: String,
val future: Future<Runnable?>
-) : ChipStateSender(appIconDrawable, R.string.media_transfer_playing, otherDeviceName)
+) : ChipStateSender(
+ appIconDrawable,
+ appIconContentDescription,
+ R.string.media_transfer_playing,
+ otherDeviceName
+)
/**
* A state representing that a transfer has been successfully completed.
@@ -69,6 +82,11 @@
*/
class TransferSucceeded(
appIconDrawable: Drawable,
+ appIconContentDescription: String,
otherDeviceName: String,
val undoRunnable: Runnable? = null
-) : ChipStateSender(appIconDrawable, R.string.media_transfer_playing, otherDeviceName)
+) : ChipStateSender(appIconDrawable,
+ appIconContentDescription,
+ R.string.media_transfer_playing,
+ otherDeviceName
+)
diff --git a/packages/SystemUI/src/com/android/systemui/media/taptotransfer/sender/MediaTttChipControllerSender.kt b/packages/SystemUI/src/com/android/systemui/media/taptotransfer/sender/MediaTttChipControllerSender.kt
index fce4b98..77d3d70 100644
--- a/packages/SystemUI/src/com/android/systemui/media/taptotransfer/sender/MediaTttChipControllerSender.kt
+++ b/packages/SystemUI/src/com/android/systemui/media/taptotransfer/sender/MediaTttChipControllerSender.kt
@@ -93,7 +93,10 @@
mainExecutor.execute {
displayChip(
TransferSucceeded(
- chipState.appIconDrawable, chipState.otherDeviceName, undoRunnable
+ chipState.appIconDrawable,
+ chipState.appIconContentDescription,
+ chipState.otherDeviceName,
+ undoRunnable
)
)
}
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/RotationLockTile.java b/packages/SystemUI/src/com/android/systemui/qs/tiles/RotationLockTile.java
index 0bbb5bd..082dc5c 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/RotationLockTile.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/RotationLockTile.java
@@ -16,12 +16,18 @@
package com.android.systemui.qs.tiles;
+import static android.hardware.SensorPrivacyManager.Sensors.CAMERA;
+
+import static com.android.systemui.statusbar.policy.RotationLockControllerImpl.hasSufficientPermission;
+
import android.content.Intent;
import android.content.res.Configuration;
import android.content.res.Resources;
+import android.hardware.SensorPrivacyManager;
import android.os.Handler;
import android.os.Looper;
import android.provider.Settings;
+import android.provider.Settings.Secure;
import android.service.quicksettings.Tile;
import android.view.View;
import android.widget.Switch;
@@ -38,18 +44,25 @@
import com.android.systemui.plugins.qs.QSTile.BooleanState;
import com.android.systemui.plugins.statusbar.StatusBarStateController;
import com.android.systemui.qs.QSHost;
+import com.android.systemui.qs.SettingObserver;
import com.android.systemui.qs.logging.QSLogger;
import com.android.systemui.qs.tileimpl.QSTileImpl;
+import com.android.systemui.statusbar.policy.BatteryController;
import com.android.systemui.statusbar.policy.RotationLockController;
import com.android.systemui.statusbar.policy.RotationLockController.RotationLockControllerCallback;
+import com.android.systemui.util.settings.SecureSettings;
import javax.inject.Inject;
/** Quick settings tile: Rotation **/
-public class RotationLockTile extends QSTileImpl<BooleanState> {
+public class RotationLockTile extends QSTileImpl<BooleanState> implements
+ BatteryController.BatteryStateChangeCallback {
private final Icon mIcon = ResourceIcon.get(com.android.internal.R.drawable.ic_qs_auto_rotate);
private final RotationLockController mController;
+ private final SensorPrivacyManager mPrivacyManager;
+ private final BatteryController mBatteryController;
+ private final SettingObserver mSetting;
@Inject
public RotationLockTile(
@@ -61,12 +74,41 @@
StatusBarStateController statusBarStateController,
ActivityStarter activityStarter,
QSLogger qsLogger,
- RotationLockController rotationLockController
+ RotationLockController rotationLockController,
+ SensorPrivacyManager privacyManager,
+ BatteryController batteryController,
+ SecureSettings secureSettings
) {
super(host, backgroundLooper, mainHandler, falsingManager, metricsLogger,
statusBarStateController, activityStarter, qsLogger);
mController = rotationLockController;
mController.observe(this, mCallback);
+ mPrivacyManager = privacyManager;
+ mBatteryController = batteryController;
+ int currentUser = host.getUserContext().getUserId();
+ mSetting = new SettingObserver(
+ secureSettings,
+ mHandler,
+ Secure.CAMERA_AUTOROTATE,
+ currentUser
+ ) {
+ @Override
+ protected void handleValueChanged(int value, boolean observedChange) {
+ // mHandler is the background handler so calling this is OK
+ handleRefreshState(null);
+ }
+ };
+ mBatteryController.observe(getLifecycle(), this);
+ }
+
+ @Override
+ protected void handleInitialize() {
+ mPrivacyManager.addSensorPrivacyListener(CAMERA, mSensorPrivacyChangedListener);
+ }
+
+ @Override
+ public void onPowerSaveChanged(boolean isPowerSave) {
+ refreshState();
}
@Override
@@ -95,14 +137,46 @@
protected void handleUpdateState(BooleanState state, Object arg) {
final boolean rotationLocked = mController.isRotationLocked();
+ final boolean powerSave = mBatteryController.isPowerSave();
+ final boolean cameraLocked = mPrivacyManager.isSensorPrivacyEnabled(CAMERA);
+ final boolean cameraRotation =
+ !powerSave && !cameraLocked && hasSufficientPermission(mContext)
+ && mController.isCameraRotationEnabled();
state.value = !rotationLocked;
state.label = mContext.getString(R.string.quick_settings_rotation_unlocked_label);
state.icon = mIcon;
state.contentDescription = getAccessibilityString(rotationLocked);
+ if (!rotationLocked && cameraRotation) {
+ state.secondaryLabel = mContext.getResources().getString(
+ R.string.rotation_lock_camera_rotation_on);
+ } else {
+ state.secondaryLabel = "";
+ }
+ state.stateDescription = state.secondaryLabel;
+
state.expandedAccessibilityClassName = Switch.class.getName();
state.state = state.value ? Tile.STATE_ACTIVE : Tile.STATE_INACTIVE;
}
+ @Override
+ protected void handleDestroy() {
+ super.handleDestroy();
+ mSetting.setListening(false);
+ mPrivacyManager.removeSensorPrivacyListener(CAMERA, mSensorPrivacyChangedListener);
+ }
+
+ @Override
+ public void handleSetListening(boolean listening) {
+ super.handleSetListening(listening);
+ mSetting.setListening(listening);
+ }
+
+ @Override
+ protected void handleUserSwitch(int newUserId) {
+ mSetting.setUserId(newUserId);
+ handleRefreshState(null);
+ }
+
public static boolean isCurrentOrientationLockPortrait(RotationLockController controller,
Resources resources) {
int lockOrientation = controller.getRotationLockOrientation();
@@ -140,4 +214,8 @@
refreshState(rotationLocked);
}
};
+
+ private final SensorPrivacyManager.OnSensorPrivacyChangedListener
+ mSensorPrivacyChangedListener =
+ (sensor, enabled) -> refreshState();
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationMediaManager.java b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationMediaManager.java
index 210ee96..3730d12 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationMediaManager.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationMediaManager.java
@@ -379,6 +379,7 @@
}
}
+ @Nullable
public String getMediaNotificationKey() {
return mMediaNotificationKey;
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationEntryManager.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationEntryManager.java
index f500d39..4717b3a 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationEntryManager.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationEntryManager.java
@@ -20,8 +20,6 @@
import static com.android.systemui.statusbar.notification.collection.NotifCollection.REASON_UNKNOWN;
import static com.android.systemui.statusbar.notification.row.NotificationRowContentBinder.InflationCallback;
-import android.annotation.NonNull;
-import android.annotation.Nullable;
import android.app.Notification;
import android.os.RemoteException;
import android.os.SystemClock;
@@ -33,6 +31,9 @@
import android.util.ArraySet;
import android.util.Log;
+import androidx.annotation.NonNull;
+import androidx.annotation.Nullable;
+
import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.statusbar.IStatusBarService;
import com.android.internal.statusbar.NotificationVisibility;
@@ -123,8 +124,8 @@
* filtered out if for instance they are not for the current user
*/
private final ArrayMap<String, NotificationEntry> mActiveNotifications = new ArrayMap<>();
- @VisibleForTesting
/** This is the list of "active notifications for this user in this context" */
+ @VisibleForTesting
protected final ArrayList<NotificationEntry> mSortedAndFiltered = new ArrayList<>();
private final List<NotificationEntry> mReadOnlyNotifications =
Collections.unmodifiableList(mSortedAndFiltered);
@@ -899,7 +900,7 @@
}
/** Calls to NotificationRankingManager and updates mSortedAndFiltered */
- private void updateRankingAndSort(@NonNull RankingMap rankingMap, String reason) {
+ private void updateRankingAndSort(RankingMap rankingMap, String reason) {
if (mNotifPipelineFlags.isNewPipelineEnabled()) {
mLogger.logUseWhileNewPipelineActive("updateRankingAndSort", reason);
return;
@@ -961,6 +962,7 @@
* Returns a collections containing ALL notifications we know about, including ones that are
* hidden or for other users. See {@link CommonNotifCollection#getAllNotifs()}.
*/
+ @NonNull
@Override
public Collection<NotificationEntry> getAllNotifs() {
mNotifPipelineFlags.checkLegacyPipelineEnabled();
@@ -969,7 +971,7 @@
@Nullable
@Override
- public NotificationEntry getEntry(String key) {
+ public NotificationEntry getEntry(@NonNull String key) {
mNotifPipelineFlags.checkLegacyPipelineEnabled();
return getPendingOrActiveNotif(key);
}
@@ -989,7 +991,7 @@
}
@Override
- public void addCollectionListener(NotifCollectionListener listener) {
+ public void addCollectionListener(@NonNull NotifCollectionListener listener) {
mNotifCollectionListeners.add(listener);
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/NotifCollection.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/NotifCollection.java
index f8f1279..b6b9c3f 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/NotifCollection.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/NotifCollection.java
@@ -44,7 +44,6 @@
import android.annotation.IntDef;
import android.annotation.MainThread;
-import android.annotation.Nullable;
import android.annotation.UserIdInt;
import android.app.Notification;
import android.os.Handler;
@@ -59,6 +58,7 @@
import android.util.Pair;
import androidx.annotation.NonNull;
+import androidx.annotation.Nullable;
import com.android.internal.statusbar.IStatusBarService;
import com.android.systemui.Dumpable;
@@ -193,7 +193,8 @@
}
/** @see NotifPipeline#getEntry(String) () */
- NotificationEntry getEntry(String key) {
+ @Nullable
+ NotificationEntry getEntry(@NonNull String key) {
return mNotificationSet.get(key);
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/BubbleCoordinator.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/BubbleCoordinator.java
index e9b7caa5..85c0064 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/BubbleCoordinator.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/BubbleCoordinator.java
@@ -82,11 +82,8 @@
public void attach(NotifPipeline pipeline) {
mNotifPipeline = pipeline;
mNotifPipeline.addNotificationDismissInterceptor(mDismissInterceptor);
- mNotifPipeline.addFinalizeFilter(mNotifFilter);
- if (mBubblesManagerOptional.isPresent()) {
- mBubblesManagerOptional.get().addNotifCallback(mNotifCallback);
- }
-
+ mNotifPipeline.addPreGroupFilter(mNotifFilter);
+ mBubblesManagerOptional.ifPresent(manager -> manager.addNotifCallback(mNotifCallback));
}
private final NotifFilter mNotifFilter = new NotifFilter(TAG) {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/KeyguardCoordinator.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/KeyguardCoordinator.java
index fe1cd7b..33005b3 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/KeyguardCoordinator.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/KeyguardCoordinator.java
@@ -90,6 +90,7 @@
readShowSilentNotificationSetting();
setupInvalidateNotifListCallbacks();
+ // Filter at the "finalize" stage so that views remain bound by PreparationCoordinator
pipeline.addFinalizeFilter(mNotifFilter);
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/MediaCoordinator.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/MediaCoordinator.java
index 8769969..ecee006 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/MediaCoordinator.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/MediaCoordinator.java
@@ -49,6 +49,6 @@
@Override
public void attach(NotifPipeline pipeline) {
- pipeline.addFinalizeFilter(mMediaFilter);
+ pipeline.addPreGroupFilter(mMediaFilter);
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/notifcollection/CommonNotifCollection.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/notifcollection/CommonNotifCollection.java
index 471c357..beaa1ba 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/notifcollection/CommonNotifCollection.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/notifcollection/CommonNotifCollection.java
@@ -16,6 +16,7 @@
package com.android.systemui.statusbar.notification.collection.notifcollection;
+import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import com.android.systemui.statusbar.notification.NotificationEntryManager;
@@ -37,7 +38,7 @@
* Registers a listener to be informed when notifications are created, added, updated, removed,
* or deleted.
*/
- void addCollectionListener(NotifCollectionListener listener);
+ void addCollectionListener(@NonNull NotifCollectionListener listener);
/**
* Returns the list of all known notifications, i.e. the notifications that are currently posted
@@ -46,11 +47,11 @@
*
* The returned collection is read-only, unsorted, unfiltered, and ungrouped.
*/
- Collection<NotificationEntry> getAllNotifs();
+ @NonNull Collection<NotificationEntry> getAllNotifs();
/**
* Returns the notification entry for the given notification key;
* the returned entry (if present) may be in any state.
*/
- @Nullable NotificationEntry getEntry(String key);
+ @Nullable NotificationEntry getEntry(@NonNull String key);
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/interruption/BypassHeadsUpNotifier.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/interruption/BypassHeadsUpNotifier.kt
index b2e15f4..b61a540 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/interruption/BypassHeadsUpNotifier.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/interruption/BypassHeadsUpNotifier.kt
@@ -77,11 +77,11 @@
override fun onPrimaryMetadataOrStateChanged(metadata: MediaMetadata?, state: Int) {
val previous = currentMediaEntry
- var newEntry = commonNotifCollection.getEntry(mediaManager.mediaNotificationKey)
- if (!NotificationMediaManager.isPlayingState(state)) {
- newEntry = null
- }
- currentMediaEntry = newEntry
+ val mediaNotificationKey = mediaManager.mediaNotificationKey
+ currentMediaEntry =
+ if (mediaNotificationKey != null && NotificationMediaManager.isPlayingState(state))
+ commonNotifCollection.getEntry(mediaNotificationKey)
+ else null
updateAutoHeadsUp(previous)
updateAutoHeadsUp(currentMediaEntry)
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ExpandableView.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ExpandableView.java
index 4b3d6f7..ab36da5 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ExpandableView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ExpandableView.java
@@ -23,8 +23,10 @@
import android.graphics.Rect;
import android.util.AttributeSet;
import android.util.IndentingPrintWriter;
+import android.util.Log;
import android.view.View;
import android.view.ViewGroup;
+import android.view.ViewParent;
import android.widget.FrameLayout;
import androidx.annotation.Nullable;
@@ -517,6 +519,36 @@
return mChangingPosition;
}
+ /**
+ * Called before adding this view to a group, which would always throw an exception if this view
+ * has a parent, so clean up the transient container and throw an exception if the parent isn't
+ * a transient container. Provide as much detail in the event of a crash as possible.
+ */
+ public void removeFromTransientContainerForAdditionTo(ViewGroup newParent) {
+ final ViewParent parent = getParent();
+ if (parent == null) {
+ // If this view has no parent, the add will succeed, so do nothing.
+ return;
+ }
+ ViewGroup transientContainer = getTransientContainer();
+ if (transientContainer == null) {
+ throw new IllegalStateException(
+ "Can't add view " + this + " to container " + newParent + "; current parent "
+ + parent + " is not a transient container");
+ }
+ if (transientContainer != parent) {
+ throw new IllegalStateException(
+ "Expandable view " + this + " has transient container " + transientContainer
+ + " which is not the same as its parent " + parent);
+ }
+ if (parent != newParent) {
+ Log.w(TAG, "Moving view " + this + " from transient container "
+ + transientContainer + " to parent " + newParent);
+ }
+ transientContainer.removeTransientView(this);
+ setTransientContainer(null);
+ }
+
public void setTransientContainer(ViewGroup transientContainer) {
mTransientContainer = transientContainer;
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationMenuRow.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationMenuRow.java
index 3a37fb4..9d599cb 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationMenuRow.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationMenuRow.java
@@ -41,11 +41,8 @@
import android.widget.FrameLayout.LayoutParams;
import com.android.internal.annotations.VisibleForTesting;
-import com.android.systemui.Dependency;
import com.android.systemui.R;
import com.android.systemui.animation.Interpolators;
-import com.android.systemui.flags.FeatureFlags;
-import com.android.systemui.flags.Flags;
import com.android.systemui.plugins.statusbar.NotificationMenuRowPlugin;
import com.android.systemui.statusbar.AlphaOptimizedImageView;
import com.android.systemui.statusbar.notification.collection.NotificationEntry;
@@ -304,12 +301,9 @@
} else {
mMenuContainer = new FrameLayout(mContext);
}
- // The setting can win (which is needed for tests) but if not set, then use the flag
final int showDismissSetting = Settings.Global.getInt(mContext.getContentResolver(),
- Settings.Global.SHOW_NEW_NOTIF_DISMISS, -1);
- final boolean newFlowHideShelf = showDismissSetting == -1
- ? Dependency.get(FeatureFlags.class).isEnabled(Flags.NOTIFICATION_UPDATES)
- : showDismissSetting == 1;
+ Settings.Global.SHOW_NEW_NOTIF_DISMISS, /* default = */ 1);
+ final boolean newFlowHideShelf = showDismissSetting == 1;
if (newFlowHideShelf) {
return;
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationChildrenContainer.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationChildrenContainer.java
index 046a133..a3fe47c 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationChildrenContainer.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationChildrenContainer.java
@@ -44,6 +44,7 @@
import com.android.systemui.statusbar.notification.NotificationUtils;
import com.android.systemui.statusbar.notification.collection.legacy.VisualStabilityManager;
import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow;
+import com.android.systemui.statusbar.notification.row.ExpandableView;
import com.android.systemui.statusbar.notification.row.HybridGroupManager;
import com.android.systemui.statusbar.notification.row.HybridNotificationView;
import com.android.systemui.statusbar.notification.row.wrapper.NotificationViewWrapper;
@@ -272,6 +273,7 @@
* @param childIndex the index to add it at, if -1 it will be added at the end
*/
public void addNotification(ExpandableNotificationRow row, int childIndex) {
+ ensureRemovedFromTransientContainer(row);
int newIndex = childIndex < 0 ? mAttachedChildren.size() : childIndex;
mAttachedChildren.add(newIndex, row);
addView(row);
@@ -291,6 +293,16 @@
}
}
+ private void ensureRemovedFromTransientContainer(View v) {
+ if (v.getParent() != null && v instanceof ExpandableView) {
+ // If the child is animating away, it will still have a parent, so detach it first
+ // TODO: We should really cancel the active animations here. This will
+ // happen automatically when the view's intro animation starts, but
+ // it's a fragile link.
+ ((ExpandableView) v).removeFromTransientContainerForAdditionTo(this);
+ }
+ }
+
public void removeNotification(ExpandableNotificationRow row) {
int childIndex = mAttachedChildren.indexOf(row);
mAttachedChildren.remove(row);
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationRoundnessManager.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationRoundnessManager.java
index 1cb5e62..464fd06 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationRoundnessManager.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationRoundnessManager.java
@@ -21,8 +21,6 @@
import com.android.systemui.R;
import com.android.systemui.dagger.SysUISingleton;
-import com.android.systemui.flags.FeatureFlags;
-import com.android.systemui.flags.Flags;
import com.android.systemui.statusbar.notification.NotificationSectionsFeatureManager;
import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow;
import com.android.systemui.statusbar.notification.row.ExpandableView;
@@ -41,7 +39,6 @@
private final ExpandableView[] mLastInSectionViews;
private final ExpandableView[] mTmpFirstInSectionViews;
private final ExpandableView[] mTmpLastInSectionViews;
- private final FeatureFlags mFeatureFlags;
private boolean mExpanded;
private HashSet<ExpandableView> mAnimatedChildren;
private Runnable mRoundingChangedCallback;
@@ -56,9 +53,7 @@
@Inject
NotificationRoundnessManager(
- NotificationSectionsFeatureManager sectionsFeatureManager,
- FeatureFlags featureFlags) {
- mFeatureFlags = featureFlags;
+ NotificationSectionsFeatureManager sectionsFeatureManager) {
int numberOfSections = sectionsFeatureManager.getNumberOfBuckets();
mFirstInSectionViews = new ExpandableView[numberOfSections];
mLastInSectionViews = new ExpandableView[numberOfSections];
@@ -125,9 +120,6 @@
ExpandableView viewBefore,
ExpandableView viewSwiped,
ExpandableView viewAfter) {
- if (!mFeatureFlags.isEnabled(Flags.NOTIFICATION_UPDATES)) {
- return;
- }
final boolean animate = true;
ExpandableView oldViewBefore = mViewBeforeSwipedView;
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayout.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayout.java
index 79b05c9..3e9ce25 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayout.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayout.java
@@ -43,7 +43,6 @@
import android.graphics.PointF;
import android.graphics.Rect;
import android.os.Bundle;
-import android.os.SystemProperties;
import android.os.UserHandle;
import android.provider.Settings;
import android.util.AttributeSet;
@@ -80,6 +79,8 @@
import com.android.systemui.ExpandHelper;
import com.android.systemui.R;
import com.android.systemui.animation.Interpolators;
+import com.android.systemui.flags.FeatureFlags;
+import com.android.systemui.flags.Flags;
import com.android.systemui.plugins.statusbar.NotificationSwipeActionHelper;
import com.android.systemui.statusbar.CommandQueue;
import com.android.systemui.statusbar.EmptyShadeView;
@@ -132,14 +133,6 @@
public static final float BACKGROUND_ALPHA_DIMMED = 0.7f;
private static final String TAG = "StackScroller";
- // Usage:
- // adb shell setprop persist.debug.nssl true && adb reboot
- private static final boolean DEBUG = SystemProperties.getBoolean("persist.debug.nssl",
- false /* default */);
- // TODO(b/187291379) disable again before release
- private static final boolean DEBUG_REMOVE_ANIMATION = SystemProperties.getBoolean(
- "persist.debug.nssl.dismiss", false /* default */);
-
// Delay in milli-seconds before shade closes for clear all.
private final int DELAY_BEFORE_SHADE_CLOSE = 200;
private boolean mShadeNeedsToClose = false;
@@ -192,7 +185,12 @@
private float mInitialTouchX;
private float mInitialTouchY;
+ private final boolean mDebugLines;
private Paint mDebugPaint;
+ /** Used to track the Y positions that were already used to draw debug text labels. */
+ private Set<Integer> mDebugTextUsedYPositions;
+ private final boolean mDebugRemoveAnimation;
+
private int mContentHeight;
private int mIntrinsicContentHeight;
private int mCollapsedSize;
@@ -569,6 +567,9 @@
public NotificationStackScrollLayout(Context context, AttributeSet attrs) {
super(context, attrs, 0, 0);
Resources res = getResources();
+ FeatureFlags featureFlags = Dependency.get(FeatureFlags.class);
+ mDebugLines = featureFlags.isEnabled(Flags.NSSL_DEBUG_LINES);
+ mDebugRemoveAnimation = featureFlags.isEnabled(Flags.NSSL_DEBUG_REMOVE_ANIMATION);
mSectionsManager = Dependency.get(NotificationSectionsManager.class);
mScreenOffAnimationController =
Dependency.get(ScreenOffAnimationController.class);
@@ -591,10 +592,10 @@
res.getBoolean(R.bool.config_drawNotificationBackground);
setOutlineProvider(mOutlineProvider);
- boolean willDraw = mShouldDrawNotificationBackground || DEBUG;
+ boolean willDraw = mShouldDrawNotificationBackground || mDebugLines;
setWillNotDraw(!willDraw);
mBackgroundPaint.setAntiAlias(true);
- if (DEBUG) {
+ if (mDebugLines) {
mDebugPaint = new Paint();
mDebugPaint.setColor(0xffff0000);
mDebugPaint.setStrokeWidth(2);
@@ -729,18 +730,17 @@
drawHeadsUpBackground(canvas);
}
- if (DEBUG) {
+ if (mDebugLines) {
onDrawDebug(canvas);
}
}
- /** Used to track the Y positions that were already used to draw debug text labels. */
- private static final Set<Integer> DEBUG_TEXT_USED_Y_POSITIONS =
- DEBUG ? new HashSet<>() : Collections.emptySet();
-
private void onDrawDebug(Canvas canvas) {
- DEBUG_TEXT_USED_Y_POSITIONS.clear();
-
+ if (mDebugTextUsedYPositions == null) {
+ mDebugTextUsedYPositions = new HashSet<>();
+ } else {
+ mDebugTextUsedYPositions.clear();
+ }
int y = mTopPadding;
drawDebugInfo(canvas, y, Color.RED, /* label= */ "mTopPadding");
@@ -776,10 +776,10 @@
private int computeDebugYTextPosition(int lineY) {
int textY = lineY;
- while (DEBUG_TEXT_USED_Y_POSITIONS.contains(textY)) {
+ while (mDebugTextUsedYPositions.contains(textY)) {
textY = (int) (textY + mDebugPaint.getTextSize());
}
- DEBUG_TEXT_USED_Y_POSITIONS.add(textY);
+ mDebugTextUsedYPositions.add(textY);
return textY;
}
@@ -2702,14 +2702,14 @@
@ShadeViewRefactor(RefactorComponent.STATE_RESOLVER)
boolean generateRemoveAnimation(ExpandableView child) {
String key = "";
- if (DEBUG_REMOVE_ANIMATION) {
+ if (mDebugRemoveAnimation) {
if (child instanceof ExpandableNotificationRow) {
key = ((ExpandableNotificationRow) child).getEntry().getKey();
}
Log.d(TAG, "generateRemoveAnimation " + key);
}
if (removeRemovedChildFromHeadsUpChangeAnimations(child)) {
- if (DEBUG_REMOVE_ANIMATION) {
+ if (mDebugRemoveAnimation) {
Log.d(TAG, "removedBecauseOfHeadsUp " + key);
}
mAddedHeadsUpChildren.remove(child);
@@ -2720,7 +2720,7 @@
mClearTransientViewsWhenFinished.add(child);
return true;
}
- if (DEBUG_REMOVE_ANIMATION) {
+ if (mDebugRemoveAnimation) {
Log.d(TAG, "generateRemove " + key
+ "\nmIsExpanded " + mIsExpanded
+ "\nmAnimationsEnabled " + mAnimationsEnabled
@@ -2728,7 +2728,7 @@
}
if (mIsExpanded && mAnimationsEnabled && !isChildInInvisibleGroup(child)) {
if (!mChildrenToAddAnimated.contains(child)) {
- if (DEBUG_REMOVE_ANIMATION) {
+ if (mDebugRemoveAnimation) {
Log.d(TAG, "needsAnimation = true " + key);
}
// Generate Animations
@@ -3227,7 +3227,7 @@
ignoreChildren);
mAnimationEvents.add(event);
mSwipedOutViews.remove(child);
- if (DEBUG_REMOVE_ANIMATION) {
+ if (mDebugRemoveAnimation) {
String key = "";
if (child instanceof ExpandableNotificationRow) {
key = ((ExpandableNotificationRow) child).getEntry().getKey();
@@ -4655,18 +4655,12 @@
}
private void ensureRemovedFromTransientContainer(View v) {
- if (v.getParent() == this && v instanceof ExpandableView) {
- ExpandableView expandableView = (ExpandableView) v;
- ViewGroup transientContainer = expandableView.getTransientContainer();
- // If the child is animating away, it will still have a parent, so
- // detach it first
+ if (v.getParent() != null && v instanceof ExpandableView) {
+ // If the child is animating away, it will still have a parent, so detach it first
// TODO: We should really cancel the active animations here. This will
// happen automatically when the view's intro animation starts, but
// it's a fragile link.
- if (transientContainer != null) {
- transientContainer.removeTransientView(v);
- expandableView.setTransientContainer(null);
- }
+ ((ExpandableView) v).removeFromTransientContainerForAdditionTo(this);
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/BiometricUnlockController.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/BiometricUnlockController.java
index ad1c232..98b5dcc 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/BiometricUnlockController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/BiometricUnlockController.java
@@ -645,7 +645,7 @@
if (biometricSourceType.equals(BiometricSourceType.FINGERPRINT)
&& mUpdateMonitor.isUdfpsSupported()
&& mNumConsecutiveFpFailures >= FP_ATTEMPTS_BEFORE_SHOW_BOUNCER) {
- mKeyguardViewController.showBouncer(true);
+ startWakeAndUnlock(MODE_SHOW_BOUNCER);
UI_EVENT_LOGGER.log(BiometricUiEvent.BIOMETRIC_BOUNCER_SHOWN);
mNumConsecutiveFpFailures = 0;
}
@@ -668,7 +668,8 @@
&& mUpdateMonitor.isUdfpsSupported()
&& (mStatusBarStateController.getState() == StatusBarState.SHADE
|| mStatusBarStateController.getState() == StatusBarState.SHADE_LOCKED)) {
- mKeyguardViewController.showBouncer(true);
+ startWakeAndUnlock(MODE_SHOW_BOUNCER);
+ UI_EVENT_LOGGER.log(BiometricUiEvent.BIOMETRIC_BOUNCER_SHOWN);
}
cleanup();
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardClockPositionAlgorithm.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardClockPositionAlgorithm.java
index 7ca8652..732e5f0 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardClockPositionAlgorithm.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardClockPositionAlgorithm.java
@@ -57,8 +57,7 @@
private int mUserSwitchPreferredY;
/**
- * Minimum top margin to avoid overlap with status bar, lock icon, or multi-user switcher
- * avatar.
+ * Minimum top margin to avoid overlap with status bar or multi-user switcher avatar.
*/
private int mMinTopMargin;
@@ -203,7 +202,7 @@
if (mBypassEnabled) {
return mUnlockedStackScrollerPadding;
} else if (mIsSplitShade) {
- return getClockY(1.0f, mDarkAmount);
+ return getClockY(1.0f, mDarkAmount) + mUserSwitchHeight;
} else {
return getClockY(1.0f, mDarkAmount) + mKeyguardStatusHeight;
}
@@ -213,7 +212,7 @@
if (mBypassEnabled) {
return (int) (mUnlockedStackScrollerPadding + mOverStretchAmount);
} else if (mIsSplitShade) {
- return Math.max(0, clockYPosition - mSplitShadeTopNotificationsMargin);
+ return clockYPosition - mSplitShadeTopNotificationsMargin + mUserSwitchHeight;
} else {
return clockYPosition + mKeyguardStatusHeight;
}
@@ -223,7 +222,7 @@
if (mBypassEnabled) {
return mUnlockedStackScrollerPadding;
} else if (mIsSplitShade) {
- return Math.max(mSplitShadeTargetTopMargin, mMinTopMargin);
+ return mSplitShadeTargetTopMargin + mUserSwitchHeight;
} else {
return mMinTopMargin + mKeyguardStatusHeight;
}
@@ -231,7 +230,7 @@
private int getExpandedPreferredClockY() {
if (mIsSplitShade) {
- return Math.max(mSplitShadeTargetTopMargin, mMinTopMargin);
+ return mSplitShadeTargetTopMargin;
} else {
return mMinTopMargin;
}
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 434671c..9ca904b 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelViewController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelViewController.java
@@ -1440,8 +1440,11 @@
mKeyguardStatusViewController.displayClock(LARGE, shouldAnimateClockChange);
}
updateKeyguardStatusViewAlignment(true /* animate */);
- int userIconHeight = mKeyguardQsUserSwitchController != null
+ int userSwitcherHeight = mKeyguardQsUserSwitchController != null
? mKeyguardQsUserSwitchController.getUserIconHeight() : 0;
+ if (mKeyguardUserSwitcherController != null) {
+ userSwitcherHeight = mKeyguardUserSwitcherController.getHeight();
+ }
float expandedFraction =
mScreenOffAnimationController.shouldExpandNotifications()
? 1.0f : getExpandedFraction();
@@ -1461,7 +1464,7 @@
mStatusBarHeaderHeightKeyguard,
expandedFraction,
mKeyguardStatusViewController.getLockscreenHeight(),
- userIconHeight,
+ userSwitcherHeight,
userSwitcherPreferredY,
darkamount, mOverStretchAmount,
bypassEnabled, getUnlockedStackScrollerPadding(),
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/KeyguardUserSwitcherController.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/KeyguardUserSwitcherController.java
index ffa7963..04a6a11 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/KeyguardUserSwitcherController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/KeyguardUserSwitcherController.java
@@ -248,6 +248,10 @@
return mUserSwitcherController.isSimpleUserSwitcher();
}
+ public int getHeight() {
+ return mListView.getHeight();
+ }
+
/**
* @param animate if the transition should be animated
* @return true if the switcher state changed
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/KeyguardUserSwitcherListView.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/KeyguardUserSwitcherListView.java
index cd8894c..850a4b4 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/KeyguardUserSwitcherListView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/KeyguardUserSwitcherListView.java
@@ -97,9 +97,12 @@
} else {
// Update clickable state immediately so that the menu feels more responsive
userItemViews[i].setClickable(open);
- // Before running the animation, ensure visibility is set correctly
- userItemViews[i].updateVisibilities(animate || open /* showItem */,
- true /* showTextName */, false /* animate */);
+ // when opening we need to make views visible beforehand so they can be animated
+ if (open) {
+ userItemViews[i].updateVisibilities(true /* showItem */,
+ true /* showTextName */, false /* animate */);
+ }
+
}
}
@@ -117,6 +120,13 @@
setClipChildren(true);
setClipToPadding(true);
mAnimating = false;
+ if (!open) {
+ // after closing we hide children so that height of this view is correct
+ for (int i = 1; i < userItemViews.length; i++) {
+ userItemViews[i].updateVisibilities(false /* showItem */,
+ true /* showTextName */, false /* animate */);
+ }
+ }
});
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/RotationLockController.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/RotationLockController.java
index f258fb1..1158324 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/RotationLockController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/RotationLockController.java
@@ -23,6 +23,7 @@
int getRotationLockOrientation();
boolean isRotationLockAffordanceVisible();
boolean isRotationLocked();
+ boolean isCameraRotationEnabled();
void setRotationLocked(boolean locked);
void setRotationLockedAtAngle(boolean locked, int rotation);
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/RotationLockControllerImpl.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/RotationLockControllerImpl.java
index 67f5364..3143a47 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/RotationLockControllerImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/RotationLockControllerImpl.java
@@ -18,6 +18,9 @@
import static com.android.systemui.statusbar.policy.dagger.StatusBarPolicyModule.DEVICE_STATE_ROTATION_LOCK_DEFAULTS;
+import android.Manifest;
+import android.content.Context;
+import android.content.pm.PackageManager;
import android.os.UserHandle;
import androidx.annotation.NonNull;
@@ -34,6 +37,7 @@
/** Platform implementation of the rotation lock controller. **/
@SysUISingleton
public final class RotationLockControllerImpl implements RotationLockController {
+
private final CopyOnWriteArrayList<RotationLockControllerCallback> mCallbacks =
new CopyOnWriteArrayList<>();
@@ -86,11 +90,15 @@
return mRotationPolicy.isRotationLocked();
}
+ public boolean isCameraRotationEnabled() {
+ return mRotationPolicy.isCameraRotationEnabled();
+ }
+
public void setRotationLocked(boolean locked) {
mRotationPolicy.setRotationLock(locked);
}
- public void setRotationLockedAtAngle(boolean locked, int rotation){
+ public void setRotationLockedAtAngle(boolean locked, int rotation) {
mRotationPolicy.setRotationLockAtAngle(locked, rotation);
}
@@ -121,4 +129,11 @@
callback.onRotationLockStateChanged(mRotationPolicy.isRotationLocked(),
mRotationPolicy.isRotationLockToggleVisible());
}
+
+ public static boolean hasSufficientPermission(Context context) {
+ final PackageManager packageManager = context.getPackageManager();
+ final String rotationPackage = packageManager.getRotationResolverPackageName();
+ return rotationPackage != null && packageManager.checkPermission(
+ Manifest.permission.CAMERA, rotationPackage) == PackageManager.PERMISSION_GRANTED;
+ }
}
diff --git a/packages/SystemUI/src/com/android/systemui/util/wrapper/RotationPolicyWrapper.kt b/packages/SystemUI/src/com/android/systemui/util/wrapper/RotationPolicyWrapper.kt
index 2a0cc7d..b64d7be 100644
--- a/packages/SystemUI/src/com/android/systemui/util/wrapper/RotationPolicyWrapper.kt
+++ b/packages/SystemUI/src/com/android/systemui/util/wrapper/RotationPolicyWrapper.kt
@@ -17,8 +17,10 @@
package com.android.systemui.util.wrapper
import android.content.Context
+import android.provider.Settings.Secure.CAMERA_AUTOROTATE
import com.android.internal.view.RotationPolicy
import com.android.internal.view.RotationPolicy.RotationPolicyListener
+import com.android.systemui.util.settings.SecureSettings
import javax.inject.Inject
/**
@@ -30,12 +32,16 @@
fun getRotationLockOrientation(): Int
fun isRotationLockToggleVisible(): Boolean
fun isRotationLocked(): Boolean
+ fun isCameraRotationEnabled(): Boolean
fun registerRotationPolicyListener(listener: RotationPolicyListener, userHandle: Int)
fun unregisterRotationPolicyListener(listener: RotationPolicyListener)
}
-class RotationPolicyWrapperImpl @Inject constructor(private val context: Context) :
- RotationPolicyWrapper {
+class RotationPolicyWrapperImpl @Inject constructor(
+ private val context: Context,
+ private val secureSettings: SecureSettings
+) :
+ RotationPolicyWrapper {
override fun setRotationLock(enabled: Boolean) {
RotationPolicy.setRotationLock(context, enabled)
@@ -54,6 +60,9 @@
override fun isRotationLocked(): Boolean =
RotationPolicy.isRotationLocked(context)
+ override fun isCameraRotationEnabled(): Boolean =
+ secureSettings.getInt(CAMERA_AUTOROTATE, 0) == 1
+
override fun registerRotationPolicyListener(
listener: RotationPolicyListener,
userHandle: Int
diff --git a/packages/SystemUI/tests/src/com/android/systemui/media/taptotransfer/common/MediaTttChipControllerCommonTest.kt b/packages/SystemUI/tests/src/com/android/systemui/media/taptotransfer/common/MediaTttChipControllerCommonTest.kt
index b74ba26..927ca7a 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/media/taptotransfer/common/MediaTttChipControllerCommonTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/media/taptotransfer/common/MediaTttChipControllerCommonTest.kt
@@ -19,14 +19,18 @@
import android.content.Context
import android.graphics.drawable.Drawable
import android.graphics.drawable.Icon
+import android.view.View
import android.view.ViewGroup
import android.view.WindowManager
+import android.widget.ImageView
import androidx.test.filters.SmallTest
import com.android.systemui.R
import com.android.systemui.SysuiTestCase
import com.android.systemui.util.mockito.any
+import com.google.common.truth.Truth.assertThat
import org.junit.Before
import org.junit.Test
+import org.mockito.ArgumentCaptor
import org.mockito.Mock
import org.mockito.Mockito.never
import org.mockito.Mockito.reset
@@ -50,24 +54,24 @@
@Test
fun displayChip_chipAdded() {
- controllerCommon.displayChip(MediaTttChipState(appIconDrawable))
+ controllerCommon.displayChip(getState())
verify(windowManager).addView(any(), any())
}
@Test
fun displayChip_twice_chipNotAddedTwice() {
- controllerCommon.displayChip(MediaTttChipState(appIconDrawable))
+ controllerCommon.displayChip(getState())
reset(windowManager)
- controllerCommon.displayChip(MediaTttChipState(appIconDrawable))
+ controllerCommon.displayChip(getState())
verify(windowManager, never()).addView(any(), any())
}
@Test
fun removeChip_chipRemoved() {
// First, add the chip
- controllerCommon.displayChip(MediaTttChipState(appIconDrawable))
+ controllerCommon.displayChip(getState())
// Then, remove it
controllerCommon.removeChip()
@@ -82,6 +86,29 @@
verify(windowManager, never()).removeView(any())
}
+ @Test
+ fun setIcon_viewHasIconAndContentDescription() {
+ controllerCommon.displayChip(getState())
+ val chipView = getChipView()
+ val drawable = Icon.createWithResource(context, R.drawable.ic_cake).loadDrawable(context)
+ val contentDescription = "test description"
+
+ controllerCommon.setIcon(MediaTttChipState(drawable, contentDescription), chipView)
+
+ assertThat(chipView.getAppIconView().drawable).isEqualTo(drawable)
+ assertThat(chipView.getAppIconView().contentDescription).isEqualTo(contentDescription)
+ }
+
+ private fun getState() = MediaTttChipState(appIconDrawable, APP_ICON_CONTENT_DESCRIPTION)
+
+ private fun getChipView(): ViewGroup {
+ val viewCaptor = ArgumentCaptor.forClass(View::class.java)
+ verify(windowManager).addView(viewCaptor.capture(), any())
+ return viewCaptor.value as ViewGroup
+ }
+
+ private fun ViewGroup.getAppIconView() = this.requireViewById<ImageView>(R.id.app_icon)
+
inner class TestControllerCommon(
context: Context,
windowManager: WindowManager
@@ -92,3 +119,5 @@
}
}
}
+
+private const val APP_ICON_CONTENT_DESCRIPTION = "Content description"
diff --git a/packages/SystemUI/tests/src/com/android/systemui/media/taptotransfer/receiver/MediaTttChipControllerReceiverTest.kt b/packages/SystemUI/tests/src/com/android/systemui/media/taptotransfer/receiver/MediaTttChipControllerReceiverTest.kt
index 2ff472f..afaab80 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/media/taptotransfer/receiver/MediaTttChipControllerReceiverTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/media/taptotransfer/receiver/MediaTttChipControllerReceiverTest.kt
@@ -16,7 +16,6 @@
package com.android.systemui.media.taptotransfer.receiver
-import android.graphics.drawable.Drawable
import android.graphics.drawable.Icon
import android.view.View
import android.view.ViewGroup
@@ -50,10 +49,12 @@
@Test
fun displayChip_chipContainsIcon() {
val drawable = Icon.createWithResource(context, R.drawable.ic_cake).loadDrawable(context)
+ val contentDescription = "Test description"
- controllerReceiver.displayChip(ChipStateReceiver(drawable))
+ controllerReceiver.displayChip(ChipStateReceiver(drawable, contentDescription))
- assertThat(getChipView().getAppIconDrawable()).isEqualTo(drawable)
+ assertThat(getChipView().getAppIconView().drawable).isEqualTo(drawable)
+ assertThat(getChipView().getAppIconView().contentDescription).isEqualTo(contentDescription)
}
private fun getChipView(): ViewGroup {
@@ -62,6 +63,5 @@
return viewCaptor.value as ViewGroup
}
- private fun ViewGroup.getAppIconDrawable(): Drawable =
- (this.requireViewById<ImageView>(R.id.app_icon)).drawable
+ private fun ViewGroup.getAppIconView() = this.requireViewById<ImageView>(R.id.app_icon)
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/media/taptotransfer/sender/MediaTttChipControllerSenderTest.kt b/packages/SystemUI/tests/src/com/android/systemui/media/taptotransfer/sender/MediaTttChipControllerSenderTest.kt
index 028ec55..caef5b9 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/media/taptotransfer/sender/MediaTttChipControllerSenderTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/media/taptotransfer/sender/MediaTttChipControllerSenderTest.kt
@@ -70,7 +70,8 @@
controllerSender.displayChip(moveCloserToTransfer())
val chipView = getChipView()
- assertThat(chipView.getAppIconDrawable()).isEqualTo(appIconDrawable)
+ assertThat(chipView.getAppIconView().drawable).isEqualTo(appIconDrawable)
+ assertThat(chipView.getAppIconView().contentDescription).isEqualTo(APP_ICON_CONTENT_DESC)
assertThat(chipView.getChipText()).contains(DEVICE_NAME)
assertThat(chipView.getLoadingIconVisibility()).isEqualTo(View.GONE)
assertThat(chipView.getUndoButton().visibility).isEqualTo(View.GONE)
@@ -85,7 +86,8 @@
// Assert we're still in the loading state
val chipView = getChipView()
- assertThat(chipView.getAppIconDrawable()).isEqualTo(appIconDrawable)
+ assertThat(chipView.getAppIconView().drawable).isEqualTo(appIconDrawable)
+ assertThat(chipView.getAppIconView().contentDescription).isEqualTo(APP_ICON_CONTENT_DESC)
assertThat(chipView.getChipText()).contains(DEVICE_NAME)
assertThat(chipView.getLoadingIconVisibility()).isEqualTo(View.VISIBLE)
assertThat(chipView.getUndoButton().visibility).isEqualTo(View.GONE)
@@ -155,7 +157,8 @@
controllerSender.displayChip(transferSucceeded())
val chipView = getChipView()
- assertThat(chipView.getAppIconDrawable()).isEqualTo(appIconDrawable)
+ assertThat(chipView.getAppIconView().drawable).isEqualTo(appIconDrawable)
+ assertThat(chipView.getAppIconView().contentDescription).isEqualTo(APP_ICON_CONTENT_DESC)
assertThat(chipView.getChipText()).contains(DEVICE_NAME)
assertThat(chipView.getLoadingIconVisibility()).isEqualTo(View.GONE)
}
@@ -220,8 +223,7 @@
assertThat(getChipView().getUndoButton().visibility).isEqualTo(View.GONE)
}
- private fun LinearLayout.getAppIconDrawable(): Drawable =
- (this.requireViewById<ImageView>(R.id.app_icon)).drawable
+ private fun LinearLayout.getAppIconView() = this.requireViewById<ImageView>(R.id.app_icon)
private fun LinearLayout.getChipText(): String =
(this.requireViewById<TextView>(R.id.text)).text as String
@@ -238,20 +240,22 @@
}
/** Helper method providing default parameters to not clutter up the tests. */
- private fun moveCloserToTransfer() = MoveCloserToTransfer(appIconDrawable, DEVICE_NAME)
+ private fun moveCloserToTransfer() =
+ MoveCloserToTransfer(appIconDrawable, APP_ICON_CONTENT_DESC, DEVICE_NAME)
/** Helper method providing default parameters to not clutter up the tests. */
private fun transferInitiated(
future: Future<Runnable?> = TEST_FUTURE
- ) = TransferInitiated(appIconDrawable, DEVICE_NAME, future)
+ ) = TransferInitiated(appIconDrawable, APP_ICON_CONTENT_DESC, DEVICE_NAME, future)
/** Helper method providing default parameters to not clutter up the tests. */
private fun transferSucceeded(
undoRunnable: Runnable? = null
- ) = TransferSucceeded(appIconDrawable, DEVICE_NAME, undoRunnable)
+ ) = TransferSucceeded(appIconDrawable, APP_ICON_CONTENT_DESC, DEVICE_NAME, undoRunnable)
}
private const val DEVICE_NAME = "My Tablet"
+private const val APP_ICON_CONTENT_DESC = "Content description"
// Use a settable future that hasn't yet been set so that we don't immediately switch to the success
// state.
private val TEST_FUTURE: SettableFuture<Runnable?> = SettableFuture.create()
diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/RotationLockTileTest.java b/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/RotationLockTileTest.java
new file mode 100644
index 0000000..55c51b2
--- /dev/null
+++ b/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/RotationLockTileTest.java
@@ -0,0 +1,212 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.qs.tiles;
+
+import static android.hardware.SensorPrivacyManager.Sensors.CAMERA;
+
+import static junit.framework.TestCase.assertEquals;
+
+import static org.mockito.Mockito.doReturn;
+import static org.mockito.Mockito.when;
+
+import android.Manifest;
+import android.content.pm.PackageManager;
+import android.hardware.SensorPrivacyManager;
+import android.os.Handler;
+import android.test.suitebuilder.annotation.SmallTest;
+import android.testing.AndroidTestingRunner;
+import android.testing.TestableLooper;
+
+import com.android.internal.logging.MetricsLogger;
+import com.android.systemui.R;
+import com.android.systemui.SysuiTestCase;
+import com.android.systemui.classifier.FalsingManagerFake;
+import com.android.systemui.plugins.ActivityStarter;
+import com.android.systemui.plugins.statusbar.StatusBarStateController;
+import com.android.systemui.qs.QSTileHost;
+import com.android.systemui.qs.logging.QSLogger;
+import com.android.systemui.statusbar.policy.BatteryController;
+import com.android.systemui.statusbar.policy.DeviceStateRotationLockSettingController;
+import com.android.systemui.statusbar.policy.RotationLockController;
+import com.android.systemui.statusbar.policy.RotationLockControllerImpl;
+import com.android.systemui.util.settings.FakeSettings;
+import com.android.systemui.util.wrapper.RotationPolicyWrapper;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.Mock;
+import org.mockito.MockitoAnnotations;
+
+
+@RunWith(AndroidTestingRunner.class)
+@TestableLooper.RunWithLooper(setAsMainLooper = true)
+@SmallTest
+public class RotationLockTileTest extends SysuiTestCase {
+
+ private static final String PACKAGE_NAME = "package_name";
+ private static final String[] DEFAULT_SETTINGS = new String[]{
+ "0:0",
+ "1:2"
+ };
+
+ @Mock
+ private PackageManager mPackageManager;
+ @Mock
+ private ActivityStarter mActivityStarter;
+ @Mock
+ private QSTileHost mHost;
+ @Mock
+ private MetricsLogger mMetricsLogger;
+ @Mock
+ private StatusBarStateController mStatusBarStateController;
+ @Mock
+ private QSLogger mQSLogger;
+ @Mock
+ private SensorPrivacyManager mPrivacyManager;
+ @Mock
+ private BatteryController mBatteryController;
+ @Mock
+ DeviceStateRotationLockSettingController mDeviceStateRotationLockSettingController;
+ @Mock
+ RotationPolicyWrapper mRotationPolicyWrapper;
+
+ private RotationLockController mController;
+ private TestableLooper mTestableLooper;
+ private RotationLockTile mLockTile;
+
+ @Before
+ public void setUp() throws Exception {
+ MockitoAnnotations.initMocks(this);
+ mTestableLooper = TestableLooper.get(this);
+
+ when(mHost.getContext()).thenReturn(mContext);
+ when(mHost.getUserContext()).thenReturn(mContext);
+
+ mController = new RotationLockControllerImpl(mRotationPolicyWrapper,
+ mDeviceStateRotationLockSettingController, DEFAULT_SETTINGS);
+
+ mLockTile = new RotationLockTile(
+ mHost,
+ mTestableLooper.getLooper(),
+ new Handler(mTestableLooper.getLooper()),
+ new FalsingManagerFake(),
+ mMetricsLogger,
+ mStatusBarStateController,
+ mActivityStarter,
+ mQSLogger,
+ mController,
+ mPrivacyManager,
+ mBatteryController,
+ new FakeSettings()
+ );
+
+ mLockTile.initialize();
+
+ // We are not setting the mocks to listening, so we trigger a first refresh state to
+ // set the initial state
+ mLockTile.refreshState();
+
+ mTestableLooper.processAllMessages();
+
+ mContext.setMockPackageManager(mPackageManager);
+ doReturn(PACKAGE_NAME).when(mPackageManager).getRotationResolverPackageName();
+ doReturn(PackageManager.PERMISSION_GRANTED).when(mPackageManager).checkPermission(
+ Manifest.permission.CAMERA, PACKAGE_NAME);
+
+ when(mBatteryController.isPowerSave()).thenReturn(false);
+ when(mPrivacyManager.isSensorPrivacyEnabled(CAMERA)).thenReturn(false);
+ enableAutoRotation();
+ enableCameraBasedRotation();
+
+ mLockTile.refreshState();
+ mTestableLooper.processAllMessages();
+ }
+
+ @Test
+ public void testSecondaryString_cameraRotateOn_returnsFaceBased() {
+ assertEquals(mContext.getString(R.string.rotation_lock_camera_rotation_on),
+ mLockTile.getState().secondaryLabel.toString());
+ }
+
+ @Test
+ public void testSecondaryString_rotateOff_isEmpty() {
+ disableAutoRotation();
+
+ mLockTile.refreshState();
+ mTestableLooper.processAllMessages();
+
+ assertEquals("", mLockTile.getState().secondaryLabel.toString());
+ }
+
+ @Test
+ public void testSecondaryString_cameraRotateOff_isEmpty() {
+ disableCameraBasedRotation();
+
+ mLockTile.refreshState();
+ mTestableLooper.processAllMessages();
+
+ assertEquals("", mLockTile.getState().secondaryLabel.toString());
+ }
+
+ @Test
+ public void testSecondaryString_powerSaveEnabled_isEmpty() {
+ when(mBatteryController.isPowerSave()).thenReturn(true);
+
+ mLockTile.refreshState();
+ mTestableLooper.processAllMessages();
+
+ assertEquals("", mLockTile.getState().secondaryLabel.toString());
+ }
+
+ @Test
+ public void testSecondaryString_cameraDisabled_isEmpty() {
+ when(mPrivacyManager.isSensorPrivacyEnabled(CAMERA)).thenReturn(true);
+
+ mLockTile.refreshState();
+ mTestableLooper.processAllMessages();
+
+ assertEquals("", mLockTile.getState().secondaryLabel.toString());
+ }
+
+ @Test
+ public void testSecondaryString_noCameraPermission_isEmpty() {
+ doReturn(PackageManager.PERMISSION_DENIED).when(mPackageManager).checkPermission(
+ Manifest.permission.CAMERA, PACKAGE_NAME);
+
+ mLockTile.refreshState();
+ mTestableLooper.processAllMessages();
+
+ assertEquals("", mLockTile.getState().secondaryLabel.toString());
+ }
+
+ private void enableAutoRotation() {
+ when(mRotationPolicyWrapper.isRotationLocked()).thenReturn(false);
+ }
+
+ private void disableAutoRotation() {
+ when(mRotationPolicyWrapper.isRotationLocked()).thenReturn(true);
+ }
+
+ private void enableCameraBasedRotation() {
+ when(mRotationPolicyWrapper.isCameraRotationEnabled()).thenReturn(true);
+ }
+
+ private void disableCameraBasedRotation() {
+ when(mRotationPolicyWrapper.isCameraRotationEnabled()).thenReturn(false);
+ }
+}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/coordinator/MediaCoordinatorTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/coordinator/MediaCoordinatorTest.java
index c5dc2b4..3ddff49 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/coordinator/MediaCoordinatorTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/coordinator/MediaCoordinatorTest.java
@@ -120,7 +120,7 @@
private NotifFilter captureFilter(MediaCoordinator coordinator) {
ArgumentCaptor<NotifFilter> filterCaptor = ArgumentCaptor.forClass(NotifFilter.class);
coordinator.attach(mNotifPipeline);
- verify(mNotifPipeline).addFinalizeFilter(filterCaptor.capture());
+ verify(mNotifPipeline).addPreGroupFilter(filterCaptor.capture());
return filterCaptor.getValue();
}
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/NotificationRoundnessManagerTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/NotificationRoundnessManagerTest.java
index 3c84c01..d3c1dc9 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/NotificationRoundnessManagerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/NotificationRoundnessManagerTest.java
@@ -32,7 +32,6 @@
import com.android.systemui.R;
import com.android.systemui.SysuiTestCase;
-import com.android.systemui.flags.FeatureFlags;
import com.android.systemui.statusbar.notification.NotificationSectionsFeatureManager;
import com.android.systemui.statusbar.notification.collection.NotificationEntry;
import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow;
@@ -44,7 +43,6 @@
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
-import org.mockito.Mock;
import org.mockito.MockitoAnnotations;
import java.util.HashSet;
@@ -59,8 +57,6 @@
private Runnable mRoundnessCallback = mock(Runnable.class);
private ExpandableNotificationRow mFirst;
private ExpandableNotificationRow mSecond;
- @Mock
- private FeatureFlags mFeatureFlags;
private float mSmallRadiusRatio;
@Before
@@ -70,8 +66,7 @@
mSmallRadiusRatio = resources.getDimension(R.dimen.notification_corner_radius_small)
/ resources.getDimension(R.dimen.notification_corner_radius);
mRoundnessManager = new NotificationRoundnessManager(
- new NotificationSectionsFeatureManager(new DeviceConfigProxy(), mContext),
- mFeatureFlags);
+ new NotificationSectionsFeatureManager(new DeviceConfigProxy(), mContext));
allowTestableLooperAsMainThread();
NotificationTestHelper testHelper = new NotificationTestHelper(
mContext,
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/KeyguardClockPositionAlgorithmTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/KeyguardClockPositionAlgorithmTest.java
index 1182695..1827c7f 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/KeyguardClockPositionAlgorithmTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/KeyguardClockPositionAlgorithmTest.java
@@ -62,6 +62,7 @@
private float mPanelExpansion;
private int mKeyguardStatusBarHeaderHeight;
private int mKeyguardStatusHeight;
+ private int mUserSwitchHeight;
private float mDark;
private float mQsExpansion;
private int mCutoutTopInset = 0;
@@ -264,8 +265,7 @@
@Test
public void clockPositionedDependingOnMarginInSplitShade() {
- when(mResources.getDimensionPixelSize(R.dimen.keyguard_split_shade_top_margin))
- .thenReturn(400);
+ setSplitShadeTopMargin(400);
mClockPositionAlgorithm.loadDimens(mResources);
givenLockScreen();
mIsSplitShade = true;
@@ -291,6 +291,32 @@
}
@Test
+ public void notifPaddingAccountsForMultiUserSwitcherInSplitShade() {
+ setSplitShadeTopMargin(100);
+ mUserSwitchHeight = 150;
+ mClockPositionAlgorithm.loadDimens(mResources);
+ givenLockScreen();
+ mIsSplitShade = true;
+ // WHEN the position algorithm is run
+ positionClock();
+ // THEN the notif padding is split shade top margin + user switch height
+ assertThat(mClockPosition.stackScrollerPadding).isEqualTo(250);
+ }
+
+ @Test
+ public void clockDoesntAccountForMultiUserSwitcherInSplitShade() {
+ setSplitShadeTopMargin(100);
+ mUserSwitchHeight = 150;
+ mClockPositionAlgorithm.loadDimens(mResources);
+ givenLockScreen();
+ mIsSplitShade = true;
+ // WHEN the position algorithm is run
+ positionClock();
+ // THEN clockY = split shade top margin
+ assertThat(mClockPosition.clockY).isEqualTo(100);
+ }
+
+ @Test
public void notifPaddingExpandedAlignedWithClockInSplitShadeMode() {
givenLockScreen();
mIsSplitShade = true;
@@ -495,6 +521,11 @@
assertThat(mClockPosition.clockY).isEqualTo(mCutoutTopInset);
}
+ private void setSplitShadeTopMargin(int value) {
+ when(mResources.getDimensionPixelSize(R.dimen.keyguard_split_shade_top_margin))
+ .thenReturn(value);
+ }
+
private void givenHighestBurnInOffset() {
when(BurnInHelperKt.getBurnInOffset(anyInt(), anyBoolean())).then(returnsFirstArg());
}
@@ -529,7 +560,7 @@
mKeyguardStatusBarHeaderHeight,
mPanelExpansion,
mKeyguardStatusHeight,
- 0 /* userSwitchHeight */,
+ mUserSwitchHeight,
0 /* userSwitchPreferredY */,
mDark,
ZERO_DRAG,
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/DeviceStateRotationLockSettingControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/DeviceStateRotationLockSettingControllerTest.java
index 30717f4..db7b2f2 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/DeviceStateRotationLockSettingControllerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/DeviceStateRotationLockSettingControllerTest.java
@@ -225,6 +225,11 @@
}
@Override
+ public boolean isCameraRotationEnabled() {
+ throw new AssertionError("Not implemented");
+ }
+
+ @Override
public void registerRotationPolicyListener(RotationPolicy.RotationPolicyListener listener,
int userHandle) {
throw new AssertionError("Not implemented");
diff --git a/packages/SystemUI/tests/src/com/android/systemui/utils/leaks/FakeRotationLockController.java b/packages/SystemUI/tests/src/com/android/systemui/utils/leaks/FakeRotationLockController.java
index be11024..4f9cb35 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/utils/leaks/FakeRotationLockController.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/utils/leaks/FakeRotationLockController.java
@@ -51,6 +51,11 @@
}
@Override
+ public boolean isCameraRotationEnabled() {
+ return false;
+ }
+
+ @Override
public void setRotationLockedAtAngle(boolean locked, int rotation) {
}
diff --git a/services/accessibility/java/com/android/server/accessibility/AccessibilityManagerService.java b/services/accessibility/java/com/android/server/accessibility/AccessibilityManagerService.java
index 52a6dc1..e59a3d6 100644
--- a/services/accessibility/java/com/android/server/accessibility/AccessibilityManagerService.java
+++ b/services/accessibility/java/com/android/server/accessibility/AccessibilityManagerService.java
@@ -193,6 +193,8 @@
private static final int OWN_PROCESS_ID = android.os.Process.myPid();
+ public static final int INVALID_SERVICE_ID = -1;
+
// Each service has an ID. Also provide one for magnification gesture handling
public static final int MAGNIFICATION_GESTURE_HANDLER_ID = 0;
diff --git a/services/accessibility/java/com/android/server/accessibility/magnification/FullScreenMagnificationController.java b/services/accessibility/java/com/android/server/accessibility/magnification/FullScreenMagnificationController.java
index 38615fe..72bc850 100644
--- a/services/accessibility/java/com/android/server/accessibility/magnification/FullScreenMagnificationController.java
+++ b/services/accessibility/java/com/android/server/accessibility/magnification/FullScreenMagnificationController.java
@@ -19,6 +19,8 @@
import static android.accessibilityservice.AccessibilityTrace.FLAGS_WINDOW_MANAGER_INTERNAL;
import static android.view.accessibility.MagnificationAnimationCallback.STUB_ANIMATION_CALLBACK;
+import static com.android.server.accessibility.AccessibilityManagerService.INVALID_SERVICE_ID;
+
import android.animation.Animator;
import android.animation.ValueAnimator;
import android.annotation.NonNull;
@@ -117,8 +119,7 @@
private final int mDisplayId;
- private static final int INVALID_ID = -1;
- private int mIdOfLastServiceToMagnify = INVALID_ID;
+ private int mIdOfLastServiceToMagnify = INVALID_SERVICE_ID;
private boolean mMagnificationActivated = false;
DisplayMagnification(int displayId) {
@@ -425,7 +426,7 @@
}
final float scale = getScale();
- offsetMagnifiedRegion(scrollX * scale, scrollY * scale, INVALID_ID);
+ offsetMagnifiedRegion(scrollX * scale, scrollY * scale, INVALID_SERVICE_ID);
}
}
@@ -472,7 +473,7 @@
spec.clear();
onMagnificationChangedLocked();
}
- mIdOfLastServiceToMagnify = INVALID_ID;
+ mIdOfLastServiceToMagnify = INVALID_SERVICE_ID;
mForceShowMagnifiableBounds = false;
sendSpecToAnimation(spec, animationCallback);
return changed;
@@ -519,7 +520,7 @@
}
final boolean changed = updateMagnificationSpecLocked(scale, centerX, centerY);
sendSpecToAnimation(mCurrentMagnificationSpec, animationCallback);
- if (isMagnifying() && (id != INVALID_ID)) {
+ if (isMagnifying() && (id != INVALID_SERVICE_ID)) {
mIdOfLastServiceToMagnify = id;
mMagnificationInfoChangedCallback.onRequestMagnificationSpec(mDisplayId,
mIdOfLastServiceToMagnify);
@@ -583,7 +584,7 @@
if (updateCurrentSpecWithOffsetsLocked(nonNormOffsetX, nonNormOffsetY)) {
onMagnificationChangedLocked();
}
- if (id != INVALID_ID) {
+ if (id != INVALID_SERVICE_ID) {
mIdOfLastServiceToMagnify = id;
}
sendSpecToAnimation(mCurrentMagnificationSpec, null);
diff --git a/services/accessibility/java/com/android/server/accessibility/magnification/MagnificationController.java b/services/accessibility/java/com/android/server/accessibility/magnification/MagnificationController.java
index 791da69..037dc1f 100644
--- a/services/accessibility/java/com/android/server/accessibility/magnification/MagnificationController.java
+++ b/services/accessibility/java/com/android/server/accessibility/magnification/MagnificationController.java
@@ -22,6 +22,8 @@
import static android.provider.Settings.Secure.ACCESSIBILITY_MAGNIFICATION_MODE_WINDOW;
import static android.view.accessibility.MagnificationAnimationCallback.STUB_ANIMATION_CALLBACK;
+import static com.android.server.accessibility.AccessibilityManagerService.MAGNIFICATION_GESTURE_HANDLER_ID;
+
import android.accessibilityservice.MagnificationConfig;
import android.annotation.NonNull;
import android.annotation.Nullable;
@@ -240,9 +242,10 @@
* @param config The targeting magnification config
* @param animate {@code true} to animate the transition, {@code false}
* to transition immediately
+ * @param id The ID of the service requesting the change
*/
public void transitionMagnificationConfigMode(int displayId, MagnificationConfig config,
- boolean animate) {
+ boolean animate, int id) {
synchronized (mLock) {
final int targetMode = config.getMode();
final PointF currentBoundsCenter = getCurrentMagnificationBoundsCenterLocked(displayId,
@@ -273,7 +276,7 @@
screenMagnificationController.reset(displayId, false);
windowMagnificationMgr.enableWindowMagnification(displayId,
scale, magnificationCenter.x, magnificationCenter.y,
- animate ? STUB_ANIMATION_CALLBACK : null);
+ animate ? STUB_ANIMATION_CALLBACK : null, id);
} else if (targetMode == ACCESSIBILITY_MAGNIFICATION_MODE_FULLSCREEN) {
windowMagnificationMgr.disableWindowMagnification(displayId, false, null);
if (!screenMagnificationController.isRegistered(displayId)) {
@@ -281,7 +284,7 @@
}
screenMagnificationController.setScaleAndCenter(displayId, scale,
magnificationCenter.x, magnificationCenter.y, animate,
- AccessibilityManagerService.MAGNIFICATION_GESTURE_HANDLER_ID);
+ id);
}
}
}
@@ -337,7 +340,7 @@
public void onRequestMagnificationSpec(int displayId, int serviceId) {
final WindowMagnificationManager windowMagnificationManager;
synchronized (mLock) {
- if (serviceId == AccessibilityManagerService.MAGNIFICATION_GESTURE_HANDLER_ID) {
+ if (serviceId == MAGNIFICATION_GESTURE_HANDLER_ID) {
return;
}
updateMagnificationButton(displayId, ACCESSIBILITY_MAGNIFICATION_MODE_FULLSCREEN);
@@ -575,7 +578,7 @@
synchronized (mLock) {
if (mWindowMagnificationMgr == null) {
mWindowMagnificationMgr = new WindowMagnificationManager(mContext,
- mUserId, this, mAms.getTraceManager(),
+ mLock, this, mAms.getTraceManager(),
mScaleProvider);
}
return mWindowMagnificationMgr;
@@ -718,11 +721,12 @@
}
fullScreenMagnificationController.setScaleAndCenter(mDisplayId, mCurrentScale,
mCurrentCenter.x, mCurrentCenter.y, mAnimate,
- AccessibilityManagerService.MAGNIFICATION_GESTURE_HANDLER_ID);
+ MAGNIFICATION_GESTURE_HANDLER_ID);
} else {
getWindowMagnificationMgr().enableWindowMagnification(mDisplayId,
mCurrentScale, mCurrentCenter.x,
- mCurrentCenter.y, mAnimate ? STUB_ANIMATION_CALLBACK : null);
+ mCurrentCenter.y, mAnimate ? STUB_ANIMATION_CALLBACK : null,
+ MAGNIFICATION_GESTURE_HANDLER_ID);
}
}
}
diff --git a/services/accessibility/java/com/android/server/accessibility/magnification/MagnificationProcessor.java b/services/accessibility/java/com/android/server/accessibility/magnification/MagnificationProcessor.java
index 7a525ee..40f77b0 100644
--- a/services/accessibility/java/com/android/server/accessibility/magnification/MagnificationProcessor.java
+++ b/services/accessibility/java/com/android/server/accessibility/magnification/MagnificationProcessor.java
@@ -99,7 +99,7 @@
*/
public boolean setMagnificationConfig(int displayId, @NonNull MagnificationConfig config,
boolean animate, int id) {
- if (transitionModeIfNeeded(displayId, config, animate)) {
+ if (transitionModeIfNeeded(displayId, config, animate, id)) {
return true;
}
@@ -114,7 +114,8 @@
} else if (configMode == MAGNIFICATION_MODE_WINDOW) {
return mController.getWindowMagnificationMgr().enableWindowMagnification(displayId,
config.getScale(), config.getCenterX(), config.getCenterY(),
- animate ? STUB_ANIMATION_CALLBACK : null);
+ animate ? STUB_ANIMATION_CALLBACK : null,
+ id);
}
return false;
}
@@ -136,13 +137,13 @@
* mode when the controlling mode is unchanged or the controlling magnifier is not activated.
*/
private boolean transitionModeIfNeeded(int displayId, MagnificationConfig config,
- boolean animate) {
+ boolean animate, int id) {
int currentMode = getControllingMode(displayId);
if (currentMode == config.getMode()
|| !mController.hasDisableMagnificationCallback(displayId)) {
return false;
}
- mController.transitionMagnificationConfigMode(displayId, config, animate);
+ mController.transitionMagnificationConfigMode(displayId, config, animate, id);
return true;
}
@@ -237,7 +238,8 @@
if (mode == MAGNIFICATION_MODE_FULLSCREEN) {
return mController.getFullScreenMagnificationController().reset(displayId, animate);
} else if (mode == MAGNIFICATION_MODE_WINDOW) {
- return mController.getWindowMagnificationMgr().reset(displayId);
+ return mController.getWindowMagnificationMgr().disableWindowMagnification(displayId,
+ false, animate ? STUB_ANIMATION_CALLBACK : null);
}
return false;
}
@@ -256,11 +258,15 @@
}
/**
- * {@link FullScreenMagnificationController#resetIfNeeded(int, boolean)}
+ * Resets all the magnifiers on all the displays.
+ * Called when the a11y service connection that has changed the current magnification spec is
+ * unbound or the binder died.
+ *
+ * @param connectionId The connection id
*/
- // TODO: support window magnification
public void resetAllIfNeeded(int connectionId) {
mController.getFullScreenMagnificationController().resetAllIfNeeded(connectionId);
+ mController.getWindowMagnificationMgr().resetAllIfNeeded(connectionId);
}
/**
diff --git a/services/accessibility/java/com/android/server/accessibility/magnification/WindowMagnificationManager.java b/services/accessibility/java/com/android/server/accessibility/magnification/WindowMagnificationManager.java
index 3f6ff25..c4a577d 100644
--- a/services/accessibility/java/com/android/server/accessibility/magnification/WindowMagnificationManager.java
+++ b/services/accessibility/java/com/android/server/accessibility/magnification/WindowMagnificationManager.java
@@ -20,6 +20,9 @@
import static android.accessibilityservice.AccessibilityTrace.FLAGS_WINDOW_MAGNIFICATION_CONNECTION_CALLBACK;
import static android.view.accessibility.MagnificationAnimationCallback.STUB_ANIMATION_CALLBACK;
+import static com.android.server.accessibility.AccessibilityManagerService.INVALID_SERVICE_ID;
+import static com.android.server.accessibility.AccessibilityManagerService.MAGNIFICATION_GESTURE_HANDLER_ID;
+
import android.annotation.IntDef;
import android.annotation.NonNull;
import android.annotation.Nullable;
@@ -84,7 +87,7 @@
})
public @interface WindowPosition {}
- private final Object mLock = new Object();
+ private final Object mLock;
private final Context mContext;
@VisibleForTesting
@GuardedBy("mLock")
@@ -149,9 +152,10 @@
private final AccessibilityTraceManager mTrace;
private final MagnificationScaleProvider mScaleProvider;
- public WindowMagnificationManager(Context context, int userId, @NonNull Callback callback,
+ public WindowMagnificationManager(Context context, Object lock, @NonNull Callback callback,
AccessibilityTraceManager trace, MagnificationScaleProvider scaleProvider) {
mContext = context;
+ mLock = lock;
mCallback = callback;
mTrace = trace;
mScaleProvider = scaleProvider;
@@ -253,7 +257,26 @@
}
mWindowMagnifiers.clear();
}
+ }
+ /**
+ * Resets the window magnifier on all displays that had been controlled by the
+ * specified service connection. Called when the service connection is unbound
+ * or binder died.
+ *
+ * @param connectionId The connection id
+ */
+ public void resetAllIfNeeded(int connectionId) {
+ synchronized (mLock) {
+ for (int i = 0; i < mWindowMagnifiers.size(); i++) {
+ final WindowMagnifier magnifier = mWindowMagnifiers.valueAt(i);
+ if (magnifier != null
+ && magnifier.mEnabled
+ && connectionId == magnifier.getIdOfLastServiceToControl()) {
+ magnifier.disableWindowMagnificationInternal(null);
+ }
+ }
+ }
}
private void resetWindowMagnifiers() {
@@ -310,7 +333,7 @@
public boolean enableWindowMagnification(int displayId, float scale, float centerX,
float centerY) {
return enableWindowMagnification(displayId, scale, centerX, centerY,
- STUB_ANIMATION_CALLBACK);
+ STUB_ANIMATION_CALLBACK, MAGNIFICATION_GESTURE_HANDLER_ID);
}
/**
@@ -324,12 +347,13 @@
* @param centerY The screen-relative Y coordinate around which to center for magnification,
* or {@link Float#NaN} to leave unchanged.
* @param animationCallback Called when the animation result is valid.
+ * @param id The connection ID
* @return {@code true} if the magnification is enabled successfully.
*/
public boolean enableWindowMagnification(int displayId, float scale, float centerX,
- float centerY, @Nullable MagnificationAnimationCallback animationCallback) {
+ float centerY, @Nullable MagnificationAnimationCallback animationCallback, int id) {
return enableWindowMagnification(displayId, scale, centerX, centerY, animationCallback,
- WINDOW_POSITION_AT_CENTER);
+ WINDOW_POSITION_AT_CENTER, id);
}
/**
@@ -348,7 +372,7 @@
public boolean enableWindowMagnification(int displayId, float scale, float centerX,
float centerY, @WindowPosition int windowPosition) {
return enableWindowMagnification(displayId, scale, centerX, centerY,
- STUB_ANIMATION_CALLBACK, windowPosition);
+ STUB_ANIMATION_CALLBACK, windowPosition, MAGNIFICATION_GESTURE_HANDLER_ID);
}
/**
@@ -367,7 +391,7 @@
*/
public boolean enableWindowMagnification(int displayId, float scale, float centerX,
float centerY, @Nullable MagnificationAnimationCallback animationCallback,
- @WindowPosition int windowPosition) {
+ @WindowPosition int windowPosition, int id) {
final boolean enabled;
boolean previousEnabled;
synchronized (mLock) {
@@ -380,7 +404,7 @@
}
previousEnabled = magnifier.mEnabled;
enabled = magnifier.enableWindowMagnificationInternal(scale, centerX, centerY,
- animationCallback, windowPosition);
+ animationCallback, windowPosition, id);
}
if (enabled && !previousEnabled) {
@@ -394,9 +418,10 @@
*
* @param displayId The logical display id.
* @param clear {@true} Clears the state of window magnification.
+ * @return {@code true} if the magnification is turned to be disabled successfully
*/
- void disableWindowMagnification(int displayId, boolean clear) {
- disableWindowMagnification(displayId, clear, STUB_ANIMATION_CALLBACK);
+ boolean disableWindowMagnification(int displayId, boolean clear) {
+ return disableWindowMagnification(displayId, clear, STUB_ANIMATION_CALLBACK);
}
/**
@@ -405,14 +430,15 @@
* @param displayId The logical display id.
* @param clear {@true} Clears the state of window magnification.
* @param animationCallback Called when the animation result is valid.
+ * @return {@code true} if the magnification is turned to be disabled successfully
*/
- void disableWindowMagnification(int displayId, boolean clear,
+ public boolean disableWindowMagnification(int displayId, boolean clear,
MagnificationAnimationCallback animationCallback) {
final boolean disabled;
synchronized (mLock) {
WindowMagnifier magnifier = mWindowMagnifiers.get(displayId);
if (magnifier == null || mConnectionWrapper == null) {
- return;
+ return false;
}
disabled = magnifier.disableWindowMagnificationInternal(animationCallback);
if (clear) {
@@ -423,6 +449,7 @@
if (disabled) {
mCallback.onWindowMagnificationActivationState(displayId, false);
}
+ return disabled;
}
/**
@@ -490,7 +517,7 @@
public float getScale(int displayId) {
synchronized (mLock) {
WindowMagnifier magnifier = mWindowMagnifiers.get(displayId);
- if (magnifier == null) {
+ if (magnifier == null || !magnifier.mEnabled) {
return 1.0f;
}
return magnifier.getScale();
@@ -548,7 +575,7 @@
public float getCenterX(int displayId) {
synchronized (mLock) {
WindowMagnifier magnifier = mWindowMagnifiers.get(displayId);
- if (magnifier == null) {
+ if (magnifier == null || !magnifier.mEnabled) {
return Float.NaN;
}
return magnifier.getCenterX();
@@ -564,7 +591,7 @@
public float getCenterY(int displayId) {
synchronized (mLock) {
WindowMagnifier magnifier = mWindowMagnifiers.get(displayId);
- if (magnifier == null) {
+ if (magnifier == null || !magnifier.mEnabled) {
return Float.NaN;
}
return magnifier.getCenterY();
@@ -581,7 +608,7 @@
public void getMagnificationSourceBounds(int displayId, @NonNull Region outRegion) {
synchronized (mLock) {
WindowMagnifier magnifier = mWindowMagnifiers.get(displayId);
- if (magnifier == null) {
+ if (magnifier == null || !magnifier.mEnabled) {
outRegion.setEmpty();
} else {
outRegion.set(magnifier.mSourceBounds);
@@ -590,24 +617,6 @@
}
/**
- * Resets the magnification scale and center.
- *
- * @param displayId The logical display id.
- * @return {@code true} if the magnification spec changed, {@code false} if
- * the spec did not change
- */
- public boolean reset(int displayId) {
- synchronized (mLock) {
- WindowMagnifier magnifier = mWindowMagnifiers.get(displayId);
- if (magnifier == null) {
- return false;
- }
- magnifier.reset();
- return true;
- }
- }
-
- /**
* Creates the windowMagnifier based on the specified display and stores it.
*
* @param displayId logical display id.
@@ -722,6 +731,9 @@
/**
* A class manipulates window magnification per display and contains the magnification
* information.
+ * <p>
+ * This class requires to hold the lock when controlling the magnifier.
+ * </p>
*/
private static class WindowMagnifier {
@@ -735,6 +747,8 @@
// The magnified bounds on the screen.
private final Rect mSourceBounds = new Rect();
+ private int mIdOfLastServiceToControl = INVALID_SERVICE_ID;
+
private PointF mMagnificationFrameOffsetRatio = new PointF(0f, 0f);
WindowMagnifier(int displayId, WindowMagnificationManager windowMagnificationManager) {
@@ -745,7 +759,7 @@
@GuardedBy("mLock")
boolean enableWindowMagnificationInternal(float scale, float centerX, float centerY,
@Nullable MagnificationAnimationCallback animationCallback,
- @WindowPosition int windowPosition) {
+ @WindowPosition int windowPosition, int id) {
// Handle defaults. The scale may be NAN when just updating magnification center.
if (Float.isNaN(scale)) {
scale = getScale();
@@ -757,7 +771,7 @@
mMagnificationFrameOffsetRatio.y, animationCallback)) {
mScale = normScale;
mEnabled = true;
-
+ mIdOfLastServiceToControl = id;
return true;
}
return false;
@@ -785,7 +799,7 @@
if (mWindowMagnificationManager.disableWindowMagnificationInternal(
mDisplayId, animationResultCallback)) {
mEnabled = false;
-
+ mIdOfLastServiceToControl = INVALID_SERVICE_ID;
return true;
}
return false;
@@ -813,6 +827,13 @@
mBounds.set(rect);
}
+ /**
+ * Returns the ID of the last service that changed the magnification config.
+ */
+ int getIdOfLastServiceToControl() {
+ return mIdOfLastServiceToControl;
+ }
+
@GuardedBy("mLock")
int pointersInWindow(MotionEvent motionEvent) {
int count = 0;
@@ -840,6 +861,8 @@
@GuardedBy("mLock")
void reset() {
mEnabled = false;
+ mIdOfLastServiceToControl = INVALID_SERVICE_ID;
+ mSourceBounds.setEmpty();
}
@GuardedBy("mLock")
@@ -849,12 +872,12 @@
@GuardedBy("mLock")
float getCenterX() {
- return mEnabled ? mSourceBounds.exactCenterX() : Float.NaN;
+ return mSourceBounds.exactCenterX();
}
@GuardedBy("mLock")
float getCenterY() {
- return mEnabled ? mSourceBounds.exactCenterY() : Float.NaN;
+ return mSourceBounds.exactCenterY();
}
}
diff --git a/services/backup/java/com/android/server/backup/UserBackupManagerService.java b/services/backup/java/com/android/server/backup/UserBackupManagerService.java
index f8da035..efa026b 100644
--- a/services/backup/java/com/android/server/backup/UserBackupManagerService.java
+++ b/services/backup/java/com/android/server/backup/UserBackupManagerService.java
@@ -3907,14 +3907,20 @@
}
int operationType;
+ TransportConnection transportConnection = null;
try {
- operationType = getOperationTypeFromTransport(
- mTransportManager.getTransportClientOrThrow(transport, /* caller */
- "BMS.beginRestoreSession"));
+ transportConnection = mTransportManager.getTransportClientOrThrow(
+ transport, /* caller */"BMS.beginRestoreSession");
+ operationType = getOperationTypeFromTransport(transportConnection);
} catch (TransportNotAvailableException | TransportNotRegisteredException
| RemoteException e) {
Slog.w(TAG, "Failed to get operation type from transport: " + e);
return null;
+ } finally {
+ if (transportConnection != null) {
+ mTransportManager.disposeOfTransportClient(transportConnection,
+ /* caller */"BMS.beginRestoreSession");
+ }
}
synchronized (this) {
diff --git a/services/companion/java/com/android/server/companion/AssociationRequestsProcessor.java b/services/companion/java/com/android/server/companion/AssociationRequestsProcessor.java
index 637994f..1914164 100644
--- a/services/companion/java/com/android/server/companion/AssociationRequestsProcessor.java
+++ b/services/companion/java/com/android/server/companion/AssociationRequestsProcessor.java
@@ -22,7 +22,6 @@
import static android.companion.CompanionDeviceManager.COMPANION_DEVICE_DISCOVERY_PACKAGE_NAME;
import static android.content.ComponentName.createRelative;
-import static com.android.internal.util.CollectionUtils.filter;
import static com.android.server.companion.CompanionDeviceManagerService.DEBUG;
import static com.android.server.companion.CompanionDeviceManagerService.LOG_TAG;
import static com.android.server.companion.PermissionsUtils.enforcePermissionsForAssociation;
@@ -57,6 +56,7 @@
import java.util.Arrays;
import java.util.HashSet;
+import java.util.List;
import java.util.Set;
/**
@@ -124,14 +124,17 @@
private static final int ASSOCIATE_WITHOUT_PROMPT_MAX_PER_TIME_WINDOW = 5;
private static final long ASSOCIATE_WITHOUT_PROMPT_WINDOW_MS = 60 * 60 * 1000; // 60 min;
- private final Context mContext;
- private final CompanionDeviceManagerService mService;
- private final PackageManagerInternal mPackageManager;
+ private final @NonNull Context mContext;
+ private final @NonNull CompanionDeviceManagerService mService;
+ private final @NonNull PackageManagerInternal mPackageManager;
+ private final @NonNull AssociationStore mAssociationStore;
- AssociationRequestsProcessor(CompanionDeviceManagerService service) {
+ AssociationRequestsProcessor(@NonNull CompanionDeviceManagerService service,
+ @NonNull AssociationStore associationStore) {
mContext = service.getContext();
mService = service;
mPackageManager = service.mPackageManagerInternal;
+ mAssociationStore = associationStore;
}
/**
@@ -330,18 +333,24 @@
}
// Throttle frequent associations
- long now = System.currentTimeMillis();
- Set<AssociationInfo> recentAssociations = filter(
- mService.getAssociations(userId, packageName),
- a -> now - a.getTimeApprovedMs() < ASSOCIATE_WITHOUT_PROMPT_WINDOW_MS);
-
- if (recentAssociations.size() >= ASSOCIATE_WITHOUT_PROMPT_MAX_PER_TIME_WINDOW) {
- Slog.w(TAG, "Too many associations. " + packageName
- + " already associated " + recentAssociations.size()
- + " devices within the last " + ASSOCIATE_WITHOUT_PROMPT_WINDOW_MS
- + "ms: " + recentAssociations);
- return false;
+ final long now = System.currentTimeMillis();
+ final List<AssociationInfo> associationForPackage =
+ mAssociationStore.getAssociationsForPackage(userId, packageName);
+ // Number of "recent" associations.
+ int recent = 0;
+ for (AssociationInfo association : associationForPackage) {
+ final boolean isRecent =
+ now - association.getTimeApprovedMs() < ASSOCIATE_WITHOUT_PROMPT_WINDOW_MS;
+ if (isRecent) {
+ if (++recent >= ASSOCIATE_WITHOUT_PROMPT_MAX_PER_TIME_WINDOW) {
+ Slog.w(TAG, "Too many associations: " + packageName + " already "
+ + "associated " + recent + " devices within the last "
+ + ASSOCIATE_WITHOUT_PROMPT_WINDOW_MS + "ms");
+ return false;
+ }
+ }
}
+
String[] sameOemCerts = mContext.getResources()
.getStringArray(com.android.internal.R.array.config_companionDeviceCerts);
diff --git a/services/companion/java/com/android/server/companion/AssociationStore.java b/services/companion/java/com/android/server/companion/AssociationStore.java
new file mode 100644
index 0000000..58fc8f7
--- /dev/null
+++ b/services/companion/java/com/android/server/companion/AssociationStore.java
@@ -0,0 +1,126 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.companion;
+
+import android.annotation.IntDef;
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.annotation.UserIdInt;
+import android.companion.AssociationInfo;
+
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.util.Collection;
+import java.util.List;
+
+/**
+ * Interface for a store of {@link AssociationInfo}-s.
+ */
+public interface AssociationStore {
+
+ @IntDef(prefix = { "CHANGE_TYPE_" }, value = {
+ CHANGE_TYPE_ADDED,
+ CHANGE_TYPE_REMOVED,
+ CHANGE_TYPE_UPDATED_ADDRESS_CHANGED,
+ CHANGE_TYPE_UPDATED_ADDRESS_UNCHANGED,
+ })
+ @Retention(RetentionPolicy.SOURCE)
+ @interface ChangeType {}
+
+ int CHANGE_TYPE_ADDED = 0;
+ int CHANGE_TYPE_REMOVED = 1;
+ int CHANGE_TYPE_UPDATED_ADDRESS_CHANGED = 2;
+ int CHANGE_TYPE_UPDATED_ADDRESS_UNCHANGED = 3;
+
+ /** Listener for any changes to {@link AssociationInfo}-s. */
+ interface OnChangeListener {
+ default void onAssociationChanged(
+ @ChangeType int changeType, AssociationInfo association) {}
+
+ default void onAssociationAdded(AssociationInfo association) {}
+
+ default void onAssociationRemoved(AssociationInfo association) {}
+
+ default void onAssociationUpdated(AssociationInfo association, boolean addressChanged) {}
+ }
+
+ /**
+ * @return all CDM associations.
+ */
+ @NonNull
+ Collection<AssociationInfo> getAssociations();
+
+ /**
+ * @return a {@link List} of associations that belong to the user.
+ */
+ @NonNull
+ List<AssociationInfo> getAssociationsForUser(@UserIdInt int userId);
+
+ /**
+ * @return a {@link List} of association that belong to the package.
+ */
+ @NonNull
+ List<AssociationInfo> getAssociationsForPackage(
+ @UserIdInt int userId, @NonNull String packageName);
+
+ /**
+ * @return an association with the given address that belong to the given package if such an
+ * association exists, otherwise {@code null}.
+ */
+ @Nullable
+ AssociationInfo getAssociationsForPackageWithAddress(
+ @UserIdInt int userId, @NonNull String packageName, @NonNull String macAddress);
+
+ /**
+ * @return an association with the given id if such an association exists, otherwise
+ * {@code null}.
+ */
+ @Nullable
+ AssociationInfo getAssociationById(int id);
+
+ /**
+ * @return all associations with the given MAc address.
+ */
+ @NonNull
+ List<AssociationInfo> getAssociationsByAddress(@NonNull String macAddress);
+
+ /** Register a {@link OnChangeListener} */
+ void registerListener(@NonNull OnChangeListener listener);
+
+ /** Un-register a previously registered {@link OnChangeListener} */
+ void unregisterListener(@NonNull OnChangeListener listener);
+
+ /** @hide */
+ static String changeTypeToString(@ChangeType int changeType) {
+ switch (changeType) {
+ case CHANGE_TYPE_ADDED:
+ return "ASSOCIATION_ADDED";
+
+ case CHANGE_TYPE_REMOVED:
+ return "ASSOCIATION_REMOVED";
+
+ case CHANGE_TYPE_UPDATED_ADDRESS_CHANGED:
+ return "ASSOCIATION_UPDATED";
+
+ case CHANGE_TYPE_UPDATED_ADDRESS_UNCHANGED:
+ return "ASSOCIATION_UPDATED_ADDRESS_UNCHANGED";
+
+ default:
+ return "Unknown (" + changeType + ")";
+ }
+ }
+}
diff --git a/services/companion/java/com/android/server/companion/AssociationStoreImpl.java b/services/companion/java/com/android/server/companion/AssociationStoreImpl.java
new file mode 100644
index 0000000..3f0200e
--- /dev/null
+++ b/services/companion/java/com/android/server/companion/AssociationStoreImpl.java
@@ -0,0 +1,302 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.companion;
+
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.annotation.UserIdInt;
+import android.companion.AssociationInfo;
+import android.net.MacAddress;
+import android.util.Log;
+import android.util.SparseArray;
+
+import com.android.internal.annotations.GuardedBy;
+import com.android.internal.util.CollectionUtils;
+
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.LinkedHashSet;
+import java.util.List;
+import java.util.Map;
+import java.util.Objects;
+import java.util.Set;
+
+/**
+ * Implementation of the {@link AssociationStore}, with addition of the methods for modification.
+ * <ul>
+ * <li> {@link #addAssociation(AssociationInfo)}
+ * <li> {@link #removeAssociation(int)}
+ * <li> {@link #updateAssociation(AssociationInfo)}
+ * </ul>
+ *
+ * The class has package-private access level, and instances of the class should only be created by
+ * the {@link CompanionDeviceManagerService}.
+ * Other system component (both inside and outside if the com.android.server.companion package)
+ * should use public {@link AssociationStore} interface.
+ */
+class AssociationStoreImpl implements AssociationStore {
+ private static final boolean DEBUG = false;
+ private static final String TAG = "AssociationStore";
+
+ private final Object mLock = new Object();
+
+ @GuardedBy("mLock")
+ private final Map<Integer, AssociationInfo> mIdMap;
+ @GuardedBy("mLock")
+ private final Map<MacAddress, Set<Integer>> mAddressMap;
+ @GuardedBy("mLock")
+ private final SparseArray<List<AssociationInfo>> mCachedPerUser = new SparseArray<>();
+
+ @GuardedBy("mListeners")
+ private final Set<OnChangeListener> mListeners = new LinkedHashSet<>();
+
+ AssociationStoreImpl(Collection<AssociationInfo> associations) {
+ synchronized (mLock) {
+ final int size = associations.size();
+ mIdMap = new HashMap<>(size);
+ mAddressMap = new HashMap<>(size);
+
+ for (AssociationInfo association : associations) {
+ final int id = association.getId();
+ mIdMap.put(id, association);
+
+ final MacAddress address = association.getDeviceMacAddress();
+ if (address != null) {
+ mAddressMap.computeIfAbsent(address, it -> new HashSet<>()).add(id);
+ }
+ }
+ }
+ }
+
+ void addAssociation(@NonNull AssociationInfo association) {
+ final int id = association.getId();
+
+ if (DEBUG) {
+ Log.i(TAG, "addAssociation() " + association.toShortString());
+ Log.d(TAG, " association=" + association);
+ }
+
+ synchronized (mLock) {
+ if (mIdMap.containsKey(id)) {
+ if (DEBUG) Log.w(TAG, "Association already stored.");
+ return;
+ }
+ mIdMap.put(id, association);
+
+ final MacAddress address = association.getDeviceMacAddress();
+ if (address != null) {
+ mAddressMap.computeIfAbsent(address, it -> new HashSet<>()).add(id);
+ }
+
+ invalidateCacheForUserLocked(association.getUserId());
+ }
+
+ broadcastChange(CHANGE_TYPE_ADDED, association);
+ }
+
+ void updateAssociation(@NonNull AssociationInfo updated) {
+ final int id = updated.getId();
+
+ if (DEBUG) {
+ Log.i(TAG, "updateAssociation() " + updated.toShortString());
+ Log.d(TAG, " updated=" + updated);
+ }
+
+ final AssociationInfo current;
+ final boolean macAddressChanged;
+ synchronized (mLock) {
+ current = mIdMap.get(id);
+ if (current == null) {
+ if (DEBUG) Log.w(TAG, "Association with id " + id + " does not exist.");
+ return;
+ }
+ if (DEBUG) Log.d(TAG, " current=" + current);
+
+ if (current.equals(updated)) {
+ if (DEBUG) Log.w(TAG, " No changes.");
+ return;
+ }
+
+ // Update the ID-to-Association map.
+ mIdMap.put(id, updated);
+
+ // Update the MacAddress-to-List<Association> map if needed.
+ final MacAddress updatedAddress = updated.getDeviceMacAddress();
+ final MacAddress currentAddress = current.getDeviceMacAddress();
+ macAddressChanged = Objects.equals(
+ current.getDeviceMacAddress(), updated.getDeviceMacAddress());
+ if (macAddressChanged) {
+ if (currentAddress != null) {
+ mAddressMap.get(currentAddress).remove(id);
+ }
+ if (updatedAddress != null) {
+ mAddressMap.computeIfAbsent(updatedAddress, it -> new HashSet<>()).add(id);
+ }
+ }
+ }
+
+ final int changeType = macAddressChanged ? CHANGE_TYPE_UPDATED_ADDRESS_CHANGED
+ : CHANGE_TYPE_UPDATED_ADDRESS_UNCHANGED;
+ broadcastChange(changeType, updated);
+ }
+
+ void removeAssociation(int id) {
+ if (DEBUG) Log.i(TAG, "removeAssociation() id=" + id);
+
+ final AssociationInfo association;
+ synchronized (mLock) {
+ association = mIdMap.remove(id);
+
+ if (association == null) {
+ if (DEBUG) Log.w(TAG, "Association with id " + id + " is not stored.");
+ return;
+ } else {
+ if (DEBUG) {
+ Log.i(TAG, "removed " + association.toShortString());
+ Log.d(TAG, " association=" + association);
+ }
+ }
+
+ final MacAddress macAddress = association.getDeviceMacAddress();
+ if (macAddress != null) {
+ mAddressMap.get(macAddress).remove(id);
+ }
+
+ invalidateCacheForUserLocked(association.getUserId());
+ }
+
+ broadcastChange(CHANGE_TYPE_REMOVED, association);
+ }
+
+ public @NonNull Collection<AssociationInfo> getAssociations() {
+ final Collection<AssociationInfo> allAssociations;
+ synchronized (mLock) {
+ allAssociations = mIdMap.values();
+ }
+ return Collections.unmodifiableCollection(allAssociations);
+ }
+
+ public @NonNull List<AssociationInfo> getAssociationsForUser(@UserIdInt int userId) {
+ synchronized (mLock) {
+ return getAssociationsForUserLocked(userId);
+ }
+ }
+
+ public @NonNull List<AssociationInfo> getAssociationsForPackage(
+ @UserIdInt int userId, @NonNull String packageName) {
+ final List<AssociationInfo> associationsForUser = getAssociationsForUser(userId);
+ final List<AssociationInfo> associationsForPackage =
+ CollectionUtils.filter(associationsForUser,
+ it -> it.getPackageName().equals(packageName));
+ return Collections.unmodifiableList(associationsForPackage);
+ }
+
+ public @Nullable AssociationInfo getAssociationsForPackageWithAddress(
+ @UserIdInt int userId, @NonNull String packageName, @NonNull String macAddress) {
+ final List<AssociationInfo> associations = getAssociationsByAddress(macAddress);
+ return CollectionUtils.find(associations,
+ it -> it.belongsToPackage(userId, packageName));
+ }
+
+ public @Nullable AssociationInfo getAssociationById(int id) {
+ synchronized (mLock) {
+ return mIdMap.get(id);
+ }
+ }
+
+ public @NonNull List<AssociationInfo> getAssociationsByAddress(@NonNull String macAddress) {
+ final MacAddress address = MacAddress.fromString(macAddress);
+
+ synchronized (mLock) {
+ final Set<Integer> ids = mAddressMap.get(address);
+ if (ids == null) return Collections.emptyList();
+
+ final List<AssociationInfo> associations = new ArrayList<>();
+ for (AssociationInfo association : mIdMap.values()) {
+ if (address.equals(association.getDeviceMacAddress())) {
+ associations.add(association);
+ }
+ }
+
+ return Collections.unmodifiableList(associations);
+ }
+ }
+
+ @GuardedBy("mLock")
+ private @NonNull List<AssociationInfo> getAssociationsForUserLocked(@UserIdInt int userId) {
+ final List<AssociationInfo> cached = mCachedPerUser.get(userId);
+ if (cached != null) {
+ return cached;
+ }
+
+ final List<AssociationInfo> associationsForUser = new ArrayList<>();
+ for (AssociationInfo association : mIdMap.values()) {
+ if (association.getUserId() == userId) {
+ associationsForUser.add(association);
+ }
+ }
+ final List<AssociationInfo> set = Collections.unmodifiableList(associationsForUser);
+ mCachedPerUser.set(userId, set);
+ return set;
+ }
+
+ @GuardedBy("mLock")
+ private void invalidateCacheForUserLocked(@UserIdInt int userId) {
+ mCachedPerUser.delete(userId);
+ }
+
+ public void registerListener(@NonNull OnChangeListener listener) {
+ synchronized (mListeners) {
+ mListeners.add(listener);
+ }
+ }
+
+ public void unregisterListener(@NonNull OnChangeListener listener) {
+ synchronized (mListeners) {
+ mListeners.remove(listener);
+ }
+ }
+
+ private void broadcastChange(@ChangeType int changeType, AssociationInfo association) {
+ synchronized (mListeners) {
+ for (OnChangeListener listener : mListeners) {
+ listener.onAssociationChanged(changeType, association);
+
+ switch (changeType) {
+ case CHANGE_TYPE_ADDED:
+ listener.onAssociationAdded(association);
+ break;
+
+ case CHANGE_TYPE_REMOVED:
+ listener.onAssociationRemoved(association);
+ break;
+
+ case CHANGE_TYPE_UPDATED_ADDRESS_CHANGED:
+ listener.onAssociationUpdated(association, true);
+ break;
+
+ case CHANGE_TYPE_UPDATED_ADDRESS_UNCHANGED:
+ listener.onAssociationUpdated(association, false);
+ break;
+ }
+ }
+ }
+ }
+}
diff --git a/services/companion/java/com/android/server/companion/CompanionDeviceManagerService.java b/services/companion/java/com/android/server/companion/CompanionDeviceManagerService.java
index 9b370dc..5aa1c93 100644
--- a/services/companion/java/com/android/server/companion/CompanionDeviceManagerService.java
+++ b/services/companion/java/com/android/server/companion/CompanionDeviceManagerService.java
@@ -27,15 +27,12 @@
import static android.os.Process.SYSTEM_UID;
import static android.os.UserHandle.getCallingUserId;
-import static com.android.internal.util.CollectionUtils.add;
import static com.android.internal.util.CollectionUtils.any;
-import static com.android.internal.util.CollectionUtils.filter;
import static com.android.internal.util.CollectionUtils.find;
-import static com.android.internal.util.CollectionUtils.forEach;
-import static com.android.internal.util.CollectionUtils.map;
import static com.android.internal.util.Preconditions.checkState;
import static com.android.internal.util.function.pooled.PooledLambda.obtainMessage;
import static com.android.internal.util.function.pooled.PooledLambda.obtainRunnable;
+import static com.android.server.companion.AssociationStore.CHANGE_TYPE_UPDATED_ADDRESS_UNCHANGED;
import static com.android.server.companion.PermissionsUtils.checkCallerCanManageAssociationsForPackage;
import static com.android.server.companion.PermissionsUtils.checkCallerCanManageCompanionDevice;
import static com.android.server.companion.PermissionsUtils.enforceCallerCanInteractWithUserId;
@@ -45,8 +42,6 @@
import static com.android.server.companion.RolesUtils.addRoleHolderForAssociation;
import static com.android.server.companion.RolesUtils.removeRoleHolderForAssociation;
-import static java.util.Collections.emptySet;
-import static java.util.Collections.unmodifiableSet;
import static java.util.Objects.requireNonNull;
import static java.util.concurrent.TimeUnit.MINUTES;
@@ -82,7 +77,6 @@
import android.content.pm.PackageItemInfo;
import android.content.pm.PackageManager;
import android.content.pm.PackageManagerInternal;
-import android.content.pm.UserInfo;
import android.net.MacAddress;
import android.net.NetworkPolicyManager;
import android.os.Binder;
@@ -90,6 +84,7 @@
import android.os.Handler;
import android.os.Parcel;
import android.os.PowerWhitelistManager;
+import android.os.RemoteCallbackList;
import android.os.RemoteException;
import android.os.ResultReceiver;
import android.os.ServiceManager;
@@ -111,9 +106,7 @@
import com.android.internal.notification.NotificationAccessConfirmationActivityContract;
import com.android.internal.os.BackgroundThread;
import com.android.internal.util.ArrayUtils;
-import com.android.internal.util.CollectionUtils;
import com.android.internal.util.DumpUtils;
-import com.android.internal.util.function.pooled.PooledLambda;
import com.android.server.FgThread;
import com.android.server.LocalServices;
import com.android.server.SystemService;
@@ -126,6 +119,7 @@
import java.text.DateFormat;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
+import java.util.Collections;
import java.util.Date;
import java.util.HashMap;
import java.util.HashSet;
@@ -134,12 +128,11 @@
import java.util.Objects;
import java.util.Set;
import java.util.TimeZone;
-import java.util.function.Function;
-import java.util.function.Predicate;
/** @hide */
@SuppressLint("LongLogTag")
-public class CompanionDeviceManagerService extends SystemService {
+public class CompanionDeviceManagerService extends SystemService
+ implements AssociationStore.OnChangeListener {
static final String LOG_TAG = "CompanionDeviceManagerService";
static final boolean DEBUG = false;
@@ -161,10 +154,11 @@
sDateFormat.setTimeZone(TimeZone.getDefault());
}
- private final CompanionDeviceManagerImpl mImpl;
// Persistent data store for all Associations.
- private final PersistentDataStore mPersistentDataStore;
- private final AssociationRequestsProcessor mAssociationRequestsProcessor;
+ private PersistentDataStore mPersistentStore;
+ private AssociationStoreImpl mAssociationStore;
+ private AssociationRequestsProcessor mAssociationRequestsProcessor;
+
private PowerWhitelistManager mPowerWhitelistManager;
private IAppOpsService mAppOpsManager;
private BluetoothAdapter mBluetoothAdapter;
@@ -183,21 +177,19 @@
mUnbindDeviceListenersRunnable = new UnbindDeviceListenersRunnable();
private ArrayMap<String, TriggerDeviceDisappearedRunnable> mTriggerDeviceDisappearedRunnables =
new ArrayMap<>();
+ private final RemoteCallbackList<IOnAssociationsChangedListener> mListeners =
+ new RemoteCallbackList<>();
- final Object mLock = new Object();
final Handler mMainHandler = Handler.getMain();
private CompanionDevicePresenceController mCompanionDevicePresenceController;
- /** Maps a {@link UserIdInt} to a set of associations for the user. */
- @GuardedBy("mLock")
- private final SparseArray<Set<AssociationInfo>> mCachedAssociations = new SparseArray<>();
/**
* A structure that consist of two nested maps, and effectively maps (userId + packageName) to
* a list of IDs that have been previously assigned to associations for that package.
* We maintain this structure so that we never re-use association IDs for the same package
* (until it's uninstalled).
*/
- @GuardedBy("mLock")
+ @GuardedBy("mPreviouslyUsedIds")
private final SparseArray<Map<String, Set<Integer>>> mPreviouslyUsedIds = new SparseArray<>();
ActivityTaskManagerInternal mAtmInternal;
@@ -206,8 +198,6 @@
public CompanionDeviceManagerService(Context context) {
super(context);
- mImpl = new CompanionDeviceManagerImpl();
- mPersistentDataStore = new PersistentDataStore();
mPowerWhitelistManager = context.getSystemService(PowerWhitelistManager.class);
mAppOpsManager = IAppOpsService.Stub.asInterface(
@@ -218,49 +208,36 @@
mPermissionControllerManager = requireNonNull(
context.getSystemService(PermissionControllerManager.class));
mUserManager = context.getSystemService(UserManager.class);
- mCompanionDevicePresenceController = new CompanionDevicePresenceController(this);
- mAssociationRequestsProcessor = new AssociationRequestsProcessor(this);
-
- registerPackageMonitor();
- }
-
- private void registerPackageMonitor() {
- new PackageMonitor() {
- @Override
- public void onPackageRemoved(String packageName, int uid) {
- final int userId = getChangingUserId();
- Slog.i(LOG_TAG, "onPackageRemoved() u" + userId + "/" + packageName);
-
- clearAssociationForPackage(userId, packageName);
- }
-
- @Override
- public void onPackageDataCleared(String packageName, int uid) {
- final int userId = getChangingUserId();
- Slog.i(LOG_TAG, "onPackageDataCleared() u" + userId + "/" + packageName);
-
- clearAssociationForPackage(userId, packageName);
- }
-
- @Override
- public void onPackageModified(String packageName) {
- final int userId = getChangingUserId();
- Slog.i(LOG_TAG, "onPackageModified() u" + userId + "/" + packageName);
-
- forEach(getAssociations(userId, packageName), association ->
- updateSpecialAccessPermissionForAssociatedPackage(association));
- }
- }.register(getContext(), FgThread.get().getLooper(), UserHandle.ALL, true);
}
@Override
public void onStart() {
- publishBinderService(Context.COMPANION_DEVICE_SERVICE, mImpl);
+ mPersistentStore = new PersistentDataStore();
+ final Set<AssociationInfo> allAssociations = new ArraySet<>();
+
+ synchronized (mPreviouslyUsedIds) {
+ // The data is stored in DE directories, so we can read the data for all users now
+ // (which would not be possible if the data was stored to CE directories).
+ mPersistentStore.readStateForUsers(
+ mUserManager.getAliveUsers(), allAssociations, mPreviouslyUsedIds);
+ }
+
+ mAssociationStore = new AssociationStoreImpl(allAssociations);
+ mAssociationStore.registerListener(this);
+
+ mCompanionDevicePresenceController = new CompanionDevicePresenceController(this);
+ mAssociationRequestsProcessor = new AssociationRequestsProcessor(this, mAssociationStore);
+
+ // Publish "binder service"
+ final CompanionDeviceManagerImpl impl = new CompanionDeviceManagerImpl();
+ publishBinderService(Context.COMPANION_DEVICE_SERVICE, impl);
}
@Override
public void onBootPhase(int phase) {
if (phase == SystemService.PHASE_SYSTEM_SERVICES_READY) {
+ registerPackageMonitor();
+
// Init Bluetooth
mBluetoothAdapter = BluetoothAdapter.getDefaultAdapter();
if (mBluetoothAdapter != null) {
@@ -279,7 +256,7 @@
@Override
public void onUserUnlocking(@NonNull TargetUser user) {
final int userId = user.getUserIdentifier();
- final Set<AssociationInfo> associations = getAllAssociationsForUser(userId);
+ final List<AssociationInfo> associations = mAssociationStore.getAssociationsForUser(userId);
if (associations.isEmpty()) return;
@@ -290,42 +267,18 @@
MINUTES.toMillis(10));
}
- @NonNull
- Set<AssociationInfo> getAllAssociationsForUser(@UserIdInt int userId) {
- synchronized (mLock) {
- readPersistedStateForUserIfNeededLocked(userId);
- // This returns non-null, because the readAssociationsInfoForUserIfNeededLocked() method
- // we just called adds an empty set, if there was no previously saved data.
- return mCachedAssociations.get(userId);
- }
- }
-
- @NonNull
- Set<AssociationInfo> getAssociations(@UserIdInt int userId, @NonNull String packageName) {
- return filter(getAllAssociationsForUser(userId),
- a -> a.belongsToPackage(userId, packageName));
- }
-
- @Nullable
- private AssociationInfo getAssociation(int associationId) {
- return find(getAllAssociations(), association -> association.getId() == associationId);
- }
-
- @Nullable
- AssociationInfo getAssociation(
- @UserIdInt int userId, @NonNull String packageName, @NonNull String macAddress) {
- return find(getAssociations(userId, packageName), a -> a.isLinkedTo(macAddress));
- }
-
@Nullable
AssociationInfo getAssociationWithCallerChecks(
@UserIdInt int userId, @NonNull String packageName, @NonNull String macAddress) {
- return sanitizeWithCallerChecks(getAssociation(userId, packageName, macAddress));
+ final AssociationInfo association = mAssociationStore.getAssociationsForPackageWithAddress(
+ userId, packageName, macAddress);
+ return sanitizeWithCallerChecks(association);
}
@Nullable
AssociationInfo getAssociationWithCallerChecks(int associationId) {
- return sanitizeWithCallerChecks(getAssociation(associationId));
+ final AssociationInfo association = mAssociationStore.getAssociationById(associationId);
+ return sanitizeWithCallerChecks(association);
}
@Nullable
@@ -341,19 +294,6 @@
return association;
}
- private Set<AssociationInfo> getAllAssociations() {
- final long identity = Binder.clearCallingIdentity();
- try {
- final Set<AssociationInfo> result = new ArraySet<>();
- for (UserInfo user : mUserManager.getAliveUsers()) {
- result.addAll(getAllAssociationsForUser(user.id));
- }
- return result;
- } finally {
- Binder.restoreCallingIdentity(identity);
- }
- }
-
void maybeGrantAutoRevokeExemptions() {
Slog.d(LOG_TAG, "maybeGrantAutoRevokeExemptions()");
PackageManager pm = getContext().getPackageManager();
@@ -366,10 +306,8 @@
}
try {
- Set<AssociationInfo> associations = getAllAssociationsForUser(userId);
- if (associations == null) {
- continue;
- }
+ final List<AssociationInfo> associations =
+ mAssociationStore.getAssociationsForUser(userId);
for (AssociationInfo a : associations) {
try {
int uid = pm.getPackageUidAsUser(a.getPackageName(), userId);
@@ -384,6 +322,61 @@
}
}
+ @Override
+ public void onAssociationChanged(
+ @AssociationStore.ChangeType int changeType, AssociationInfo association) {
+ final int id = association.getId();
+ final int userId = association.getUserId();
+ final String packageName = association.getPackageName();
+
+ if (changeType == AssociationStore.CHANGE_TYPE_REMOVED) {
+ markIdAsPreviouslyUsedForPackage(id, userId, packageName);
+ }
+
+ final List<AssociationInfo> updatedAssociations =
+ mAssociationStore.getAssociationsForUser(userId);
+ final Map<String, Set<Integer>> usedIdsForUser = getPreviouslyUsedIdsForUser(userId);
+ BackgroundThread.getHandler().post(() ->
+ mPersistentStore.persistStateForUser(userId, updatedAssociations, usedIdsForUser));
+
+ // Notify listeners if ADDED, REMOVED or UPDATED_ADDRESS_CHANGED.
+ // Do NOT notify when UPDATED_ADDRESS_UNCHANGED, which means a minor tweak in association's
+ // configs, which "listeners" won't (and shouldn't) be able to see.
+ if (changeType != CHANGE_TYPE_UPDATED_ADDRESS_UNCHANGED) {
+ notifyListeners(userId, updatedAssociations);
+ }
+ updateAtm(userId, updatedAssociations);
+
+ restartBleScan();
+ }
+
+ private void notifyListeners(
+ @UserIdInt int userId, @NonNull List<AssociationInfo> associations) {
+ mListeners.broadcast((listener, callbackUserId) -> {
+ if ((int) callbackUserId == userId) {
+ try {
+ listener.onAssociationsChanged(associations);
+ } catch (RemoteException ignored) {
+ }
+ }
+ });
+ }
+
+ private void markIdAsPreviouslyUsedForPackage(
+ int associationId, @UserIdInt int userId, @NonNull String packageName) {
+ synchronized (mPreviouslyUsedIds) {
+ Map<String, Set<Integer>> usedIdsForUser = mPreviouslyUsedIds.get(userId);
+ if (usedIdsForUser == null) {
+ usedIdsForUser = new HashMap<>();
+ mPreviouslyUsedIds.put(userId, usedIdsForUser);
+ }
+
+ final Set<Integer> usedIdsForPackage =
+ usedIdsForUser.computeIfAbsent(packageName, it -> new HashSet<>());
+ usedIdsForPackage.add(associationId);
+ }
+ }
+
class CompanionDeviceManagerImpl extends ICompanionDeviceManager.Stub {
@Override
@@ -421,8 +414,7 @@
checkUsesFeature(packageName, getCallingUserId());
}
- return new ArrayList<>(
- CompanionDeviceManagerService.this.getAssociations(userId, packageName));
+ return mAssociationStore.getAssociationsForPackage(userId, packageName);
}
@Override
@@ -430,8 +422,7 @@
enforceCallerCanInteractWithUserId(getContext(), userId);
enforceCallerCanManageCompanionDevice(getContext(), "getAllAssociationsForUser");
- return new ArrayList<>(
- CompanionDeviceManagerService.this.getAllAssociationsForUser(userId));
+ return mAssociationStore.getAssociationsForUser(userId);
}
@Override
@@ -441,13 +432,17 @@
enforceCallerCanManageCompanionDevice(getContext(),
"addOnAssociationsChangedListener");
- //TODO: Implement.
+ mListeners.register(listener, userId);
}
@Override
public void removeOnAssociationsChangedListener(IOnAssociationsChangedListener listener,
int userId) {
- //TODO: Implement.
+ enforceCallerCanInteractWithUserId(getContext(), userId);
+ enforceCallerCanManageCompanionDevice(
+ getContext(), "removeOnAssociationsChangedListener");
+
+ mListeners.unregister(listener);
}
@Override
@@ -463,7 +458,7 @@
+ "(ie. it belongs to a different package or a different user).");
}
- disassociateInternal(userId, association.getId());
+ disassociateInternal(association.getId());
}
@Override
@@ -476,7 +471,7 @@
+ "or belongs to a different user");
}
- disassociateInternal(association.getUserId(), associationId);
+ disassociateInternal(associationId);
}
@Override
@@ -533,7 +528,7 @@
return true;
}
- return any(CompanionDeviceManagerService.this.getAssociations(userId, packageName),
+ return any(mAssociationStore.getAssociationsForPackage(userId, packageName),
a -> a.isLinkedTo(macAddress));
}
@@ -616,25 +611,18 @@
final int userId = getCallingUserId();
enforceCallerIsSystemOr(userId, packageName);
- Set<AssociationInfo> deviceAssociations = filter(
- CompanionDeviceManagerService.this.getAssociations(userId, packageName),
- a -> a.isLinkedTo(deviceAddress));
+ final AssociationInfo association =
+ mAssociationStore.getAssociationsForPackageWithAddress(
+ userId, packageName, deviceAddress);
- if (deviceAssociations.isEmpty()) {
+ if (association == null) {
throw new RemoteException(new DeviceNotAssociatedException("App " + packageName
+ " is not associated with device " + deviceAddress
+ " for user " + userId));
}
- updateAssociations(associations -> map(associations, association -> {
- if (association.belongsToPackage(userId, packageName)
- && association.isLinkedTo(deviceAddress)) {
- association.setNotifyOnDeviceNearby(active);
- }
- return association;
- }), userId);
-
- restartBleScan();
+ association.setNotifyOnDeviceNearby(active);
+ mAssociationStore.updateAssociation(association);
}
@Override
@@ -657,14 +645,16 @@
enforceCallerIsSystemOr(userId, callingPackage);
checkState(!ArrayUtils.isEmpty(
- CompanionDeviceManagerService.this.getAssociations(userId, callingPackage)),
+ mAssociationStore.getAssociationsForPackage(userId, callingPackage)),
"App must have an association before calling this API");
checkUsesFeature(callingPackage, userId);
}
@Override
public boolean canPairWithoutPrompt(String packageName, String macAddress, int userId) {
- final AssociationInfo association = getAssociation(userId, packageName, macAddress);
+ final AssociationInfo association =
+ mAssociationStore.getAssociationsForPackageWithAddress(
+ userId, packageName, macAddress);
if (association == null) {
return false;
}
@@ -677,7 +667,8 @@
String[] args, ShellCallback callback, ResultReceiver resultReceiver)
throws RemoteException {
enforceCallerCanManageCompanionDevice(getContext(), "onShellCommand");
- new CompanionDeviceShellCommand(CompanionDeviceManagerService.this)
+ new CompanionDeviceShellCommand(
+ CompanionDeviceManagerService.this, mAssociationStore)
.exec(this, in, out, err, args, callback, resultReceiver);
}
@@ -690,13 +681,8 @@
}
fout.append("Companion Device Associations:").append('\n');
- synchronized (mLock) {
- for (UserInfo user : getAllUsers()) {
- forEach(mCachedAssociations.get(user.id), a -> {
- fout.append(" ").append(a.toString()).append('\n');
- });
- }
-
+ for (AssociationInfo a : mAssociationStore.getAssociations()) {
+ fout.append(" ").append(a.toString()).append('\n');
}
fout.append("Currently Connected Devices:").append('\n');
@@ -747,31 +733,56 @@
@Nullable String deviceProfile, boolean selfManaged) {
final int id = getNewAssociationIdForPackage(userId, packageName);
final long timestamp = System.currentTimeMillis();
+
final AssociationInfo association = new AssociationInfo(id, userId, packageName,
macAddress, displayName, deviceProfile, selfManaged, false, timestamp);
+ Slog.i(LOG_TAG, "New CDM association created=" + association);
+ mAssociationStore.addAssociation(association);
updateSpecialAccessPermissionForAssociatedPackage(association);
- recordAssociation(association, userId);
return association;
}
- @GuardedBy("mLock")
+ @NonNull
+ private Map<String, Set<Integer>> getPreviouslyUsedIdsForUser(@UserIdInt int userId) {
+ synchronized (mPreviouslyUsedIds) {
+ return getPreviouslyUsedIdsForUserLocked(userId);
+ }
+ }
+
+ @GuardedBy("mPreviouslyUsedIds")
+ @NonNull
+ private Map<String, Set<Integer>> getPreviouslyUsedIdsForUserLocked(@UserIdInt int userId) {
+ final Map<String, Set<Integer>> usedIdsForUser = mPreviouslyUsedIds.get(userId);
+ if (usedIdsForUser == null) {
+ return Collections.emptyMap();
+ }
+ return deepUnmodifiableCopy(usedIdsForUser);
+ }
+
+ @GuardedBy("mPreviouslyUsedIds")
@NonNull
private Set<Integer> getPreviouslyUsedIdsForPackageLocked(
@UserIdInt int userId, @NonNull String packageName) {
- final Set<Integer> previouslyUsedIds = mPreviouslyUsedIds.get(userId).get(packageName);
- if (previouslyUsedIds != null) return previouslyUsedIds;
- return emptySet();
+ // "Deeply unmodifiable" map: the map itself and the Set<Integer> values it contains are all
+ // unmodifiable.
+ final Map<String, Set<Integer>> usedIdsForUser = getPreviouslyUsedIdsForUserLocked(userId);
+ final Set<Integer> usedIdsForPackage = usedIdsForUser.get(packageName);
+
+ if (usedIdsForPackage == null) {
+ return Collections.emptySet();
+ }
+
+ //The set is already unmodifiable.
+ return usedIdsForPackage;
}
private int getNewAssociationIdForPackage(@UserIdInt int userId, @NonNull String packageName) {
- synchronized (mLock) {
- readPersistedStateForUserIfNeededLocked(userId);
-
+ synchronized (mPreviouslyUsedIds) {
// First: collect all IDs currently in use for this user's Associations.
final SparseBooleanArray usedIds = new SparseBooleanArray();
- for (AssociationInfo it : getAllAssociationsForUser(userId)) {
+ for (AssociationInfo it : mAssociationStore.getAssociationsForUser(userId)) {
usedIds.put(it.getId(), true);
}
@@ -797,41 +808,14 @@
}
}
- //TODO also revoke notification access
- void disassociateInternal(@UserIdInt int userId, int associationId) {
- updateAssociations(associations ->
- filterOut(associations, it -> {
- if (it.getId() != associationId) return false;
-
- onAssociationPreRemove(it);
- markIdAsPreviouslyUsedForPackage(
- it.getId(), it.getUserId(), it.getPackageName());
- return true;
- }), userId);
-
- restartBleScan();
+ //TODO: also revoke notification access
+ void disassociateInternal(int associationId) {
+ onAssociationPreRemove(associationId);
+ mAssociationStore.removeAssociation(associationId);
}
- void clearAssociationForPackage(@UserIdInt int userId, @NonNull String packageName) {
- if (DEBUG) Slog.d(LOG_TAG, "clearAssociationForPackage() u" + userId + "/" + packageName);
-
- mCompanionDevicePresenceController.unbindDevicePresenceListener(packageName, userId);
- updateAssociations(set -> filterOut(set, it -> it.belongsToPackage(userId, packageName)),
- userId);
- }
-
- private void markIdAsPreviouslyUsedForPackage(
- int associationId, @UserIdInt int userId, @NonNull String packageName) {
- synchronized (mLock) {
- // Mark as previously used.
- readPersistedStateForUserIfNeededLocked(userId);
- mPreviouslyUsedIds.get(userId)
- .computeIfAbsent(packageName, it -> new HashSet<>())
- .add(associationId);
- }
- }
-
- void onAssociationPreRemove(AssociationInfo association) {
+ void onAssociationPreRemove(int associationId) {
+ final AssociationInfo association = mAssociationStore.getAssociationById(associationId);
if (association.isNotifyOnDeviceNearby()
|| (association.isSelfManaged()
&& mPresentSelfManagedDevices.contains(association.getId()))) {
@@ -842,7 +826,7 @@
String deviceProfile = association.getDeviceProfile();
if (deviceProfile != null) {
AssociationInfo otherAssociationWithDeviceProfile = find(
- getAllAssociationsForUser(association.getUserId()),
+ mAssociationStore.getAssociationsForUser(association.getUserId()),
a -> !a.equals(association) && deviceProfile.equals(a.getDeviceProfile()));
if (otherAssociationWithDeviceProfile != null) {
Slog.i(LOG_TAG, "Not revoking " + deviceProfile
@@ -934,37 +918,7 @@
.getPackageInfoAsUser(packageName, flags , userId));
}
- private void recordAssociation(AssociationInfo association, int userId) {
- Slog.i(LOG_TAG, "recordAssociation(" + association + ")");
- updateAssociations(associations -> add(associations, association), userId);
- }
-
- private void updateAssociations(Function<Set<AssociationInfo>, Set<AssociationInfo>> update,
- int userId) {
- synchronized (mLock) {
- if (DEBUG) Slog.d(LOG_TAG, "Updating Associations set...");
-
- final Set<AssociationInfo> prevAssociations = getAllAssociationsForUser(userId);
- if (DEBUG) Slog.d(LOG_TAG, " > Before : " + prevAssociations + "...");
-
- final Set<AssociationInfo> updatedAssociations = update.apply(
- new ArraySet<>(prevAssociations));
- if (DEBUG) Slog.d(LOG_TAG, " > After: " + updatedAssociations);
-
- mCachedAssociations.put(userId, unmodifiableSet(updatedAssociations));
-
- BackgroundThread.getHandler().sendMessage(
- PooledLambda.obtainMessage(
- (associations, usedIds) ->
- mPersistentDataStore
- .persistStateForUser(userId, associations, usedIds),
- updatedAssociations, deepCopy(mPreviouslyUsedIds.get(userId))));
-
- updateAtm(userId, updatedAssociations);
- }
- }
-
- private void updateAtm(int userId, Set<AssociationInfo> associations) {
+ private void updateAtm(int userId, List<AssociationInfo> associations) {
final Set<Integer> companionAppUids = new ArraySet<>();
for (AssociationInfo association : associations) {
final int uid = mPackageManagerInternal.getPackageUid(association.getPackageName(),
@@ -979,50 +933,18 @@
}
}
- @GuardedBy("mLock")
- private void readPersistedStateForUserIfNeededLocked(@UserIdInt int userId) {
- if (mCachedAssociations.get(userId) != null) return;
-
- Slog.i(LOG_TAG, "Reading state for user " + userId + " from the disk");
-
- final Set<AssociationInfo> associations = new ArraySet<>();
- final Map<String, Set<Integer>> previouslyUsedIds = new ArrayMap<>();
- mPersistentDataStore.readStateForUser(userId, associations, previouslyUsedIds);
-
- if (DEBUG) {
- Slog.d(LOG_TAG, " > associations=" + associations + "\n"
- + " > previouslyUsedIds=" + previouslyUsedIds);
- }
-
- mCachedAssociations.put(userId, unmodifiableSet(associations));
- mPreviouslyUsedIds.append(userId, previouslyUsedIds);
- }
-
- private List<UserInfo> getAllUsers() {
- final long identity = Binder.clearCallingIdentity();
- try {
- return mUserManager.getUsers();
- } finally {
- Binder.restoreCallingIdentity(identity);
- }
- }
-
void onDeviceConnected(String address) {
Slog.d(LOG_TAG, "onDeviceConnected(address = " + address + ")");
mCurrentlyConnectedDevices.add(address);
- for (UserInfo user : getAllUsers()) {
- for (AssociationInfo association : getAllAssociationsForUser(user.id)) {
- if (association.isLinkedTo(address)) {
- if (association.getDeviceProfile() != null) {
- Slog.i(LOG_TAG, "Granting role " + association.getDeviceProfile()
- + " to " + association.getPackageName()
- + " due to device connected: " + association.getDeviceMacAddress());
+ for (AssociationInfo association : mAssociationStore.getAssociationsByAddress(address)) {
+ if (association.getDeviceProfile() != null) {
+ Slog.i(LOG_TAG, "Granting role " + association.getDeviceProfile()
+ + " to " + association.getPackageName()
+ + " due to device connected: " + association.getDeviceMacAddress());
- addRoleHolderForAssociation(getContext(), association);
- }
- }
+ addRoleHolderForAssociation(getContext(), association);
}
}
@@ -1115,7 +1037,9 @@
Date lastNearby = mDevicesLastNearby.valueAt(i);
if (isDeviceDisappeared(lastNearby)) {
- for (AssociationInfo association : getAllAssociations(address)) {
+ final List<AssociationInfo> associations =
+ mAssociationStore.getAssociationsByAddress(address);
+ for (AssociationInfo association : associations) {
if (association.isNotifyOnDeviceNearby()) {
mCompanionDevicePresenceController.unbindDevicePresenceListener(
association.getPackageName(), association.getUserId());
@@ -1157,20 +1081,6 @@
}
}
- private Set<AssociationInfo> getAllAssociations(String deviceAddress) {
- List<UserInfo> aliveUsers = mUserManager.getAliveUsers();
- Set<AssociationInfo> result = new ArraySet<>();
- for (int i = 0, size = aliveUsers.size(); i < size; i++) {
- UserInfo user = aliveUsers.get(i);
- for (AssociationInfo association : getAllAssociationsForUser(user.id)) {
- if (association.isLinkedTo(deviceAddress)) {
- result.add(association);
- }
- }
- }
- return result;
- }
-
private void onDeviceNearby(String address) {
Date timestamp = new Date();
Date oldTimestamp = mDevicesLastNearby.put(address, timestamp);
@@ -1186,7 +1096,9 @@
|| timestamp.getTime() - oldTimestamp.getTime() >= DEVICE_DISAPPEARED_TIMEOUT_MS;
if (justAppeared) {
Slog.i(LOG_TAG, "onDeviceNearby(justAppeared, address = " + address + ")");
- for (AssociationInfo association : getAllAssociations(address)) {
+ final List<AssociationInfo> associations =
+ mAssociationStore.getAssociationsByAddress(address);
+ for (AssociationInfo association : associations) {
if (association.isNotifyOnDeviceNearby()) {
mCompanionDevicePresenceController.onDeviceNotifyAppeared(association,
getContext(), mMainHandler);
@@ -1199,7 +1111,9 @@
Slog.i(LOG_TAG, "onDeviceDisappeared(address = " + address + ")");
boolean hasDeviceListeners = false;
- for (AssociationInfo association : getAllAssociations(address)) {
+ final List<AssociationInfo> associations =
+ mAssociationStore.getAssociationsByAddress(address);
+ for (AssociationInfo association : associations) {
if (association.isNotifyOnDeviceNearby()) {
mCompanionDevicePresenceController.onDeviceNotifyDisappeared(
association, getContext(), mMainHandler);
@@ -1272,7 +1186,7 @@
private List<ScanFilter> getBleScanFilters() {
ArrayList<ScanFilter> result = new ArrayList<>();
ArraySet<String> addressesSeen = new ArraySet<>();
- for (AssociationInfo association : getAllAssociations()) {
+ for (AssociationInfo association : mAssociationStore.getAssociations()) {
if (association.isSelfManaged()) {
continue;
}
@@ -1312,17 +1226,6 @@
}
}
- private static @NonNull <T> Set<T> filterOut(
- @NonNull Set<T> set, @NonNull Predicate<? super T> predicate) {
- return CollectionUtils.filter(set, predicate.negate());
- }
-
- private Map<String, Set<Integer>> deepCopy(Map<String, Set<Integer>> orig) {
- final Map<String, Set<Integer>> copy = new HashMap<>(orig.size(), 1f);
- forEach(orig, (key, value) -> copy.put(key, new ArraySet<>(value)));
- return copy;
- }
-
void checkUsesFeature(@NonNull String pkg, @UserIdInt int userId) {
if (getCallingUid() == SYSTEM_UID) return;
@@ -1337,4 +1240,61 @@
+ FEATURE_COMPANION_DEVICE_SETUP
+ " in manifest to use this API");
}
+
+ private void registerPackageMonitor() {
+ new PackageMonitor() {
+ @Override
+ public void onPackageRemoved(String packageName, int uid) {
+ final int userId = getChangingUserId();
+ Slog.i(LOG_TAG, "onPackageRemoved() u" + userId + "/" + packageName);
+
+ clearAssociationForPackage(userId, packageName);
+ }
+
+ @Override
+ public void onPackageDataCleared(String packageName, int uid) {
+ final int userId = getChangingUserId();
+ Slog.i(LOG_TAG, "onPackageDataCleared() u" + userId + "/" + packageName);
+
+ clearAssociationForPackage(userId, packageName);
+ }
+
+ @Override
+ public void onPackageModified(String packageName) {
+ final int userId = getChangingUserId();
+ Slog.i(LOG_TAG, "onPackageModified() u" + userId + "/" + packageName);
+
+ final List<AssociationInfo> associationsForPackage =
+ mAssociationStore.getAssociationsForPackage(userId, packageName);
+ for (AssociationInfo association : associationsForPackage) {
+ updateSpecialAccessPermissionForAssociatedPackage(association);
+ }
+ }
+ }.register(getContext(), FgThread.get().getLooper(), UserHandle.ALL, true);
+ }
+
+ private void clearAssociationForPackage(@UserIdInt int userId, @NonNull String packageName) {
+ if (DEBUG) Slog.d(LOG_TAG, "clearAssociationForPackage() u" + userId + "/" + packageName);
+
+ // First, unbind CompanionService if needed.
+ mCompanionDevicePresenceController.unbindDevicePresenceListener(packageName, userId);
+
+ // Clear associations.
+ final List<AssociationInfo> associationsForPackage =
+ mAssociationStore.getAssociationsForPackage(userId, packageName);
+ for (AssociationInfo association : associationsForPackage) {
+ mAssociationStore.removeAssociation(association.getId());
+ }
+ }
+
+ private static Map<String, Set<Integer>> deepUnmodifiableCopy(Map<String, Set<Integer>> orig) {
+ final Map<String, Set<Integer>> copy = new HashMap<>();
+
+ for (Map.Entry<String, Set<Integer>> entry : orig.entrySet()) {
+ final Set<Integer> valueCopy = new HashSet<>(entry.getValue());
+ copy.put(entry.getKey(), Collections.unmodifiableSet(valueCopy));
+ }
+
+ return Collections.unmodifiableMap(copy);
+ }
}
diff --git a/services/companion/java/com/android/server/companion/CompanionDeviceShellCommand.java b/services/companion/java/com/android/server/companion/CompanionDeviceShellCommand.java
index 5cb3079..5c0571d 100644
--- a/services/companion/java/com/android/server/companion/CompanionDeviceShellCommand.java
+++ b/services/companion/java/com/android/server/companion/CompanionDeviceShellCommand.java
@@ -16,7 +16,6 @@
package com.android.server.companion;
-import static com.android.internal.util.CollectionUtils.forEach;
import static com.android.server.companion.CompanionDeviceManagerService.LOG_TAG;
import android.companion.AssociationInfo;
@@ -24,24 +23,33 @@
import android.util.Slog;
import java.io.PrintWriter;
+import java.util.List;
class CompanionDeviceShellCommand extends android.os.ShellCommand {
private final CompanionDeviceManagerService mService;
+ private final AssociationStore mAssociationStore;
- CompanionDeviceShellCommand(CompanionDeviceManagerService service) {
+ CompanionDeviceShellCommand(CompanionDeviceManagerService service,
+ AssociationStore associationStore) {
mService = service;
+ mAssociationStore = associationStore;
}
@Override
public int onCommand(String cmd) {
+ final PrintWriter out = getOutPrintWriter();
try {
switch (cmd) {
case "list": {
- forEach(
- mService.getAllAssociationsForUser(getNextArgInt()),
- a -> getOutPrintWriter()
- .println(a.getPackageName() + " "
- + a.getDeviceMacAddress()));
+ final int userId = getNextArgInt();
+ final List<AssociationInfo> associationsForUser =
+ mAssociationStore.getAssociationsForUser(userId);
+ for (AssociationInfo association : associationsForUser) {
+ // TODO(b/212535524): use AssociationInfo.toShortString(), once it's not
+ // longer referenced in tests.
+ out.println(association.getPackageName() + " "
+ + association.getDeviceMacAddress());
+ }
}
break;
@@ -60,7 +68,7 @@
final AssociationInfo association =
mService.getAssociationWithCallerChecks(userId, packageName, address);
if (association != null) {
- mService.disassociateInternal(userId, association.getId());
+ mService.disassociateInternal(association.getId());
}
}
break;
diff --git a/services/companion/java/com/android/server/companion/PersistentDataStore.java b/services/companion/java/com/android/server/companion/PersistentDataStore.java
index 87558df..97ec3bb 100644
--- a/services/companion/java/com/android/server/companion/PersistentDataStore.java
+++ b/services/companion/java/com/android/server/companion/PersistentDataStore.java
@@ -33,15 +33,18 @@
import android.annotation.Nullable;
import android.annotation.UserIdInt;
import android.companion.AssociationInfo;
+import android.content.pm.UserInfo;
import android.net.MacAddress;
import android.os.Environment;
+import android.util.ArrayMap;
import android.util.AtomicFile;
-import android.util.ExceptionUtils;
import android.util.Slog;
+import android.util.SparseArray;
import android.util.TypedXmlPullParser;
import android.util.TypedXmlSerializer;
import android.util.Xml;
+import com.android.internal.util.FunctionalUtils.ThrowingConsumer;
import com.android.internal.util.XmlUtils;
import org.xmlpull.v1.XmlPullParser;
@@ -50,8 +53,11 @@
import java.io.File;
import java.io.FileInputStream;
+import java.io.FileOutputStream;
import java.io.IOException;
+import java.util.Collection;
import java.util.HashSet;
+import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.ConcurrentHashMap;
@@ -68,8 +74,8 @@
*
* Before Android T the data was stored using the v0 schema.
*
- * @see #readAssociationsV0(TypedXmlPullParser, int, Set)
- * @see #readAssociationV0(TypedXmlPullParser, int, int, Set)
+ * @see #readAssociationsV0(TypedXmlPullParser, int, Collection)
+ * @see #readAssociationV0(TypedXmlPullParser, int, int, Collection)
*
* The following snippet is a sample of a the file that is using v0 schema.
* <pre>{@code
@@ -100,8 +106,8 @@
* optional.
*
* @see #CURRENT_PERSISTENCE_VERSION
- * @see #readAssociationsV1(TypedXmlPullParser, int, Set)
- * @see #readAssociationV1(TypedXmlPullParser, int, Set)
+ * @see #readAssociationsV1(TypedXmlPullParser, int, Collection)
+ * @see #readAssociationV1(TypedXmlPullParser, int, Collection)
* @see #readPreviouslyUsedIdsV1(TypedXmlPullParser, Map)
*
* The following snippet is a sample of a the file that is using v0 schema.
@@ -168,6 +174,23 @@
private final @NonNull ConcurrentMap<Integer, AtomicFile> mUserIdToStorageFile =
new ConcurrentHashMap<>();
+ void readStateForUsers(@NonNull List<UserInfo> users,
+ @NonNull Set<AssociationInfo> allAssociationsOut,
+ @NonNull SparseArray<Map<String, Set<Integer>>> previouslyUsedIdsPerUserOut) {
+ for (UserInfo user : users) {
+ final int userId = user.id;
+ // Previously used IDs are stored in the "out" collection per-user.
+ final Map<String, Set<Integer>> previouslyUsedIds = new ArrayMap<>();
+
+ // Associations for all users are stored in a single "flat" set: so we read directly
+ // into it.
+ readStateForUser(userId, allAssociationsOut, previouslyUsedIds);
+
+ // Save previously used IDs for this user into the "out" structure.
+ previouslyUsedIdsPerUserOut.append(userId, previouslyUsedIds);
+ }
+ }
+
/**
* Reads previously persisted data for the given user "into" the provided containers.
*
@@ -176,7 +199,7 @@
* @param previouslyUsedIdsPerPackageOut a container to read the used IDs "into".
*/
void readStateForUser(@UserIdInt int userId,
- @NonNull Set<AssociationInfo> associationsOut,
+ @NonNull Collection<AssociationInfo> associationsOut,
@NonNull Map<String, Set<Integer>> previouslyUsedIdsPerPackageOut) {
Slog.i(LOG_TAG, "Reading associations for user " + userId + " from disk");
final AtomicFile file = getStorageFileForUser(userId);
@@ -237,7 +260,8 @@
* @param associations a set of user's associations.
* @param previouslyUsedIdsPerPackage a set previously used Association IDs for the user.
*/
- void persistStateForUser(@UserIdInt int userId, @NonNull Set<AssociationInfo> associations,
+ void persistStateForUser(@UserIdInt int userId,
+ @NonNull Collection<AssociationInfo> associations,
@NonNull Map<String, Set<Integer>> previouslyUsedIdsPerPackage) {
Slog.i(LOG_TAG, "Writing associations for user " + userId + " to disk");
if (DEBUG) Slog.d(LOG_TAG, " > " + associations);
@@ -250,7 +274,7 @@
}
private int readStateFromFileLocked(@UserIdInt int userId, @NonNull AtomicFile file,
- @NonNull String rootTag, @Nullable Set<AssociationInfo> associationsOut,
+ @NonNull String rootTag, @Nullable Collection<AssociationInfo> associationsOut,
@NonNull Map<String, Set<Integer>> previouslyUsedIdsPerPackageOut) {
try (FileInputStream in = file.openRead()) {
final TypedXmlPullParser parser = Xml.resolvePullParser(in);
@@ -282,28 +306,25 @@
}
private void persistStateToFileLocked(@NonNull AtomicFile file,
- @Nullable Set<AssociationInfo> associations,
+ @Nullable Collection<AssociationInfo> associations,
@NonNull Map<String, Set<Integer>> previouslyUsedIdsPerPackage) {
- file.write(out -> {
- try {
- final TypedXmlSerializer serializer = Xml.resolveSerializer(out);
- serializer.setFeature(
- "http://xmlpull.org/v1/doc/features.html#indent-output", true);
+ // Writing to file could fail, for example, if the user has been recently removed and so was
+ // their DE (/data/system_de/<user-id>/) directory.
+ writeToFileSafely(file, out -> {
+ final TypedXmlSerializer serializer = Xml.resolveSerializer(out);
+ serializer.setFeature(
+ "http://xmlpull.org/v1/doc/features.html#indent-output", true);
- serializer.startDocument(null, true);
- serializer.startTag(null, XML_TAG_STATE);
- writeIntAttribute(serializer,
- XML_ATTR_PERSISTENCE_VERSION, CURRENT_PERSISTENCE_VERSION);
+ serializer.startDocument(null, true);
+ serializer.startTag(null, XML_TAG_STATE);
+ writeIntAttribute(serializer,
+ XML_ATTR_PERSISTENCE_VERSION, CURRENT_PERSISTENCE_VERSION);
- writeAssociations(serializer, associations);
- writePreviouslyUsedIds(serializer, previouslyUsedIdsPerPackage);
+ writeAssociations(serializer, associations);
+ writePreviouslyUsedIds(serializer, previouslyUsedIdsPerPackage);
- serializer.endTag(null, XML_TAG_STATE);
- serializer.endDocument();
- } catch (Exception e) {
- Slog.e(LOG_TAG, "Error while writing associations file", e);
- throw ExceptionUtils.propagate(e);
- }
+ serializer.endTag(null, XML_TAG_STATE);
+ serializer.endDocument();
});
}
@@ -321,7 +342,7 @@
}
private static void readAssociationsV0(@NonNull TypedXmlPullParser parser,
- @UserIdInt int userId, @NonNull Set<AssociationInfo> out)
+ @UserIdInt int userId, @NonNull Collection<AssociationInfo> out)
throws XmlPullParserException, IOException {
requireStartOfTag(parser, XML_TAG_ASSOCIATIONS);
@@ -342,7 +363,8 @@
}
private static void readAssociationV0(@NonNull TypedXmlPullParser parser, @UserIdInt int userId,
- int associationId, @NonNull Set<AssociationInfo> out) throws XmlPullParserException {
+ int associationId, @NonNull Collection<AssociationInfo> out)
+ throws XmlPullParserException {
requireStartOfTag(parser, XML_TAG_ASSOCIATION);
final String appPackage = readStringAttribute(parser, XML_ATTR_PACKAGE);
@@ -360,7 +382,7 @@
}
private static void readAssociationsV1(@NonNull TypedXmlPullParser parser,
- @UserIdInt int userId, @NonNull Set<AssociationInfo> out)
+ @UserIdInt int userId, @NonNull Collection<AssociationInfo> out)
throws XmlPullParserException, IOException {
requireStartOfTag(parser, XML_TAG_ASSOCIATIONS);
@@ -374,7 +396,7 @@
}
private static void readAssociationV1(@NonNull TypedXmlPullParser parser, @UserIdInt int userId,
- @NonNull Set<AssociationInfo> out) throws XmlPullParserException, IOException {
+ @NonNull Collection<AssociationInfo> out) throws XmlPullParserException, IOException {
requireStartOfTag(parser, XML_TAG_ASSOCIATION);
final int associationId = readIntAttribute(parser, XML_ATTR_ID);
@@ -421,9 +443,11 @@
}
private static void writeAssociations(@NonNull XmlSerializer parent,
- @Nullable Set<AssociationInfo> associations) throws IOException {
+ @Nullable Collection<AssociationInfo> associations) throws IOException {
final XmlSerializer serializer = parent.startTag(null, XML_TAG_ASSOCIATIONS);
- forEach(associations, it -> writeAssociation(serializer, it));
+ for (AssociationInfo association : associations) {
+ writeAssociation(serializer, association);
+ }
serializer.endTag(null, XML_TAG_ASSOCIATIONS);
}
@@ -498,4 +522,13 @@
}
return associationInfo;
}
+
+ private static void writeToFileSafely(@NonNull AtomicFile file,
+ @NonNull ThrowingConsumer<FileOutputStream> consumer) {
+ try {
+ file.write(consumer);
+ } catch (Exception e) {
+ Slog.e(LOG_TAG, "Error while writing to file " + file, e);
+ }
+ }
}
diff --git a/services/core/java/android/content/pm/PackageManagerInternal.java b/services/core/java/android/content/pm/PackageManagerInternal.java
index 6fe2806..9b2948f 100644
--- a/services/core/java/android/content/pm/PackageManagerInternal.java
+++ b/services/core/java/android/content/pm/PackageManagerInternal.java
@@ -50,6 +50,7 @@
import com.android.server.pm.pkg.AndroidPackageApi;
import com.android.server.pm.pkg.PackageState;
import com.android.server.pm.pkg.PackageStateInternal;
+import com.android.server.pm.pkg.mutate.PackageStateMutator;
import java.io.IOException;
import java.lang.annotation.Retention;
@@ -885,17 +886,6 @@
public abstract boolean userNeedsBadging(int userId);
/**
- * Perform the given action for each package.
- * Note that packages lock will be held while performing the actions.
- *
- * If the caller does not need all packages, prefer the potentially non-locking
- * {@link #withPackageSettingsSnapshot(Consumer)}.
- *
- * @param actionLocked action to be performed
- */
- public abstract void forEachPackage(Consumer<AndroidPackage> actionLocked);
-
- /**
* Perform the given action for each {@link PackageSetting}.
* Note that packages lock will be held while performing the actions.
*
@@ -918,12 +908,24 @@
public abstract void forEachPackageState(boolean locked, Consumer<PackageStateInternal> action);
/**
+ * {@link #forEachPackageState(boolean, Consumer)} but filtered to only states with packages
+ * on device where {@link PackageStateInternal#getPkg()} is not null.
+ */
+ public abstract void forEachPackage(Consumer<AndroidPackage> action);
+
+ /**
* Perform the given action for each installed package for a user.
* Note that packages lock will be held while performing the actions.
*/
public abstract void forEachInstalledPackage(
@NonNull Consumer<AndroidPackage> actionLocked, @UserIdInt int userId);
+ /**
+ * Perform the given action for each installed package for a user.
+ */
+ public abstract void forEachInstalledPackage(boolean locked,
+ @NonNull Consumer<AndroidPackage> action, @UserIdInt int userId);
+
/** Returns the list of enabled components */
public abstract ArraySet<String> getEnabledComponents(String packageName, int userId);
@@ -1265,4 +1267,62 @@
*/
public abstract void reconcileAppsData(int userId, @StorageManager.StorageFlags int flags,
boolean migrateAppsData);
+
+ /**
+ * Initiates a package state mutation request, returning the current state as known by
+ * PackageManager. This allows the later commit request to compare the initial values and
+ * determine if any state was changed or any packages were updated since the whole request
+ * was initiated.
+ *
+ * As a concrete example, consider the following steps:
+ * <ol>
+ * <li>Read a package state without taking a lock</li>
+ * <li>Check some values in that state, determine that a mutation needs to occur</li>
+ * <li>Call to commit the change with the new value, takes lock</li>
+ * </ol>
+ *
+ * Between steps 1 and 3, because the lock was not taken for the entire flow, it's possible
+ * a package state was changed by another consumer or a package was updated/installed.
+ *
+ * If anything has changed,
+ * {@link #commitPackageStateMutation(PackageStateMutator.InitialState, Consumer)} will return
+ * a {@link PackageStateMutator.Result} indicating so. If the caller has not indicated it can
+ * ignore changes, it can opt to re-run the commit logic from the top with a true write lock
+ * around all of its read-logic-commit loop.
+ *
+ * Note that if the caller does not care about potential race conditions or package/state
+ * changes between steps 1 and 3, it can simply opt to not call this method and pass in null
+ * for the initial state. This is useful to avoid long running data structure locks when the
+ * caller is changing a value as part of a one-off request. Perhaps from an app side API which
+ * mutates only a single package, where it doesn't care what the state of that package is or
+ * any other packages on the devices.
+ *
+ * Important to note is that if no locking is enforced, callers themselves will not be
+ * synchronized with themselves. The caller may be relying on the PackageManager lock to
+ * enforce ordering within their own code path, and that has to be adjusted if migrated off
+ * the lock.
+ */
+ @NonNull
+ public abstract PackageStateMutator.InitialState recordInitialState();
+
+ /**
+ * Some questions to ask when designing a mutation:
+ * <ol>
+ * <li>What external system state is required and is it synchronized properly?</li>
+ * <li>Are there any package/state changes that could happen to the target (or another)
+ * package that could result in the commit being invalid?</li>
+ * <li>Is the caller synchronized with itself and can handle multiple mutations being
+ * requested from different threads?</li>
+ * <li>What should be done in case of a conflict and the commit can't be finished?</li>
+ * </ol>
+ *
+ * @param state See {@link #recordInitialState()}. If null, no result is returned.
+ * @param consumer Lean wrapper around just the logic that changes state values
+ * @return result if anything changed since initial state, or null if nothing changed and
+ * commit was successful
+ */
+ @Nullable
+ public abstract PackageStateMutator.Result commitPackageStateMutation(
+ @Nullable PackageStateMutator.InitialState state,
+ @NonNull Consumer<PackageStateMutator> consumer);
}
diff --git a/services/core/java/com/android/server/BluetoothManagerService.java b/services/core/java/com/android/server/BluetoothManagerService.java
index 6e058412..bc8da84 100644
--- a/services/core/java/com/android/server/BluetoothManagerService.java
+++ b/services/core/java/com/android/server/BluetoothManagerService.java
@@ -109,10 +109,6 @@
private static final String BLUETOOTH_PRIVILEGED =
android.Manifest.permission.BLUETOOTH_PRIVILEGED;
- private static final String SECURE_SETTINGS_BLUETOOTH_ADDR_VALID = "bluetooth_addr_valid";
- private static final String SECURE_SETTINGS_BLUETOOTH_ADDRESS = "bluetooth_address";
- private static final String SECURE_SETTINGS_BLUETOOTH_NAME = "bluetooth_name";
-
private static final int ACTIVE_LOG_MAX_SIZE = 20;
private static final int CRASH_LOG_MAX_SIZE = 100;
@@ -640,7 +636,7 @@
if (mContext.getResources()
.getBoolean(com.android.internal.R.bool.config_bluetooth_address_validation)
&& Settings.Secure.getIntForUser(mContentResolver,
- SECURE_SETTINGS_BLUETOOTH_ADDR_VALID, 0, mUserId)
+ Settings.Secure.BLUETOOTH_NAME, 0, mUserId)
== 0) {
// if the valid flag is not set, don't load the address and name
if (DBG) {
@@ -649,9 +645,9 @@
return;
}
mName = Settings.Secure.getStringForUser(
- mContentResolver, SECURE_SETTINGS_BLUETOOTH_NAME, mUserId);
+ mContentResolver, Settings.Secure.BLUETOOTH_NAME, mUserId);
mAddress = Settings.Secure.getStringForUser(
- mContentResolver, SECURE_SETTINGS_BLUETOOTH_ADDRESS, mUserId);
+ mContentResolver, Settings.Secure.BLUETOOTH_ADDRESS, mUserId);
if (DBG) {
Slog.d(TAG, "Stored bluetooth Name=" + mName + ",Address=" + mAddress);
}
@@ -665,30 +661,30 @@
*/
private void storeNameAndAddress(String name, String address) {
if (name != null) {
- Settings.Secure.putStringForUser(mContentResolver, SECURE_SETTINGS_BLUETOOTH_NAME, name,
+ Settings.Secure.putStringForUser(mContentResolver, Settings.Secure.BLUETOOTH_NAME, name,
mUserId);
mName = name;
if (DBG) {
Slog.d(TAG, "Stored Bluetooth name: " + Settings.Secure.getStringForUser(
- mContentResolver, SECURE_SETTINGS_BLUETOOTH_NAME,
+ mContentResolver, Settings.Secure.BLUETOOTH_NAME,
mUserId));
}
}
if (address != null) {
- Settings.Secure.putStringForUser(mContentResolver, SECURE_SETTINGS_BLUETOOTH_ADDRESS,
+ Settings.Secure.putStringForUser(mContentResolver, Settings.Secure.BLUETOOTH_ADDRESS,
address, mUserId);
mAddress = address;
if (DBG) {
Slog.d(TAG,
"Stored Bluetoothaddress: " + Settings.Secure.getStringForUser(
- mContentResolver, SECURE_SETTINGS_BLUETOOTH_ADDRESS,
+ mContentResolver, Settings.Secure.BLUETOOTH_ADDRESS,
mUserId));
}
}
if ((name != null) && (address != null)) {
- Settings.Secure.putIntForUser(mContentResolver, SECURE_SETTINGS_BLUETOOTH_ADDR_VALID, 1,
+ Settings.Secure.putIntForUser(mContentResolver, Settings.Secure.BLUETOOTH_ADDR_VALID, 1,
mUserId);
}
}
diff --git a/services/core/java/com/android/server/StorageManagerService.java b/services/core/java/com/android/server/StorageManagerService.java
index c7f4b4d..780afd8 100644
--- a/services/core/java/com/android/server/StorageManagerService.java
+++ b/services/core/java/com/android/server/StorageManagerService.java
@@ -80,6 +80,7 @@
import android.database.ContentObserver;
import android.net.Uri;
import android.os.Binder;
+import android.os.Build;
import android.os.DropBoxManager;
import android.os.Environment;
import android.os.Handler;
@@ -1648,7 +1649,8 @@
// obb data to its new location. This may take time depending on the size of
// the data to be copied so it's done on the StorageManager worker thread.
// This needs to be finished before start mounting obb directories.
- if (userId == 0) {
+ if (userId == 0
+ && Build.VERSION.DEVICE_INITIAL_SDK_INT < Build.VERSION_CODES.Q) {
mPmInternal.migrateLegacyObbData();
}
diff --git a/services/core/java/com/android/server/am/CachedAppOptimizer.java b/services/core/java/com/android/server/am/CachedAppOptimizer.java
index 0c94fbb..c55bbe8 100644
--- a/services/core/java/com/android/server/am/CachedAppOptimizer.java
+++ b/services/core/java/com/android/server/am/CachedAppOptimizer.java
@@ -920,7 +920,7 @@
void unfreezeTemporarily(ProcessRecord app) {
if (mUseFreezer) {
synchronized (mProcLock) {
- if (app.mOptRecord.isFrozen()) {
+ if (app.mOptRecord.isFrozen() || app.mOptRecord.isPendingFreeze()) {
unfreezeAppLSP(app);
freezeAppAsyncLSP(app);
}
diff --git a/services/core/java/com/android/server/audio/AudioService.java b/services/core/java/com/android/server/audio/AudioService.java
index 8615393..902cdb9 100644
--- a/services/core/java/com/android/server/audio/AudioService.java
+++ b/services/core/java/com/android/server/audio/AudioService.java
@@ -9771,7 +9771,7 @@
projection)) {
Slog.w(TAG, "Permission denied to register audio policy for pid "
+ Binder.getCallingPid() + " / uid " + Binder.getCallingUid()
- + ", need MODIFY_AUDIO_ROUTING or MediaProjection that can project audio");
+ + ", need system permission or a MediaProjection that can project audio");
return null;
}
diff --git a/services/core/java/com/android/server/biometrics/sensors/AcquisitionClient.java b/services/core/java/com/android/server/biometrics/sensors/AcquisitionClient.java
index 2465ec5..6f71768 100644
--- a/services/core/java/com/android/server/biometrics/sensors/AcquisitionClient.java
+++ b/services/core/java/com/android/server/biometrics/sensors/AcquisitionClient.java
@@ -38,8 +38,8 @@
private static final String TAG = "Biometrics/AcquisitionClient";
- private static final VibrationAttributes TOUCH_VIBRATION_ATTRIBUTES =
- VibrationAttributes.createForUsage(VibrationAttributes.USAGE_TOUCH);
+ private static final VibrationAttributes HARDWARE_FEEDBACK_VIBRATION_ATTRIBUTES =
+ VibrationAttributes.createForUsage(VibrationAttributes.USAGE_HARDWARE_FEEDBACK);
private static final VibrationEffect SUCCESS_VIBRATION_EFFECT =
VibrationEffect.get(VibrationEffect.EFFECT_CLICK);
@@ -196,7 +196,7 @@
getContext().getOpPackageName(),
SUCCESS_VIBRATION_EFFECT,
getClass().getSimpleName() + "::success",
- TOUCH_VIBRATION_ATTRIBUTES);
+ HARDWARE_FEEDBACK_VIBRATION_ATTRIBUTES);
}
}
@@ -207,7 +207,7 @@
getContext().getOpPackageName(),
ERROR_VIBRATION_EFFECT,
getClass().getSimpleName() + "::error",
- TOUCH_VIBRATION_ATTRIBUTES);
+ HARDWARE_FEEDBACK_VIBRATION_ATTRIBUTES);
}
}
}
diff --git a/services/core/java/com/android/server/communal/CommunalManagerService.java b/services/core/java/com/android/server/communal/CommunalManagerService.java
index df95bf5..1220391 100644
--- a/services/core/java/com/android/server/communal/CommunalManagerService.java
+++ b/services/core/java/com/android/server/communal/CommunalManagerService.java
@@ -341,7 +341,8 @@
UserHandle.SYSTEM,
mIntentFilter,
/* broadcastPermission= */null,
- /* scheduler= */ null);
+ /* scheduler= */ null,
+ Context.RECEIVER_EXPORTED_UNAUDITED);
}
private void unregister() {
diff --git a/services/core/java/com/android/server/display/DisplayManagerService.java b/services/core/java/com/android/server/display/DisplayManagerService.java
index c0a6abf..c4f2b14 100644
--- a/services/core/java/com/android/server/display/DisplayManagerService.java
+++ b/services/core/java/com/android/server/display/DisplayManagerService.java
@@ -714,7 +714,6 @@
display.getDisplayInfoLocked().shouldConstrainMetricsForLauncher;
if (display.setDisplayInfoOverrideFromWindowManagerLocked(info)) {
handleLogicalDisplayChangedLocked(display);
- scheduleTraversalLocked(false);
}
}
}
diff --git a/services/core/java/com/android/server/input/InputShellCommand.java b/services/core/java/com/android/server/input/InputShellCommand.java
index 9fa6fad..773dc68 100644
--- a/services/core/java/com/android/server/input/InputShellCommand.java
+++ b/services/core/java/com/android/server/input/InputShellCommand.java
@@ -560,7 +560,12 @@
sleep(duration);
for (KeyEvent event: events) {
- injectKeyEventAsync(KeyEvent.changeAction(event, KeyEvent.ACTION_UP));
+ final int keyCode = event.getKeyCode();
+ final KeyEvent upEvent = new KeyEvent(now, now, KeyEvent.ACTION_UP, keyCode,
+ 0, metaState, KeyCharacterMap.VIRTUAL_KEYBOARD, 0 /*scancode*/, 0 /*flags*/,
+ inputSource);
+ injectKeyEventAsync(upEvent);
+ metaState &= ~MODIFIER.getOrDefault(keyCode, 0);
}
}
diff --git a/services/core/java/com/android/server/inputmethod/ImfLock.java b/services/core/java/com/android/server/inputmethod/ImfLock.java
new file mode 100644
index 0000000..612c14f
--- /dev/null
+++ b/services/core/java/com/android/server/inputmethod/ImfLock.java
@@ -0,0 +1,45 @@
+/*
+ * 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.inputmethod;
+
+/**
+ * The implicit lock of this class serves as the global lock for
+ * the {@link InputMethodManagerService} and its controllers,
+ * which contain the main logic of the input method framework (IMF).
+ *
+ * <p>
+ * This lock can be used as follows in code:
+ * <pre>
+ * synchronized (ImfLock.class) {
+ * ...
+ * }
+ * </pre>
+ *
+ * <p>
+ * For annotations, you can use a similar syntax:
+ * <pre>
+ * @GuardedBy("ImfLock.class")
+ * myMethodDeclaration() {
+ * ...
+ * }
+ * </pre>
+ */
+final class ImfLock {
+ private ImfLock() {
+ // no instances
+ }
+}
diff --git a/services/core/java/com/android/server/inputmethod/InputMethodBindingController.java b/services/core/java/com/android/server/inputmethod/InputMethodBindingController.java
index c5dc23e..220d790 100644
--- a/services/core/java/com/android/server/inputmethod/InputMethodBindingController.java
+++ b/services/core/java/com/android/server/inputmethod/InputMethodBindingController.java
@@ -72,16 +72,16 @@
@NonNull private final WindowManagerInternal mWindowManagerInternal;
@NonNull private final Resources mRes;
- private long mLastBindTime;
- private boolean mHasConnection;
- @Nullable private String mCurId;
- @Nullable private String mSelectedMethodId;
- @Nullable private Intent mCurIntent;
- @Nullable private IInputMethod mCurMethod;
- private int mCurMethodUid = Process.INVALID_UID;
- private IBinder mCurToken;
- private int mCurSeq;
- private boolean mVisibleBound;
+ @GuardedBy("ImfLock.class") private long mLastBindTime;
+ @GuardedBy("ImfLock.class") private boolean mHasConnection;
+ @GuardedBy("ImfLock.class") @Nullable private String mCurId;
+ @GuardedBy("ImfLock.class") @Nullable private String mSelectedMethodId;
+ @GuardedBy("ImfLock.class") @Nullable private Intent mCurIntent;
+ @GuardedBy("ImfLock.class") @Nullable private IInputMethod mCurMethod;
+ @GuardedBy("ImfLock.class") private int mCurMethodUid = Process.INVALID_UID;
+ @GuardedBy("ImfLock.class") private IBinder mCurToken;
+ @GuardedBy("ImfLock.class") private int mCurSeq;
+ @GuardedBy("ImfLock.class") private boolean mVisibleBound;
private boolean mSupportsStylusHw;
/**
@@ -146,6 +146,7 @@
* Time that we last initiated a bind to the input method, to determine
* if we should try to disconnect and reconnect to it.
*/
+ @GuardedBy("ImfLock.class")
long getLastBindTime() {
return mLastBindTime;
}
@@ -154,6 +155,7 @@
* Set to true if our ServiceConnection is currently actively bound to
* a service (whether or not we have gotten its IBinder back yet).
*/
+ @GuardedBy("ImfLock.class")
boolean hasConnection() {
return mHasConnection;
}
@@ -166,6 +168,7 @@
*
* @see #getSelectedMethodId()
*/
+ @GuardedBy("ImfLock.class")
@Nullable
String getCurId() {
return mCurId;
@@ -184,11 +187,13 @@
*
* @see #getCurId()
*/
+ @GuardedBy("ImfLock.class")
@Nullable
String getSelectedMethodId() {
return mSelectedMethodId;
}
+ @GuardedBy("ImfLock.class")
void setSelectedMethodId(@Nullable String selectedMethodId) {
mSelectedMethodId = selectedMethodId;
}
@@ -197,6 +202,7 @@
* The token we have made for the currently active input method, to
* identify it in the future.
*/
+ @GuardedBy("ImfLock.class")
IBinder getCurToken() {
return mCurToken;
}
@@ -204,6 +210,7 @@
/**
* The Intent used to connect to the current input method.
*/
+ @GuardedBy("ImfLock.class")
@Nullable
Intent getCurIntent() {
return mCurIntent;
@@ -213,6 +220,7 @@
* The current binding sequence number, incremented every time there is
* a new bind performed.
*/
+ @GuardedBy("ImfLock.class")
int getSequenceNumber() {
return mCurSeq;
}
@@ -221,6 +229,7 @@
* Increase the current binding sequence number by one.
* Reset to 1 on overflow.
*/
+ @GuardedBy("ImfLock.class")
void advanceSequenceNumber() {
mCurSeq += 1;
if (mCurSeq <= 0) {
@@ -232,6 +241,7 @@
* If non-null, this is the input method service we are currently connected
* to.
*/
+ @GuardedBy("ImfLock.class")
@Nullable
IInputMethod getCurMethod() {
return mCurMethod;
@@ -240,6 +250,7 @@
/**
* If not {@link Process#INVALID_UID}, then the UID of {@link #getCurIntent()}.
*/
+ @GuardedBy("ImfLock.class")
int getCurMethodUid() {
return mCurMethodUid;
}
@@ -247,6 +258,7 @@
/**
* Indicates whether {@link #mVisibleConnection} is currently in use.
*/
+ @GuardedBy("ImfLock.class")
boolean isVisibleBound() {
return mVisibleBound;
}
@@ -254,11 +266,12 @@
/**
* Used to bring IME service up to visible adjustment while it is being shown.
*/
+ @GuardedBy("ImfLock.class")
private final ServiceConnection mVisibleConnection = new ServiceConnection() {
@Override public void onBindingDied(ComponentName name) {
- synchronized (mMethodMap) {
+ synchronized (ImfLock.class) {
if (mVisibleBound) {
- unbindVisibleConnectionLocked();
+ unbindVisibleConnection();
}
}
}
@@ -273,17 +286,18 @@
/**
* Used to bind the IME while it is not currently being shown.
*/
+ @GuardedBy("ImfLock.class")
private final ServiceConnection mMainConnection = new ServiceConnection() {
@Override
public void onServiceConnected(ComponentName name, IBinder service) {
Trace.traceBegin(TRACE_TAG_WINDOW_MANAGER, "IMMS.onServiceConnected");
- synchronized (mMethodMap) {
+ synchronized (ImfLock.class) {
if (mCurIntent != null && name.equals(mCurIntent.getComponent())) {
mCurMethod = IInputMethod.Stub.asInterface(service);
- updateCurrentMethodUidLocked();
+ updateCurrentMethodUid();
if (mCurToken == null) {
Slog.w(TAG, "Service connected without a token!");
- unbindCurrentMethodLocked();
+ unbindCurrentMethod();
Trace.traceEnd(TRACE_TAG_WINDOW_MANAGER);
return;
}
@@ -304,8 +318,8 @@
Trace.traceEnd(TRACE_TAG_WINDOW_MANAGER);
}
- @GuardedBy("mMethodMap")
- private void updateCurrentMethodUidLocked() {
+ @GuardedBy("ImfLock.class")
+ private void updateCurrentMethodUid() {
final String curMethodPackage = mCurIntent.getComponent().getPackageName();
final int curMethodUid = mPackageManagerInternal.getPackageUid(
curMethodPackage, 0 /* flags */, mSettings.getCurrentUserId());
@@ -330,7 +344,7 @@
// refreshed when this method is called back. Running
// adb install -r <APK that implements the current IME>
// would be a good way to trigger such a situation.
- synchronized (mMethodMap) {
+ synchronized (ImfLock.class) {
if (DEBUG) {
Slog.v(TAG, "Service disconnected: " + name + " mCurIntent=" + mCurIntent);
}
@@ -339,7 +353,7 @@
// We consider this to be a new bind attempt, since the system
// should now try to restart the service for us.
mLastBindTime = SystemClock.uptimeMillis();
- clearCurMethodAndSessionsLocked();
+ clearCurMethodAndSessions();
mService.clearInputShowRequestLocked();
mService.unbindCurrentClientLocked(UnbindReason.DISCONNECT_IME);
}
@@ -347,35 +361,35 @@
}
};
- @GuardedBy("mMethodMap")
- void unbindCurrentMethodLocked() {
+ @GuardedBy("ImfLock.class")
+ void unbindCurrentMethod() {
if (mVisibleBound) {
- unbindVisibleConnectionLocked();
+ unbindVisibleConnection();
}
if (mHasConnection) {
- unbindMainConnectionLocked();
+ unbindMainConnection();
}
if (mCurToken != null) {
- removeCurrentTokenLocked();
+ removeCurrentToken();
mService.resetSystemUiLocked();
}
mCurId = null;
- clearCurMethodAndSessionsLocked();
+ clearCurMethodAndSessions();
}
- @GuardedBy("mMethodMap")
- private void clearCurMethodAndSessionsLocked() {
+ @GuardedBy("ImfLock.class")
+ private void clearCurMethodAndSessions() {
mService.clearClientSessionsLocked();
mCurMethod = null;
mCurMethodUid = Process.INVALID_UID;
}
- @GuardedBy("mMethodMap")
- private void removeCurrentTokenLocked() {
- int curTokenDisplayId = mService.getCurTokenDisplayId();
+ @GuardedBy("ImfLock.class")
+ private void removeCurrentToken() {
+ int curTokenDisplayId = mService.getCurTokenDisplayIdLocked();
if (DEBUG) {
Slog.v(TAG,
@@ -386,9 +400,9 @@
mCurToken = null;
}
- @GuardedBy("mMethodMap")
+ @GuardedBy("ImfLock.class")
@NonNull
- InputBindResult bindCurrentMethodLocked() {
+ InputBindResult bindCurrentMethod() {
InputMethodInfo info = mMethodMap.get(mSelectedMethodId);
if (info == null) {
throw new IllegalArgumentException("Unknown id: " + mSelectedMethodId);
@@ -396,11 +410,11 @@
mCurIntent = createImeBindingIntent(info.getComponent());
- if (bindCurrentInputMethodServiceMainConnectionLocked()) {
+ if (bindCurrentInputMethodServiceMainConnection()) {
mCurId = info.getId();
mLastBindTime = SystemClock.uptimeMillis();
- addFreshWindowTokenLocked();
+ addFreshWindowToken();
return new InputBindResult(
InputBindResult.ResultCode.SUCCESS_WAITING_IME_BINDING,
null, null, mCurId, mCurSeq, false);
@@ -424,12 +438,12 @@
return intent;
}
- @GuardedBy("mMethodMap")
- private void addFreshWindowTokenLocked() {
- int displayIdToShowIme = mService.getDisplayIdToShowIme();
+ @GuardedBy("ImfLock.class")
+ private void addFreshWindowToken() {
+ int displayIdToShowIme = mService.getDisplayIdToShowImeLocked();
mCurToken = new Binder();
- mService.setCurTokenDisplayId(displayIdToShowIme);
+ mService.setCurTokenDisplayIdLocked(displayIdToShowIme);
try {
if (DEBUG) {
@@ -444,20 +458,20 @@
}
}
- @GuardedBy("mMethodMap")
- private void unbindMainConnectionLocked() {
+ @GuardedBy("ImfLock.class")
+ private void unbindMainConnection() {
mContext.unbindService(mMainConnection);
mHasConnection = false;
}
- @GuardedBy("mMethodMap")
- void unbindVisibleConnectionLocked() {
+ @GuardedBy("ImfLock.class")
+ void unbindVisibleConnection() {
mContext.unbindService(mVisibleConnection);
mVisibleBound = false;
}
- @GuardedBy("mMethodMap")
- private boolean bindCurrentInputMethodServiceLocked(ServiceConnection conn, int flags) {
+ @GuardedBy("ImfLock.class")
+ private boolean bindCurrentInputMethodService(ServiceConnection conn, int flags) {
if (mCurIntent == null || conn == null) {
Slog.e(TAG, "--- bind failed: service = " + mCurIntent + ", conn = " + conn);
return false;
@@ -466,16 +480,16 @@
new UserHandle(mSettings.getCurrentUserId()));
}
- @GuardedBy("mMethodMap")
- private boolean bindCurrentInputMethodServiceVisibleConnectionLocked() {
- mVisibleBound = bindCurrentInputMethodServiceLocked(mVisibleConnection,
+ @GuardedBy("ImfLock.class")
+ private boolean bindCurrentInputMethodServiceVisibleConnection() {
+ mVisibleBound = bindCurrentInputMethodService(mVisibleConnection,
IME_VISIBLE_BIND_FLAGS);
return mVisibleBound;
}
- @GuardedBy("mMethodMap")
- private boolean bindCurrentInputMethodServiceMainConnectionLocked() {
- mHasConnection = bindCurrentInputMethodServiceLocked(mMainConnection,
+ @GuardedBy("ImfLock.class")
+ private boolean bindCurrentInputMethodServiceMainConnection() {
+ mHasConnection = bindCurrentInputMethodService(mMainConnection,
mImeConnectionBindFlags);
return mHasConnection;
}
@@ -486,27 +500,36 @@
* <p>
* Performs a rebind if no binding is achieved in {@link #TIME_TO_RECONNECT} milliseconds.
*/
- @GuardedBy("mMethodMap")
- void setCurrentMethodVisibleLocked() {
+ @GuardedBy("ImfLock.class")
+ void setCurrentMethodVisible() {
if (mCurMethod != null) {
- if (DEBUG) Slog.d(TAG, "setCurrentMethodVisibleLocked: mCurToken=" + mCurToken);
+ if (DEBUG) Slog.d(TAG, "setCurrentMethodVisible: mCurToken=" + mCurToken);
if (mHasConnection && !mVisibleBound) {
- bindCurrentInputMethodServiceVisibleConnectionLocked();
+ bindCurrentInputMethodServiceVisibleConnection();
}
return;
}
+ // No IME is currently connected. Reestablish the main connection.
+ if (!mHasConnection) {
+ if (DEBUG) {
+ Slog.d(TAG, "Cannot show input: no IME bound. Rebinding.");
+ }
+ bindCurrentMethod();
+ return;
+ }
+
long bindingDuration = SystemClock.uptimeMillis() - mLastBindTime;
- if (mHasConnection && bindingDuration >= TIME_TO_RECONNECT) {
+ if (bindingDuration >= TIME_TO_RECONNECT) {
// The client has asked to have the input method shown, but
// we have been sitting here too long with a connection to the
// service and no interface received, so let's disconnect/connect
// to try to prod things along.
EventLog.writeEvent(EventLogTags.IMF_FORCE_RECONNECT_IME, getSelectedMethodId(),
bindingDuration, 1);
- Slog.w(TAG, "Force disconnect/connect to the IME in setCurrentMethodVisibleLocked()");
- unbindMainConnectionLocked();
- bindCurrentInputMethodServiceMainConnectionLocked();
+ Slog.w(TAG, "Force disconnect/connect to the IME in setCurrentMethodVisible()");
+ unbindMainConnection();
+ bindCurrentInputMethodServiceMainConnection();
} else {
if (DEBUG) {
Slog.d(TAG, "Can't show input: connection = " + mHasConnection + ", time = "
@@ -518,10 +541,10 @@
/**
* Remove the binding needed for the IME to be shown.
*/
- @GuardedBy("mMethodMap")
- void setCurrentMethodNotVisibleLocked() {
+ @GuardedBy("ImfLock.class")
+ void setCurrentMethodNotVisible() {
if (mVisibleBound) {
- unbindVisibleConnectionLocked();
+ unbindVisibleConnection();
}
}
}
diff --git a/services/core/java/com/android/server/inputmethod/InputMethodManagerService.java b/services/core/java/com/android/server/inputmethod/InputMethodManagerService.java
index da3729d..c87dc89 100644
--- a/services/core/java/com/android/server/inputmethod/InputMethodManagerService.java
+++ b/services/core/java/com/android/server/inputmethod/InputMethodManagerService.java
@@ -149,6 +149,7 @@
import com.android.internal.compat.IPlatformCompat;
import com.android.internal.content.PackageMonitor;
import com.android.internal.infra.AndroidFuture;
+import com.android.internal.inputmethod.DirectBootAwareness;
import com.android.internal.inputmethod.IInputContentUriToken;
import com.android.internal.inputmethod.IInputMethodPrivilegedOperations;
import com.android.internal.inputmethod.ImeTracing;
@@ -261,6 +262,16 @@
private static final String ACTION_SHOW_INPUT_METHOD_PICKER =
"com.android.server.inputmethod.InputMethodManagerService.SHOW_INPUT_METHOD_PICKER";
+ /**
+ * When set, {@link #startInputUncheckedLocked} will return
+ * {@link InputBindResult#NO_EDITOR} instead of starting an IME connection
+ * unless {@link StartInputFlags#IS_TEXT_EDITOR} is set. This behavior overrides
+ * {@link LayoutParams#SOFT_INPUT_STATE_VISIBLE SOFT_INPUT_STATE_VISIBLE} and
+ * {@link LayoutParams#SOFT_INPUT_STATE_ALWAYS_VISIBLE SOFT_INPUT_STATE_ALWAYS_VISIBLE}
+ * starting from {@link android.os.Build.VERSION_CODES#P}.
+ */
+ private final boolean mPreventImeStartupUnlessTextEditor;
+
@UserIdInt
private int mLastSwitchUserId;
@@ -292,9 +303,7 @@
@Nullable
private AudioManagerInternal mAudioManagerInternal = null;
-
- // All known input methods. mMethodMap also serves as the global
- // lock for this class.
+ // All known input methods.
final ArrayList<InputMethodInfo> mMethodList = new ArrayList<>();
final ArrayMap<String, InputMethodInfo> mMethodMap = new ArrayMap<>();
final InputMethodSubtypeSwitchingController mSwitchingController;
@@ -302,18 +311,18 @@
/**
* Tracks how many times {@link #mMethodMap} was updated.
*/
- @GuardedBy("mMethodMap")
+ @GuardedBy("ImfLock.class")
private int mMethodMapUpdateCount = 0;
/**
* The display id for which the latest startInput was called.
*/
- @GuardedBy("mMethodMap")
- int getDisplayIdToShowIme() {
+ @GuardedBy("ImfLock.class")
+ int getDisplayIdToShowImeLocked() {
return mDisplayIdToShowIme;
}
- @GuardedBy("mMethodMap")
+ @GuardedBy("ImfLock.class")
private int mDisplayIdToShowIme = INVALID_DISPLAY;
// Ongoing notification
@@ -399,7 +408,7 @@
}
}
- @GuardedBy("mMethodMap")
+ @GuardedBy("ImfLock.class")
final ArrayMap<IBinder, ClientState> mClients = new ArrayMap<>();
/**
@@ -415,17 +424,19 @@
* <p>This can be transiently {@code null} when the system is re-initializing input method
* settings, e.g., the system locale is just changed.</p>
*
- * <p>Note that {@link InputMethodBindingController#getCurId()} is used to track which IME is
- * being connected to {@link InputMethodManagerService}.</p>
+ * <p>Note that {@link InputMethodBindingController#getCurId()} is used to track which IME
+ * is being connected to {@link InputMethodManagerService}.</p>
*
* @see InputMethodBindingController#getCurId()
*/
+ @GuardedBy("ImfLock.class")
@Nullable
- private String getSelectedMethodId() {
+ String getSelectedMethodIdLocked() {
return mBindingController.getSelectedMethodId();
}
- private void setSelectedMethodId(@Nullable String selectedMethodId) {
+ @GuardedBy("ImfLock.class")
+ private void setSelectedMethodIdLocked(@Nullable String selectedMethodId) {
mBindingController.setSelectedMethodId(selectedMethodId);
}
@@ -433,7 +444,8 @@
* The current binding sequence number, incremented every time there is
* a new bind performed.
*/
- private int getSequenceNumber() {
+ @GuardedBy("ImfLock.class")
+ private int getSequenceNumberLocked() {
return mBindingController.getSequenceNumber();
}
@@ -441,7 +453,8 @@
* Increase the current binding sequence number by one.
* Reset to 1 on overflow.
*/
- private void advanceSequenceNumber() {
+ @GuardedBy("ImfLock.class")
+ private void advanceSequenceNumberLocked() {
mBindingController.advanceSequenceNumber();
}
@@ -500,10 +513,11 @@
*
* <p>This can be {@code null} when no input method is connected.</p>
*
- * @see #getSelectedMethodId()
+ * @see #getSelectedMethodIdLocked()
*/
+ @GuardedBy("ImfLock.class")
@Nullable
- private String getCurId() {
+ private String getCurIdLocked() {
return mBindingController.getCurId();
}
@@ -521,7 +535,8 @@
* Set to true if our ServiceConnection is currently actively bound to
* a service (whether or not we have gotten its IBinder back yet).
*/
- private boolean hasConnection() {
+ @GuardedBy("ImfLock.class")
+ private boolean hasConnectionLocked() {
return mBindingController.hasConnection();
}
@@ -553,8 +568,9 @@
/**
* The Intent used to connect to the current input method.
*/
+ @GuardedBy("ImfLock.class")
@Nullable
- private Intent getCurIntent() {
+ private Intent getCurIntentLocked() {
return mBindingController.getCurIntent();
}
@@ -562,27 +578,31 @@
* The token we have made for the currently active input method, to
* identify it in the future.
*/
- private IBinder getCurToken() {
+ @GuardedBy("ImfLock.class")
+ private IBinder getCurTokenLocked() {
return mBindingController.getCurToken();
}
/**
* The displayId of current active input method.
*/
- int getCurTokenDisplayId() {
+ @GuardedBy("ImfLock.class")
+ int getCurTokenDisplayIdLocked() {
return mCurTokenDisplayId;
}
- void setCurTokenDisplayId(int curTokenDisplayId) {
+ @GuardedBy("ImfLock.class")
+ void setCurTokenDisplayIdLocked(int curTokenDisplayId) {
mCurTokenDisplayId = curTokenDisplayId;
}
- int mCurTokenDisplayId = INVALID_DISPLAY;
+ @GuardedBy("ImfLock.class")
+ private int mCurTokenDisplayId = INVALID_DISPLAY;
/**
* The host input token of the current active input method.
*/
- @GuardedBy("mMethodMap")
+ @GuardedBy("ImfLock.class")
@Nullable
private IBinder mCurHostInputToken;
@@ -598,15 +618,17 @@
* If non-null, this is the input method service we are currently connected
* to.
*/
+ @GuardedBy("ImfLock.class")
@Nullable
- private IInputMethod getCurMethod() {
+ private IInputMethod getCurMethodLocked() {
return mBindingController.getCurMethod();
}
/**
- * If not {@link Process#INVALID_UID}, then the UID of {@link #getCurIntent()}.
+ * If not {@link Process#INVALID_UID}, then the UID of {@link #getCurIntentLocked()}.
*/
- private int getCurMethodUid() {
+ @GuardedBy("ImfLock.class")
+ private int getCurMethodUidLocked() {
return mBindingController.getCurMethodUid();
}
@@ -614,7 +636,8 @@
* Time that we last initiated a bind to the input method, to determine
* if we should try to disconnect and reconnect to it.
*/
- private long getLastBindTime() {
+ @GuardedBy("ImfLock.class")
+ private long getLastBindTimeLocked() {
return mBindingController.getLastBindTime();
}
@@ -657,7 +680,7 @@
* </dd>
* </dl>
* <em>Do not update this value outside of {@link #setImeWindowStatus(IBinder, int, int)} and
- * {@link InputMethodBindingController#unbindCurrentMethodLocked()}.</em>
+ * {@link InputMethodBindingController#unbindCurrentMethod()}.</em>
*/
int mImeWindowVis;
@@ -740,7 +763,7 @@
}
}
- @GuardedBy("mMethodMap")
+ @GuardedBy("ImfLock.class")
private final WeakHashMap<IBinder, IBinder> mImeTargetWindowMap = new WeakHashMap<>();
private static final class SoftInputShowHideHistory {
@@ -848,7 +871,7 @@
* {@link InputMethodManager#showSoftInput(View, int)}.
* This map tracks origin of showSoftInput requests.
*/
- @GuardedBy("mMethodMap")
+ @GuardedBy("ImfLock.class")
private final WeakHashMap<IBinder, IBinder> mShowRequestWindowMap = new WeakHashMap<>();
/**
@@ -856,7 +879,7 @@
* {@link InputMethodManager#hideSoftInputFromWindow(IBinder, int)}.
* This map tracks origin of hideSoftInput requests.
*/
- @GuardedBy("mMethodMap")
+ @GuardedBy("ImfLock.class")
private final WeakHashMap<IBinder, IBinder> mHideRequestWindowMap = new WeakHashMap<>();
/**
@@ -1013,11 +1036,11 @@
}
}
- @GuardedBy("mMethodMap")
+ @GuardedBy("ImfLock.class")
@NonNull
private final StartInputHistory mStartInputHistory = new StartInputHistory();
- @GuardedBy("mMethodMap")
+ @GuardedBy("ImfLock.class")
@NonNull
private final SoftInputShowHideHistory mSoftInputShowHideHistory =
new SoftInputShowHideHistory();
@@ -1035,7 +1058,7 @@
super(handler);
}
- @GuardedBy("mMethodMap")
+ @GuardedBy("ImfLock.class")
public void registerContentObserverLocked(@UserIdInt int userId) {
if (mRegistered && mUserId == userId) {
return;
@@ -1067,7 +1090,7 @@
Settings.Secure.SHOW_IME_WITH_HARD_KEYBOARD);
final Uri accessibilityRequestingNoImeUri = Settings.Secure.getUriFor(
Settings.Secure.ACCESSIBILITY_SOFT_KEYBOARD_MODE);
- synchronized (mMethodMap) {
+ synchronized (ImfLock.class) {
if (showImeUri.equals(uri)) {
mMenuController.updateKeyboardFromSettingsLocked();
} else if (accessibilityRequestingNoImeUri.equals(uri)) {
@@ -1173,7 +1196,7 @@
* <p>Caution: This method must not be called when system is not ready.</p>
*/
void onActionLocaleChanged() {
- synchronized (mMethodMap) {
+ synchronized (ImfLock.class) {
final LocaleList possibleNewLocale = mRes.getConfiguration().getLocales();
if (possibleNewLocale != null && possibleNewLocale.equals(mLastSystemLocales)) {
return;
@@ -1195,7 +1218,7 @@
* dynamically unless the entire package is updated, which also always triggers package
* rescanning.</p>
*/
- @GuardedBy("mMethodMap")
+ @GuardedBy("ImfLock.class")
final private ArraySet<String> mKnownImePackageNames = new ArraySet<>();
/**
@@ -1218,17 +1241,17 @@
*/
private boolean mImePackageAppeared = false;
- @GuardedBy("mMethodMap")
+ @GuardedBy("ImfLock.class")
void clearKnownImePackageNamesLocked() {
mKnownImePackageNames.clear();
}
- @GuardedBy("mMethodMap")
+ @GuardedBy("ImfLock.class")
final void addKnownImePackageNameLocked(@NonNull String packageName) {
mKnownImePackageNames.add(packageName);
}
- @GuardedBy("mMethodMap")
+ @GuardedBy("ImfLock.class")
private boolean isChangingPackagesOfCurrentUserLocked() {
final int userId = getChangingUserId();
final boolean retval = userId == mSettings.getCurrentUserId();
@@ -1242,7 +1265,7 @@
@Override
public boolean onHandleForceStop(Intent intent, String[] packages, int uid, boolean doit) {
- synchronized (mMethodMap) {
+ synchronized (ImfLock.class) {
if (!isChangingPackagesOfCurrentUserLocked()) {
return false;
}
@@ -1330,7 +1353,7 @@
mImePackageAppeared = false;
}
- @GuardedBy("mMethodMap")
+ @GuardedBy("ImfLock.class")
private boolean shouldRebuildInputMethodListLocked() {
// This method is guaranteed to be called only by getRegisteredHandler().
@@ -1354,7 +1377,7 @@
}
private void onFinishPackageChangesInternal() {
- synchronized (mMethodMap) {
+ synchronized (ImfLock.class) {
if (!isChangingPackagesOfCurrentUserLocked()) {
return;
}
@@ -1477,7 +1500,7 @@
@Override
public void run() {
- synchronized (mService.mMethodMap) {
+ synchronized (ImfLock.class) {
if (mService.mUserSwitchHandlerTask != this) {
// This task was already canceled before it is handled here. So do nothing.
return;
@@ -1494,7 +1517,7 @@
* a handler callback. This needs to be set and unset only within the lock.
*/
@Nullable
- @GuardedBy("mMethodMap")
+ @GuardedBy("ImfLock.class")
private UserSwitchHandlerTask mUserSwitchHandlerTask;
public static final class Lifecycle extends SystemService {
@@ -1516,7 +1539,7 @@
@Override
public void onUserSwitching(@Nullable TargetUser from, @NonNull TargetUser to) {
// Called on ActivityManager thread.
- synchronized (mService.mMethodMap) {
+ synchronized (ImfLock.class) {
mService.scheduleSwitchUserTaskLocked(to.getUserIdentifier(),
/* clientToBeReset= */ null);
}
@@ -1542,7 +1565,7 @@
}
void onUnlockUser(@UserIdInt int userId) {
- synchronized(mMethodMap) {
+ synchronized (ImfLock.class) {
final int currentUserId = mSettings.getCurrentUserId();
if (DEBUG) {
Slog.d(TAG, "onUnlockUser: userId=" + userId + " curUserId=" + currentUserId);
@@ -1559,7 +1582,7 @@
}
}
- @GuardedBy("mMethodMap")
+ @GuardedBy("ImfLock.class")
void scheduleSwitchUserTaskLocked(@UserIdInt int userId,
@Nullable IInputMethodClient clientToBeReset) {
if (mUserSwitchHandlerTask != null) {
@@ -1648,12 +1671,14 @@
mSettings, context);
mMenuController = new InputMethodMenuController(this);
mBindingController = new InputMethodBindingController(this);
+ mPreventImeStartupUnlessTextEditor = mRes.getBoolean(
+ com.android.internal.R.bool.config_preventImeStartupUnlessTextEditor);
}
- @GuardedBy("mMethodMap")
+ @GuardedBy("ImfLock.class")
private void resetDefaultImeLocked(Context context) {
// Do not reset the default (current) IME when it is a 3rd-party IME
- String selectedMethodId = getSelectedMethodId();
+ String selectedMethodId = getSelectedMethodIdLocked();
if (selectedMethodId != null && !mMethodMap.get(selectedMethodId).isSystem()) {
return;
}
@@ -1670,7 +1695,7 @@
setSelectedInputMethodAndSubtypeLocked(defIm, NOT_A_SUBTYPE_ID, false);
}
- @GuardedBy("mMethodMap")
+ @GuardedBy("ImfLock.class")
private void switchUserOnHandlerLocked(@UserIdInt int newUserId,
IInputMethodClient clientToBeReset) {
if (DEBUG) Slog.d(TAG, "Switching user stage 1/3. newUserId=" + newUserId
@@ -1756,7 +1781,7 @@
}
public void systemRunning(StatusBarManagerService statusBar) {
- synchronized (mMethodMap) {
+ synchronized (ImfLock.class) {
if (DEBUG) {
Slog.d(TAG, "--- systemReady");
}
@@ -1812,7 +1837,7 @@
// Check whether or not this is a valid IPC. Assumes an IPC is valid when either
// 1) it comes from the system process
// 2) the calling process' user id is identical to the current user id IMMS thinks.
- @GuardedBy("mMethodMap")
+ @GuardedBy("ImfLock.class")
private boolean calledFromValidUserLocked() {
final int uid = Binder.getCallingUid();
final int userId = UserHandle.getUserId(uid);
@@ -1856,12 +1881,12 @@
* @param token The window token given to the input method when it was started.
* @return true if and only if non-null valid token is specified.
*/
- @GuardedBy("mMethodMap")
+ @GuardedBy("ImfLock.class")
private boolean calledWithValidTokenLocked(@NonNull IBinder token) {
if (token == null) {
throw new InvalidParameterException("token must not be null.");
}
- if (token != getCurToken()) {
+ if (token != getCurTokenLocked()) {
Slog.e(TAG, "Ignoring " + Debug.getCaller() + " due to an invalid token."
+ " uid:" + Binder.getCallingUid() + " token:" + token);
return false;
@@ -1869,13 +1894,13 @@
return true;
}
- @Override
- public List<InputMethodInfo> getInputMethodList(@UserIdInt int userId) {
+ private List<InputMethodInfo> getInputMethodListInternal(@UserIdInt int userId,
+ @DirectBootAwareness int directBootAwareness) {
if (UserHandle.getCallingUserId() != userId) {
mContext.enforceCallingPermission(
Manifest.permission.INTERACT_ACROSS_USERS_FULL, null);
}
- synchronized (mMethodMap) {
+ synchronized (ImfLock.class) {
final int[] resolvedUserIds = InputMethodUtils.resolveUserId(userId,
mSettings.getCurrentUserId(), null);
if (resolvedUserIds.length != 1) {
@@ -1883,7 +1908,7 @@
}
final long ident = Binder.clearCallingIdentity();
try {
- return getInputMethodListLocked(resolvedUserIds[0]);
+ return getInputMethodListLocked(resolvedUserIds[0], directBootAwareness);
} finally {
Binder.restoreCallingIdentity(ident);
}
@@ -1891,12 +1916,23 @@
}
@Override
+ public List<InputMethodInfo> getInputMethodList(@UserIdInt int userId) {
+ return getInputMethodListInternal(userId, DirectBootAwareness.AUTO);
+ }
+
+ @Override
+ public List<InputMethodInfo> getAwareLockedInputMethodList(@UserIdInt int userId,
+ @DirectBootAwareness int directBootAwareness) {
+ return getInputMethodListInternal(userId, directBootAwareness);
+ }
+
+ @Override
public List<InputMethodInfo> getEnabledInputMethodList(@UserIdInt int userId) {
if (UserHandle.getCallingUserId() != userId) {
mContext.enforceCallingPermission(
Manifest.permission.INTERACT_ACROSS_USERS_FULL, null);
}
- synchronized (mMethodMap) {
+ synchronized (ImfLock.class) {
final int[] resolvedUserIds = InputMethodUtils.resolveUserId(userId,
mSettings.getCurrentUserId(), null);
if (resolvedUserIds.length != 1) {
@@ -1911,10 +1947,12 @@
}
}
- @GuardedBy("mMethodMap")
- private List<InputMethodInfo> getInputMethodListLocked(@UserIdInt int userId) {
+ @GuardedBy("ImfLock.class")
+ private List<InputMethodInfo> getInputMethodListLocked(@UserIdInt int userId,
+ @DirectBootAwareness int directBootAwareness) {
final ArrayList<InputMethodInfo> methodList;
- if (userId == mSettings.getCurrentUserId()) {
+ if (userId == mSettings.getCurrentUserId()
+ && directBootAwareness == DirectBootAwareness.AUTO) {
// Create a copy.
methodList = new ArrayList<>(mMethodList);
} else {
@@ -1924,12 +1962,12 @@
new ArrayMap<>();
AdditionalSubtypeUtils.load(additionalSubtypeMap, userId);
queryInputMethodServicesInternal(mContext, userId, additionalSubtypeMap, methodMap,
- methodList);
+ methodList, directBootAwareness);
}
return methodList;
}
- @GuardedBy("mMethodMap")
+ @GuardedBy("ImfLock.class")
private List<InputMethodInfo> getEnabledInputMethodListLocked(@UserIdInt int userId) {
if (userId == mSettings.getCurrentUserId()) {
return mSettings.getEnabledInputMethodListLocked();
@@ -1940,18 +1978,19 @@
return settings.getEnabledInputMethodListLocked();
}
- @GuardedBy("mMethodMap")
+ @GuardedBy("ImfLock.class")
private void onCreateInlineSuggestionsRequestLocked(@UserIdInt int userId,
InlineSuggestionsRequestInfo requestInfo, IInlineSuggestionsRequestCallback callback) {
- final InputMethodInfo imi = mMethodMap.get(getSelectedMethodId());
+ final InputMethodInfo imi = mMethodMap.get(getSelectedMethodIdLocked());
try {
- IInputMethod curMethod = getCurMethod();
+ IInputMethod curMethod = getCurMethodLocked();
if (userId == mSettings.getCurrentUserId() && imi != null
&& imi.isInlineSuggestionsEnabled() && curMethod != null) {
executeOrSendMessage(curMethod,
mCaller.obtainMessageOOO(MSG_INLINE_SUGGESTIONS_REQUEST, curMethod,
requestInfo, new InlineSuggestionsRequestCallbackDecorator(callback,
- imi.getPackageName(), mCurTokenDisplayId, getCurToken(),
+ imi.getPackageName(), mCurTokenDisplayId,
+ getCurTokenLocked(),
this)));
} else {
callback.onInlineSuggestionsUnsupported();
@@ -2047,7 +2086,7 @@
* @param hostInputToken the host input token of the current active input method
*/
void setCurHostInputToken(@NonNull IBinder callerImeToken, @Nullable IBinder hostInputToken) {
- synchronized (mMethodMap) {
+ synchronized (ImfLock.class) {
if (!calledWithValidTokenLocked(callerImeToken)) {
return;
}
@@ -2066,7 +2105,7 @@
public List<InputMethodSubtype> getEnabledInputMethodSubtypeList(String imiId,
boolean allowsImplicitlySelectedSubtypes) {
final int callingUserId = UserHandle.getCallingUserId();
- synchronized (mMethodMap) {
+ synchronized (ImfLock.class) {
final int[] resolvedUserIds = InputMethodUtils.resolveUserId(callingUserId,
mSettings.getCurrentUserId(), null);
if (resolvedUserIds.length != 1) {
@@ -2082,12 +2121,12 @@
}
}
- @GuardedBy("mMethodMap")
+ @GuardedBy("ImfLock.class")
private List<InputMethodSubtype> getEnabledInputMethodSubtypeListLocked(String imiId,
boolean allowsImplicitlySelectedSubtypes, @UserIdInt int userId) {
if (userId == mSettings.getCurrentUserId()) {
final InputMethodInfo imi;
- String selectedMethodId = getSelectedMethodId();
+ String selectedMethodId = getSelectedMethodIdLocked();
if (imiId == null && selectedMethodId != null) {
imi = mMethodMap.get(selectedMethodId);
} else {
@@ -2137,7 +2176,7 @@
// actually running.
final int callerUid = Binder.getCallingUid();
final int callerPid = Binder.getCallingPid();
- synchronized (mMethodMap) {
+ synchronized (ImfLock.class) {
// TODO: Optimize this linear search.
final int numClients = mClients.size();
for (int i = 0; i < numClients; ++i) {
@@ -2170,7 +2209,7 @@
}
void removeClient(IInputMethodClient client) {
- synchronized (mMethodMap) {
+ synchronized (ImfLock.class) {
ClientState cs = mClients.remove(client.asBinder());
if (cs != null) {
client.asBinder().unlinkToDeath(cs.clientDeathRecipient, 0);
@@ -2180,7 +2219,7 @@
mCurFocusedWindow, 0, null, SoftInputShowHideReason.HIDE_REMOVE_CLIENT);
if (mBoundToMethod) {
mBoundToMethod = false;
- IInputMethod curMethod = getCurMethod();
+ IInputMethod curMethod = getCurMethodLocked();
if (curMethod != null) {
executeOrSendMessage(curMethod, mCaller.obtainMessageO(
MSG_UNBIND_INPUT, curMethod));
@@ -2204,14 +2243,14 @@
}
}
- @GuardedBy("mMethodMap")
+ @GuardedBy("ImfLock.class")
void unbindCurrentClientLocked(@UnbindReason int unbindClientReason) {
if (mCurClient != null) {
if (DEBUG) Slog.v(TAG, "unbindCurrentInputLocked: client="
+ mCurClient.client.asBinder());
if (mBoundToMethod) {
mBoundToMethod = false;
- IInputMethod curMethod = getCurMethod();
+ IInputMethod curMethod = getCurMethodLocked();
if (curMethod != null) {
executeOrSendMessage(curMethod, mCaller.obtainMessageO(
MSG_UNBIND_INPUT, curMethod));
@@ -2221,7 +2260,8 @@
scheduleSetActiveToClient(mCurClient, false /* active */, false /* fullscreen */,
false /* reportToImeController */);
executeOrSendMessage(mCurClient.client, mCaller.obtainMessageIIO(
- MSG_UNBIND_CLIENT, getSequenceNumber(), unbindClientReason, mCurClient.client));
+ MSG_UNBIND_CLIENT, getSequenceNumberLocked(), unbindClientReason,
+ mCurClient.client));
mCurClient.sessionRequested = false;
mCurClient = null;
@@ -2229,13 +2269,13 @@
}
}
- @GuardedBy("mMethodMap")
+ @GuardedBy("ImfLock.class")
void clearInputShowRequestLocked() {
mShowRequested = mInputShown;
mInputShown = false;
}
- @GuardedBy("mMethodMap")
+ @GuardedBy("ImfLock.class")
private int getImeShowFlagsLocked() {
int flags = 0;
if (mShowForced) {
@@ -2247,7 +2287,7 @@
return flags;
}
- @GuardedBy("mMethodMap")
+ @GuardedBy("ImfLock.class")
private int getAppShowFlagsLocked() {
int flags = 0;
if (mShowForced) {
@@ -2258,22 +2298,23 @@
return flags;
}
- @GuardedBy("mMethodMap")
+ @GuardedBy("ImfLock.class")
@NonNull
InputBindResult attachNewInputLocked(@StartInputReason int startInputReason, boolean initial) {
if (!mBoundToMethod) {
- IInputMethod curMethod = getCurMethod();
+ IInputMethod curMethod = getCurMethodLocked();
executeOrSendMessage(curMethod, mCaller.obtainMessageOO(
MSG_BIND_INPUT, curMethod, mCurClient.binding));
mBoundToMethod = true;
}
final Binder startInputToken = new Binder();
- final StartInputInfo info = new StartInputInfo(mSettings.getCurrentUserId(), getCurToken(),
- mCurTokenDisplayId, getCurId(), startInputReason, !initial,
+ final StartInputInfo info = new StartInputInfo(mSettings.getCurrentUserId(),
+ getCurTokenLocked(),
+ mCurTokenDisplayId, getCurIdLocked(), startInputReason, !initial,
UserHandle.getUserId(mCurClient.uid), mCurClient.selfReportedDisplayId,
mCurFocusedWindow, mCurAttribute, mCurFocusedWindowSoftInputMode,
- getSequenceNumber());
+ getSequenceNumberLocked());
mImeTargetWindowMap.put(startInputToken, mCurFocusedWindow);
mStartInputHistory.addEntry(info);
@@ -2284,7 +2325,7 @@
// INTERACT_ACROSS_USERS(_FULL) permissions, which is actually almost always the case.
if (mSettings.getCurrentUserId() == UserHandle.getUserId(mCurClient.uid)) {
mPackageManagerInternal.grantImplicitAccess(mSettings.getCurrentUserId(),
- null /* intent */, UserHandle.getAppId(getCurMethodUid()), mCurClient.uid,
+ null /* intent */, UserHandle.getAppId(getCurMethodUidLocked()), mCurClient.uid,
true /* direct */);
}
@@ -2298,22 +2339,22 @@
SoftInputShowHideReason.ATTACH_NEW_INPUT);
}
- String curId = getCurId();
+ String curId = getCurIdLocked();
final InputMethodInfo curInputMethodInfo = mMethodMap.get(curId);
final boolean suppressesSpellChecker =
curInputMethodInfo != null && curInputMethodInfo.suppressesSpellChecker();
return new InputBindResult(InputBindResult.ResultCode.SUCCESS_WITH_IME_SESSION,
session.session, (session.channel != null ? session.channel.dup() : null),
- curId, getSequenceNumber(), suppressesSpellChecker);
+ curId, getSequenceNumberLocked(), suppressesSpellChecker);
}
- @GuardedBy("mMethodMap")
+ @GuardedBy("ImfLock.class")
@NonNull
InputBindResult startInputUncheckedLocked(@NonNull ClientState cs, IInputContext inputContext,
@NonNull EditorInfo attribute, @StartInputFlags int startInputFlags,
- @StartInputReason int startInputReason) {
+ @StartInputReason int startInputReason, int unverifiedTargetSdkVersion) {
// If no method is currently selected, do nothing.
- String selectedMethodId = getSelectedMethodId();
+ String selectedMethodId = getSelectedMethodIdLocked();
if (selectedMethodId == null) {
return InputBindResult.NO_IME;
}
@@ -2323,7 +2364,7 @@
// party code.
return new InputBindResult(
InputBindResult.ResultCode.ERROR_SYSTEM_NOT_READY,
- null, null, selectedMethodId, getSequenceNumber(), false);
+ null, null, selectedMethodId, getSequenceNumberLocked(), false);
}
if (!InputMethodUtils.checkIfPackageBelongsToUid(mAppOpsManager, cs.uid,
@@ -2355,15 +2396,27 @@
}
// Bump up the sequence for this client and attach it.
- advanceSequenceNumber();
+ advanceSequenceNumberLocked();
mCurClient = cs;
mCurInputContext = inputContext;
mCurAttribute = attribute;
+ // If configured, we want to avoid starting up the IME if it is not supposed to be showing
+ if (mPreventImeStartupUnlessTextEditor
+ && !InputMethodUtils.isSoftInputModeStateVisibleAllowed(unverifiedTargetSdkVersion,
+ startInputFlags)
+ && !mShowRequested) {
+ if (DEBUG) {
+ Slog.d(TAG, "Avoiding IME startup and unbinding current input method.");
+ }
+ mBindingController.unbindCurrentMethod();
+ return InputBindResult.NO_EDITOR;
+ }
+
// Check if the input method is changing.
// We expect the caller has already verified that the client is allowed to access this
// display ID.
- if (isSelectedMethodBound()) {
+ if (isSelectedMethodBoundLocked()) {
if (cs.curSession != null) {
// Fast case: if we are already connected to the input method,
// then just return it.
@@ -2377,18 +2430,19 @@
}
}
- mBindingController.unbindCurrentMethodLocked();
+ mBindingController.unbindCurrentMethod();
- return mBindingController.bindCurrentMethodLocked();
+ return mBindingController.bindCurrentMethod();
}
- private boolean isSelectedMethodBound() {
- String curId = getCurId();
- return curId != null && curId.equals(getSelectedMethodId())
+ @GuardedBy("ImfLock.class")
+ private boolean isSelectedMethodBoundLocked() {
+ String curId = getCurIdLocked();
+ return curId != null && curId.equals(getSelectedMethodIdLocked())
&& mDisplayIdToShowIme == mCurTokenDisplayId;
}
- @GuardedBy("mMethodMap")
+ @GuardedBy("ImfLock.class")
private void prepareClientSwitchLocked(ClientState cs) {
// If the client is changing, we need to switch over to the new
// one.
@@ -2400,19 +2454,19 @@
}
}
- @GuardedBy("mMethodMap")
+ @GuardedBy("ImfLock.class")
@Nullable
private InputBindResult tryReuseConnectionLocked(@NonNull ClientState cs) {
- if (hasConnection()) {
- if (getCurMethod() != null) {
+ if (hasConnectionLocked()) {
+ if (getCurMethodLocked() != null) {
// Return to client, and we will get back with it when
// we have had a session made for it.
requestClientSessionLocked(cs);
return new InputBindResult(
InputBindResult.ResultCode.SUCCESS_WAITING_IME_SESSION,
- null, null, getCurId(), getSequenceNumber(), false);
+ null, null, getCurIdLocked(), getSequenceNumberLocked(), false);
} else {
- long bindingDuration = SystemClock.uptimeMillis() - getLastBindTime();
+ long bindingDuration = SystemClock.uptimeMillis() - getLastBindTimeLocked();
if (bindingDuration < TIME_TO_RECONNECT) {
// In this case we have connected to the service, but
// don't yet have its interface. If it hasn't been too
@@ -2423,10 +2477,10 @@
// to see if we can get back in touch with the service.
return new InputBindResult(
InputBindResult.ResultCode.SUCCESS_WAITING_IME_BINDING,
- null, null, getCurId(), getSequenceNumber(), false);
+ null, null, getCurIdLocked(), getSequenceNumberLocked(), false);
} else {
EventLog.writeEvent(EventLogTags.IMF_FORCE_RECONNECT_IME,
- getSelectedMethodId(), bindingDuration, 0);
+ getSelectedMethodIdLocked(), bindingDuration, 0);
}
}
}
@@ -2473,13 +2527,13 @@
void onSessionCreated(IInputMethod method, IInputMethodSession session,
InputChannel channel) {
- synchronized (mMethodMap) {
+ synchronized (ImfLock.class) {
if (mUserSwitchHandlerTask != null) {
// We have a pending user-switching task so it's better to just ignore this session.
channel.dispose();
return;
}
- IInputMethod curMethod = getCurMethod();
+ IInputMethod curMethod = getCurMethodLocked();
if (curMethod != null && method != null
&& curMethod.asBinder() == method.asBinder()) {
if (mCurClient != null) {
@@ -2501,7 +2555,7 @@
channel.dispose();
}
- @GuardedBy("mMethodMap")
+ @GuardedBy("ImfLock.class")
void resetSystemUiLocked() {
// Set IME window status as invisible when unbinding current method.
mImeWindowVis = 0;
@@ -2511,14 +2565,14 @@
mCurHostInputToken = null;
}
- @GuardedBy("mMethodMap")
+ @GuardedBy("ImfLock.class")
void resetCurrentMethodAndClientLocked(@UnbindReason int unbindClientReason) {
- setSelectedMethodId(null);
- mBindingController.unbindCurrentMethodLocked();
+ setSelectedMethodIdLocked(null);
+ mBindingController.unbindCurrentMethod();
unbindCurrentClientLocked(unbindClientReason);
}
- @GuardedBy("mMethodMap")
+ @GuardedBy("ImfLock.class")
void reRequestCurrentClientSessionLocked() {
if (mCurClient != null) {
clearClientSessionLocked(mCurClient);
@@ -2526,27 +2580,27 @@
}
}
- @GuardedBy("mMethodMap")
+ @GuardedBy("ImfLock.class")
void requestClientSessionLocked(ClientState cs) {
if (!cs.sessionRequested) {
if (DEBUG) Slog.v(TAG, "Creating new session for client " + cs);
InputChannel[] channels = InputChannel.openInputChannelPair(cs.toString());
cs.sessionRequested = true;
- IInputMethod curMethod = getCurMethod();
+ IInputMethod curMethod = getCurMethodLocked();
executeOrSendMessage(curMethod, mCaller.obtainMessageOOO(
MSG_CREATE_SESSION, curMethod, channels[1],
new MethodCallback(this, curMethod, channels[0])));
}
}
- @GuardedBy("mMethodMap")
+ @GuardedBy("ImfLock.class")
void clearClientSessionLocked(ClientState cs) {
finishSessionLocked(cs.curSession);
cs.curSession = null;
cs.sessionRequested = false;
}
- @GuardedBy("mMethodMap")
+ @GuardedBy("ImfLock.class")
private void finishSessionLocked(SessionState sessionState) {
if (sessionState != null) {
if (sessionState.session != null) {
@@ -2565,9 +2619,9 @@
}
}
- @GuardedBy("mMethodMap")
+ @GuardedBy("ImfLock.class")
void clearClientSessionsLocked() {
- if (getCurMethod() != null) {
+ if (getCurMethodLocked() != null) {
final int numClients = mClients.size();
for (int i = 0; i < numClients; ++i) {
clearClientSessionLocked(mClients.valueAt(i));
@@ -2584,7 +2638,7 @@
@BinderThread
private void updateStatusIcon(@NonNull IBinder token, String packageName,
@DrawableRes int iconId) {
- synchronized (mMethodMap) {
+ synchronized (ImfLock.class) {
if (!calledWithValidTokenLocked(token)) {
return;
}
@@ -2618,14 +2672,14 @@
}
}
- @GuardedBy("mMethodMap")
+ @GuardedBy("ImfLock.class")
private void hideStatusBarIconLocked() {
if (mStatusBar != null) {
mStatusBar.setIconVisibility(mSlotIme, false);
}
}
- @GuardedBy("mMethodMap")
+ @GuardedBy("ImfLock.class")
private boolean shouldShowImeSwitcherLocked(int visibility) {
if (!mShowOngoingImeSwitcherForPhones) return false;
if (mMenuController.getSwitchingDialogLocked() != null) return false;
@@ -2694,7 +2748,7 @@
private void setImeWindowStatus(@NonNull IBinder token, int vis, int backDisposition) {
final int topFocusedDisplayId = mWindowManagerInternal.getTopFocusedDisplayId();
- synchronized (mMethodMap) {
+ synchronized (ImfLock.class) {
if (!calledWithValidTokenLocked(token)) {
return;
}
@@ -2729,7 +2783,7 @@
@BinderThread
private void reportStartInput(@NonNull IBinder token, IBinder startInputToken) {
- synchronized (mMethodMap) {
+ synchronized (ImfLock.class) {
if (!calledWithValidTokenLocked(token)) {
return;
}
@@ -2742,7 +2796,7 @@
}
private void updateImeWindowStatus(boolean disableImeIcon) {
- synchronized (mMethodMap) {
+ synchronized (ImfLock.class) {
if (disableImeIcon) {
updateSystemUiLocked(0, mBackDisposition);
} else {
@@ -2751,15 +2805,15 @@
}
}
- @GuardedBy("mMethodMap")
+ @GuardedBy("ImfLock.class")
void updateSystemUiLocked() {
updateSystemUiLocked(mImeWindowVis, mBackDisposition);
}
// Caution! This method is called in this class. Handle multi-user carefully
- @GuardedBy("mMethodMap")
+ @GuardedBy("ImfLock.class")
private void updateSystemUiLocked(int vis, int backDisposition) {
- if (getCurToken() == null) {
+ if (getCurTokenLocked() == null) {
return;
}
if (DEBUG) {
@@ -2784,10 +2838,10 @@
// mImeWindowVis should be updated before calling shouldShowImeSwitcherLocked().
final boolean needsToShowImeSwitcher = shouldShowImeSwitcherLocked(vis);
if (mStatusBar != null) {
- mStatusBar.setImeWindowStatus(mCurTokenDisplayId, getCurToken(), vis,
+ mStatusBar.setImeWindowStatus(mCurTokenDisplayId, getCurTokenLocked(), vis,
backDisposition, needsToShowImeSwitcher);
}
- final InputMethodInfo imi = mMethodMap.get(getSelectedMethodId());
+ final InputMethodInfo imi = mMethodMap.get(getSelectedMethodIdLocked());
if (imi != null && needsToShowImeSwitcher) {
// Used to load label
final CharSequence title = mRes.getText(
@@ -2826,13 +2880,13 @@
}
}
- @GuardedBy("mMethodMap")
+ @GuardedBy("ImfLock.class")
void updateFromSettingsLocked(boolean enabledMayChange) {
updateInputMethodsFromSettingsLocked(enabledMayChange);
mMenuController.updateKeyboardFromSettingsLocked();
}
- @GuardedBy("mMethodMap")
+ @GuardedBy("ImfLock.class")
void updateInputMethodsFromSettingsLocked(boolean enabledMayChange) {
if (enabledMayChange) {
List<InputMethodInfo> enabled = mSettings.getEnabledInputMethodListLocked();
@@ -2887,7 +2941,7 @@
}
- @GuardedBy("mMethodMap")
+ @GuardedBy("ImfLock.class")
void setInputMethodLocked(String id, int subtypeId) {
InputMethodInfo info = mMethodMap.get(id);
if (info == null) {
@@ -2895,7 +2949,7 @@
}
// See if we need to notify a subtype change within the same IME.
- if (id.equals(getSelectedMethodId())) {
+ if (id.equals(getSelectedMethodIdLocked())) {
final int subtypeCount = info.getSubtypeCount();
if (subtypeCount <= 0) {
return;
@@ -2916,7 +2970,7 @@
}
if (newSubtype != oldSubtype) {
setSelectedInputMethodAndSubtypeLocked(info, subtypeId, true);
- IInputMethod curMethod = getCurMethod();
+ IInputMethod curMethod = getCurMethodLocked();
if (curMethod != null) {
try {
updateSystemUiLocked(mImeWindowVis, mBackDisposition);
@@ -2938,7 +2992,7 @@
// mCurMethodId should be updated after setSelectedInputMethodAndSubtypeLocked()
// because mCurMethodId is stored as a history in
// setSelectedInputMethodAndSubtypeLocked().
- setSelectedMethodId(id);
+ setSelectedMethodIdLocked(id);
if (LocalServices.getService(ActivityManagerInternal.class).isSystemReady()) {
Intent intent = new Intent(Intent.ACTION_INPUT_METHOD_CHANGED);
@@ -2959,7 +3013,7 @@
int uid = Binder.getCallingUid();
ImeTracing.getInstance().triggerManagerServiceDump(
"InputMethodManagerService#showSoftInput");
- synchronized (mMethodMap) {
+ synchronized (ImfLock.class) {
if (!calledFromValidUserLocked()) {
return false;
}
@@ -2995,7 +3049,7 @@
public void reportPerceptibleAsync(IBinder windowToken, boolean perceptible) {
Objects.requireNonNull(windowToken, "windowToken must not be null");
int uid = Binder.getCallingUid();
- synchronized (mMethodMap) {
+ synchronized (ImfLock.class) {
if (!calledFromValidUserLocked()) {
return;
}
@@ -3012,7 +3066,7 @@
}
}
- @GuardedBy("mMethodMap")
+ @GuardedBy("ImfLock.class")
boolean showCurrentInputLocked(IBinder windowToken, int flags, ResultReceiver resultReceiver,
@SoftInputShowHideReason int reason) {
mShowRequested = true;
@@ -3031,12 +3085,12 @@
return false;
}
- mBindingController.setCurrentMethodVisibleLocked();
- if (getCurMethod() != null) {
+ mBindingController.setCurrentMethodVisible();
+ if (getCurMethodLocked() != null) {
// create a placeholder token for IMS so that IMS cannot inject windows into client app.
Binder showInputToken = new Binder();
mShowRequestWindowMap.put(showInputToken, windowToken);
- IInputMethod curMethod = getCurMethod();
+ IInputMethod curMethod = getCurMethodLocked();
executeOrSendMessage(curMethod, mCaller.obtainMessageIIOOO(MSG_SHOW_SOFT_INPUT,
getImeShowFlagsLocked(), reason, curMethod, resultReceiver,
showInputToken));
@@ -3053,7 +3107,7 @@
int uid = Binder.getCallingUid();
ImeTracing.getInstance().triggerManagerServiceDump(
"InputMethodManagerService#hideSoftInput");
- synchronized (mMethodMap) {
+ synchronized (ImfLock.class) {
if (!InputMethodManagerService.this.calledFromValidUserLocked()) {
return false;
}
@@ -3090,7 +3144,7 @@
}
}
- @GuardedBy("mMethodMap")
+ @GuardedBy("ImfLock.class")
boolean hideCurrentInputLocked(IBinder windowToken, int flags, ResultReceiver resultReceiver,
@SoftInputShowHideReason int reason) {
if ((flags&InputMethodManager.HIDE_IMPLICIT_ONLY) != 0
@@ -3111,7 +3165,7 @@
// since Android Eclair. That's why we need to accept IMM#hideSoftInput() even when only
// IMMS#InputShown indicates that the software keyboard is shown.
// TODO: Clean up, IMMS#mInputShown, IMMS#mImeWindowVis and mShowRequested.
- IInputMethod curMethod = getCurMethod();
+ IInputMethod curMethod = getCurMethodLocked();
final boolean shouldHideSoftInput = (curMethod != null) && (mInputShown
|| (mImeWindowVis & InputMethodService.IME_ACTIVE) != 0);
boolean res;
@@ -3128,7 +3182,7 @@
} else {
res = false;
}
- mBindingController.setCurrentMethodNotVisibleLocked();
+ mBindingController.setCurrentMethodNotVisible();
mInputShown = false;
mShowRequested = false;
mShowExplicitlyRequested = false;
@@ -3184,7 +3238,7 @@
userId = callingUserId;
}
final InputBindResult result;
- synchronized (mMethodMap) {
+ synchronized (ImfLock.class) {
final long ident = Binder.clearCallingIdentity();
try {
result = startInputOrWindowGainedFocusInternalLocked(startInputReason,
@@ -3209,7 +3263,7 @@
}
}
- @GuardedBy("mMethodMap")
+ @GuardedBy("ImfLock.class")
@NonNull
private InputBindResult startInputOrWindowGainedFocusInternalLocked(
@StartInputReason int startInputReason, IInputMethodClient client,
@@ -3302,7 +3356,7 @@
}
if (attribute != null) {
return startInputUncheckedLocked(cs, inputContext, attribute, startInputFlags,
- startInputReason);
+ startInputReason, unverifiedTargetSdkVersion);
}
return new InputBindResult(
InputBindResult.ResultCode.SUCCESS_REPORT_WINDOW_FOCUS_ONLY,
@@ -3343,7 +3397,7 @@
if (isTextEditor && attribute != null
&& shouldRestoreImeVisibility(windowToken, softInputMode)) {
res = startInputUncheckedLocked(cs, inputContext, attribute, startInputFlags,
- startInputReason);
+ startInputReason, unverifiedTargetSdkVersion);
showCurrentInputLocked(windowToken, InputMethodManager.SHOW_IMPLICIT, null,
SoftInputShowHideReason.SHOW_RESTORE_IME_VISIBILITY);
return res;
@@ -3367,7 +3421,7 @@
// Note that we can trust client's display ID as long as it matches
// to the display ID obtained from the window.
if (cs.selfReportedDisplayId != mCurTokenDisplayId) {
- mBindingController.unbindCurrentMethodLocked();
+ mBindingController.unbindCurrentMethod();
}
}
} else if (isTextEditor && doAutoShow
@@ -3382,7 +3436,7 @@
if (DEBUG) Slog.v(TAG, "Unspecified window will show input");
if (attribute != null) {
res = startInputUncheckedLocked(cs, inputContext, attribute,
- startInputFlags, startInputReason);
+ startInputFlags, startInputReason, unverifiedTargetSdkVersion);
didStart = true;
}
showCurrentInputLocked(windowToken, InputMethodManager.SHOW_IMPLICIT, null,
@@ -3413,7 +3467,7 @@
unverifiedTargetSdkVersion, startInputFlags)) {
if (attribute != null) {
res = startInputUncheckedLocked(cs, inputContext, attribute,
- startInputFlags, startInputReason);
+ startInputFlags, startInputReason, unverifiedTargetSdkVersion);
didStart = true;
}
showCurrentInputLocked(windowToken, InputMethodManager.SHOW_IMPLICIT, null,
@@ -3432,7 +3486,7 @@
if (!sameWindowFocused) {
if (attribute != null) {
res = startInputUncheckedLocked(cs, inputContext, attribute,
- startInputFlags, startInputReason);
+ startInputFlags, startInputReason, unverifiedTargetSdkVersion);
didStart = true;
}
showCurrentInputLocked(windowToken, InputMethodManager.SHOW_IMPLICIT, null,
@@ -3461,7 +3515,7 @@
}
}
res = startInputUncheckedLocked(cs, inputContext, attribute, startInputFlags,
- startInputReason);
+ startInputReason, unverifiedTargetSdkVersion);
} else {
res = InputBindResult.NULL_EDITOR_INFO;
}
@@ -3482,17 +3536,17 @@
return mWindowManagerInternal.shouldRestoreImeVisibility(windowToken);
}
- @GuardedBy("mMethodMap")
+ @GuardedBy("ImfLock.class")
private boolean canShowInputMethodPickerLocked(IInputMethodClient client) {
// TODO(yukawa): multi-display support.
final int uid = Binder.getCallingUid();
if (mCurFocusedWindowClient != null && client != null
&& mCurFocusedWindowClient.client.asBinder() == client.asBinder()) {
return true;
- } else if (getCurIntent() != null && InputMethodUtils.checkIfPackageBelongsToUid(
+ } else if (getCurIntentLocked() != null && InputMethodUtils.checkIfPackageBelongsToUid(
mAppOpsManager,
uid,
- getCurIntent().getComponent().getPackageName())) {
+ getCurIntentLocked().getComponent().getPackageName())) {
return true;
}
return false;
@@ -3501,7 +3555,7 @@
@Override
public void showInputMethodPickerFromClient(IInputMethodClient client,
int auxiliarySubtypeMode) {
- synchronized (mMethodMap) {
+ synchronized (ImfLock.class) {
if (!calledFromValidUserLocked()) {
return;
}
@@ -3538,14 +3592,14 @@
* A test API for CTS to make sure that the input method menu is showing.
*/
public boolean isInputMethodPickerShownForTest() {
- synchronized(mMethodMap) {
+ synchronized (ImfLock.class) {
return mMenuController.isisInputMethodPickerShownForTestLocked();
}
}
@BinderThread
private void setInputMethod(@NonNull IBinder token, String id) {
- synchronized (mMethodMap) {
+ synchronized (ImfLock.class) {
if (!calledWithValidTokenLocked(token)) {
return;
}
@@ -3556,7 +3610,7 @@
@BinderThread
private void setInputMethodAndSubtype(@NonNull IBinder token, String id,
InputMethodSubtype subtype) {
- synchronized (mMethodMap) {
+ synchronized (ImfLock.class) {
if (!calledWithValidTokenLocked(token)) {
return;
}
@@ -3573,19 +3627,19 @@
@Override
public void showInputMethodAndSubtypeEnablerFromClient(
IInputMethodClient client, String inputMethodId) {
- synchronized (mMethodMap) {
+ synchronized (ImfLock.class) {
// TODO(yukawa): Should we verify the display ID?
if (!calledFromValidUserLocked()) {
return;
}
- executeOrSendMessage(getCurMethod(), mCaller.obtainMessageO(
+ executeOrSendMessage(getCurMethodLocked(), mCaller.obtainMessageO(
MSG_SHOW_IM_SUBTYPE_ENABLER, inputMethodId));
}
}
@BinderThread
private boolean switchToPreviousInputMethod(@NonNull IBinder token) {
- synchronized (mMethodMap) {
+ synchronized (ImfLock.class) {
if (!calledWithValidTokenLocked(token)) {
return false;
}
@@ -3599,7 +3653,7 @@
String targetLastImiId = null;
int subtypeId = NOT_A_SUBTYPE_ID;
if (lastIme != null && lastImi != null) {
- final boolean imiIdIsSame = lastImi.getId().equals(getSelectedMethodId());
+ final boolean imiIdIsSame = lastImi.getId().equals(getSelectedMethodIdLocked());
final int lastSubtypeHash = Integer.parseInt(lastIme.second);
final int currentSubtypeHash = mCurrentSubtype == null ? NOT_A_SUBTYPE_ID
: mCurrentSubtype.hashCode();
@@ -3645,7 +3699,7 @@
if (!TextUtils.isEmpty(targetLastImiId)) {
if (DEBUG) {
Slog.d(TAG, "Switch to: " + lastImi.getId() + ", " + lastIme.second
- + ", from: " + getSelectedMethodId() + ", " + subtypeId);
+ + ", from: " + getSelectedMethodIdLocked() + ", " + subtypeId);
}
setInputMethodWithSubtypeIdLocked(token, targetLastImiId, subtypeId);
return true;
@@ -3657,12 +3711,12 @@
@BinderThread
private boolean switchToNextInputMethod(@NonNull IBinder token, boolean onlyCurrentIme) {
- synchronized (mMethodMap) {
+ synchronized (ImfLock.class) {
if (!calledWithValidTokenLocked(token)) {
return false;
}
final ImeSubtypeListItem nextSubtype = mSwitchingController.getNextInputMethodLocked(
- onlyCurrentIme, mMethodMap.get(getSelectedMethodId()), mCurrentSubtype);
+ onlyCurrentIme, mMethodMap.get(getSelectedMethodIdLocked()), mCurrentSubtype);
if (nextSubtype == null) {
return false;
}
@@ -3674,12 +3728,12 @@
@BinderThread
private boolean shouldOfferSwitchingToNextInputMethod(@NonNull IBinder token) {
- synchronized (mMethodMap) {
+ synchronized (ImfLock.class) {
if (!calledWithValidTokenLocked(token)) {
return false;
}
final ImeSubtypeListItem nextSubtype = mSwitchingController.getNextInputMethodLocked(
- false /* onlyCurrentIme */, mMethodMap.get(getSelectedMethodId()),
+ false /* onlyCurrentIme */, mMethodMap.get(getSelectedMethodIdLocked()),
mCurrentSubtype);
if (nextSubtype == null) {
return false;
@@ -3690,7 +3744,7 @@
@Override
public InputMethodSubtype getLastInputMethodSubtype() {
- synchronized (mMethodMap) {
+ synchronized (ImfLock.class) {
if (!calledFromValidUserLocked()) {
return null;
}
@@ -3728,7 +3782,7 @@
+ subtype.getLocale() + ", " + subtype.getMode());
}
}
- synchronized (mMethodMap) {
+ synchronized (ImfLock.class) {
if (!calledFromValidUserLocked()) {
return;
}
@@ -3779,7 +3833,11 @@
@Override
public int getInputMethodWindowVisibleHeight() {
// TODO(yukawa): Should we verify the display ID?
- return mWindowManagerInternal.getInputMethodWindowVisibleHeight(mCurTokenDisplayId);
+ final int curTokenDisplayId;
+ synchronized (ImfLock.class) {
+ curTokenDisplayId = mCurTokenDisplayId;
+ }
+ return mWindowManagerInternal.getInputMethodWindowVisibleHeight(curTokenDisplayId);
}
@Override
@@ -3858,7 +3916,7 @@
public void startImeTrace() {
ImeTracing.getInstance().startTrace(null /* printwriter */);
ArrayMap<IBinder, ClientState> clients;
- synchronized (mMethodMap) {
+ synchronized (ImfLock.class) {
clients = new ArrayMap<>(mClients);
}
for (ClientState state : clients.values()) {
@@ -3877,7 +3935,7 @@
public void stopImeTrace() {
ImeTracing.getInstance().stopTrace(null /* printwriter */);
ArrayMap<IBinder, ClientState> clients;
- synchronized (mMethodMap) {
+ synchronized (ImfLock.class) {
clients = new ArrayMap<>(mClients);
}
for (ClientState state : clients.values()) {
@@ -3892,10 +3950,10 @@
}
private void dumpDebug(ProtoOutputStream proto, long fieldId) {
- synchronized (mMethodMap) {
+ synchronized (ImfLock.class) {
final long token = proto.start(fieldId);
- proto.write(CUR_METHOD_ID, getSelectedMethodId());
- proto.write(CUR_SEQ, getSequenceNumber());
+ proto.write(CUR_METHOD_ID, getSelectedMethodIdLocked());
+ proto.write(CUR_SEQ, getSequenceNumberLocked());
proto.write(CUR_CLIENT, Objects.toString(mCurClient));
proto.write(CUR_FOCUSED_WINDOW_NAME,
mWindowManagerInternal.getWindowName(mCurFocusedWindow));
@@ -3906,17 +3964,17 @@
if (mCurAttribute != null) {
mCurAttribute.dumpDebug(proto, CUR_ATTRIBUTE);
}
- proto.write(CUR_ID, getCurId());
+ proto.write(CUR_ID, getCurIdLocked());
proto.write(SHOW_REQUESTED, mShowRequested);
proto.write(SHOW_EXPLICITLY_REQUESTED, mShowExplicitlyRequested);
proto.write(SHOW_FORCED, mShowForced);
proto.write(INPUT_SHOWN, mInputShown);
proto.write(IN_FULLSCREEN_MODE, mInFullscreenMode);
- proto.write(CUR_TOKEN, Objects.toString(getCurToken()));
+ proto.write(CUR_TOKEN, Objects.toString(getCurTokenLocked()));
proto.write(CUR_TOKEN_DISPLAY_ID, mCurTokenDisplayId);
proto.write(SYSTEM_READY, mSystemReady);
proto.write(LAST_SWITCH_USER_ID, mLastSwitchUserId);
- proto.write(HAVE_CONNECTION, hasConnection());
+ proto.write(HAVE_CONNECTION, hasConnectionLocked());
proto.write(BOUND_TO_METHOD, mBoundToMethod);
proto.write(IS_INTERACTIVE, mIsInteractive);
proto.write(BACK_DISPOSITION, mBackDisposition);
@@ -3933,15 +3991,15 @@
if (DEBUG) {
Slog.d(TAG, "Got the notification of a user action.");
}
- synchronized (mMethodMap) {
- if (getCurToken() != token) {
+ synchronized (ImfLock.class) {
+ if (getCurTokenLocked() != token) {
if (DEBUG) {
Slog.d(TAG, "Ignoring the user action notification from IMEs that are no longer"
+ " active.");
}
return;
}
- final InputMethodInfo imi = mMethodMap.get(getSelectedMethodId());
+ final InputMethodInfo imi = mMethodMap.get(getSelectedMethodIdLocked());
if (imi != null) {
mSwitchingController.onUserActionLocked(imi, mCurrentSubtype);
}
@@ -3951,7 +4009,7 @@
@BinderThread
private void applyImeVisibility(IBinder token, IBinder windowToken, boolean setVisible) {
Trace.traceBegin(TRACE_TAG_WINDOW_MANAGER, "IMMS.applyImeVisibility");
- synchronized (mMethodMap) {
+ synchronized (ImfLock.class) {
if (!calledWithValidTokenLocked(token)) {
return;
}
@@ -3975,7 +4033,7 @@
Trace.traceEnd(TRACE_TAG_WINDOW_MANAGER);
}
- @GuardedBy("mMethodMap")
+ @GuardedBy("ImfLock.class")
private void setInputMethodWithSubtypeIdLocked(IBinder token, String id, int subtypeId) {
if (token == null) {
if (mContext.checkCallingOrSelfPermission(
@@ -3985,7 +4043,7 @@
"Using null token requires permission "
+ android.Manifest.permission.WRITE_SECURE_SETTINGS);
}
- } else if (getCurToken() != token) {
+ } else if (getCurTokenLocked() != token) {
Slog.w(TAG, "Ignoring setInputMethod of uid " + Binder.getCallingUid()
+ " token: " + token);
return;
@@ -4000,6 +4058,7 @@
}
/** Called right after {@link IInputMethod#showSoftInput}. */
+ @GuardedBy("ImfLock.class")
private void onShowHideSoftInputRequested(boolean show, IBinder requestToken,
@SoftInputShowHideReason int reason) {
final WindowManagerInternal.ImeTargetInfo info =
@@ -4014,7 +4073,7 @@
@BinderThread
private void hideMySoftInput(@NonNull IBinder token, int flags) {
Trace.traceBegin(TRACE_TAG_WINDOW_MANAGER, "IMMS.hideMySoftInput");
- synchronized (mMethodMap) {
+ synchronized (ImfLock.class) {
if (!calledWithValidTokenLocked(token)) {
return;
}
@@ -4034,7 +4093,7 @@
@BinderThread
private void showMySoftInput(@NonNull IBinder token, int flags) {
Trace.traceBegin(TRACE_TAG_WINDOW_MANAGER, "IMMS.showMySoftInput");
- synchronized (mMethodMap) {
+ synchronized (ImfLock.class) {
if (!calledWithValidTokenLocked(token)) {
return;
}
@@ -4132,8 +4191,11 @@
final IBinder token = (IBinder) args.arg3;
((IInputMethod) args.arg1).showSoftInput(
token, msg.arg1 /* flags */, (ResultReceiver) args.arg2);
- final IBinder requestToken = mShowRequestWindowMap.get(token);
- onShowHideSoftInputRequested(true /* show */, requestToken, reason);
+ final IBinder requestToken;
+ synchronized (ImfLock.class) {
+ requestToken = mShowRequestWindowMap.get(token);
+ onShowHideSoftInputRequested(true /* show */, requestToken, reason);
+ }
} catch (RemoteException e) {
}
args.recycle();
@@ -4148,14 +4210,17 @@
final IBinder token = (IBinder) args.arg3;
((IInputMethod)args.arg1).hideSoftInput(
token, 0 /* flags */, (ResultReceiver) args.arg2);
- final IBinder requestToken = mHideRequestWindowMap.get(token);
- onShowHideSoftInputRequested(false /* show */, requestToken, reason);
+ final IBinder requestToken;
+ synchronized (ImfLock.class) {
+ requestToken = mHideRequestWindowMap.get(token);
+ onShowHideSoftInputRequested(false /* show */, requestToken, reason);
+ }
} catch (RemoteException e) {
}
args.recycle();
return true;
case MSG_HIDE_CURRENT_INPUT_METHOD:
- synchronized (mMethodMap) {
+ synchronized (ImfLock.class) {
final @SoftInputShowHideReason int reason = (int) msg.obj;
hideCurrentInputLocked(mCurFocusedWindow, 0, null, reason);
@@ -4165,8 +4230,10 @@
args = (SomeArgs)msg.obj;
try {
if (DEBUG) {
- Slog.v(TAG, "Sending attach of token: " + args.arg2 + " for display: "
- + mCurTokenDisplayId);
+ synchronized (ImfLock.class) {
+ Slog.v(TAG, "Sending attach of token: " + args.arg2 + " for display: "
+ + mCurTokenDisplayId);
+ }
}
final IBinder token = (IBinder) args.arg2;
((IInputMethod) args.arg1).initializeInternal(token,
@@ -4193,7 +4260,7 @@
return true;
}
case MSG_REMOVE_IME_SURFACE: {
- synchronized (mMethodMap) {
+ synchronized (ImfLock.class) {
try {
if (mEnabledSession != null && mEnabledSession.session != null
&& !mShowRequested) {
@@ -4206,7 +4273,7 @@
}
case MSG_REMOVE_IME_SURFACE_FROM_WINDOW: {
IBinder windowToken = (IBinder) msg.obj;
- synchronized (mMethodMap) {
+ synchronized (ImfLock.class) {
try {
if (windowToken == mCurFocusedWindow
&& mEnabledSession != null && mEnabledSession.session != null) {
@@ -4348,7 +4415,7 @@
}
private void handleSetInteractive(final boolean interactive) {
- synchronized (mMethodMap) {
+ synchronized (ImfLock.class) {
mIsInteractive = interactive;
updateSystemUiLocked(interactive ? mImeWindowVis : 0, mBackDisposition);
@@ -4357,7 +4424,7 @@
boolean reportToImeController = false;
try {
reportToImeController = mPlatformCompat.isChangeEnabledByUid(
- FINISH_INPUT_NO_FALLBACK_CONNECTION, getCurMethodUid());
+ FINISH_INPUT_NO_FALLBACK_CONNECTION, getCurMethodUidLocked());
} catch (RemoteException e) {
}
scheduleSetActiveToClient(mCurClient, mIsInteractive, mInFullscreenMode,
@@ -4372,7 +4439,7 @@
active ? 1 : 0, fullscreen ? 1 : 0, reportToImeController ? 1 : 0, 0, state));
}
- @GuardedBy("mMethodMap")
+ @GuardedBy("ImfLock.class")
private boolean chooseNewDefaultIMELocked() {
final InputMethodInfo imi = InputMethodUtils.getMostApplicableDefaultIME(
mSettings.getEnabledInputMethodListLocked());
@@ -4389,17 +4456,31 @@
static void queryInputMethodServicesInternal(Context context,
@UserIdInt int userId, ArrayMap<String, List<InputMethodSubtype>> additionalSubtypeMap,
- ArrayMap<String, InputMethodInfo> methodMap, ArrayList<InputMethodInfo> methodList) {
+ ArrayMap<String, InputMethodInfo> methodMap, ArrayList<InputMethodInfo> methodList,
+ @DirectBootAwareness int directBootAwareness) {
methodList.clear();
methodMap.clear();
- // Note: We do not specify PackageManager.MATCH_ENCRYPTION_* flags here because the default
- // behavior of PackageManager is exactly what we want. It by default picks up appropriate
- // services depending on the unlock state for the specified user.
+ final int directBootAwarenessFlags;
+ switch (directBootAwareness) {
+ case DirectBootAwareness.ANY:
+ directBootAwarenessFlags = PackageManager.MATCH_DIRECT_BOOT_AWARE
+ | PackageManager.MATCH_DIRECT_BOOT_UNAWARE;
+ break;
+ case DirectBootAwareness.AUTO:
+ directBootAwarenessFlags = PackageManager.MATCH_DIRECT_BOOT_AUTO;
+ break;
+ default:
+ directBootAwarenessFlags = PackageManager.MATCH_DIRECT_BOOT_AUTO;
+ Slog.e(TAG, "Unknown directBootAwareness=" + directBootAwareness
+ + ". Falling back to DirectBootAwareness.AUTO");
+ break;
+ }
+ final int flags = PackageManager.GET_META_DATA
+ | PackageManager.MATCH_DISABLED_UNTIL_USED_COMPONENTS
+ | directBootAwarenessFlags;
final List<ResolveInfo> services = context.getPackageManager().queryIntentServicesAsUser(
- new Intent(InputMethod.SERVICE_INTERFACE),
- PackageManager.GET_META_DATA | PackageManager.MATCH_DISABLED_UNTIL_USED_COMPONENTS,
- userId);
+ new Intent(InputMethod.SERVICE_INTERFACE), flags, userId);
methodList.ensureCapacity(services.size());
methodMap.ensureCapacity(services.size());
@@ -4434,7 +4515,7 @@
}
}
- @GuardedBy("mMethodMap")
+ @GuardedBy("ImfLock.class")
void buildInputMethodListLocked(boolean resetDefaultEnabledIme) {
if (DEBUG) {
Slog.d(TAG, "--- re-buildInputMethodList reset = " + resetDefaultEnabledIme
@@ -4448,7 +4529,7 @@
mMyPackageMonitor.clearKnownImePackageNamesLocked();
queryInputMethodServicesInternal(mContext, mSettings.getCurrentUserId(),
- mAdditionalSubtypeMap, mMethodMap, mMethodList);
+ mAdditionalSubtypeMap, mMethodMap, mMethodList, DirectBootAwareness.AUTO);
// Construct the set of possible IME packages for onPackageChanged() to avoid false
// negatives when the package state remains to be the same but only the component state is
@@ -4542,7 +4623,7 @@
mSettings.getCurrentUserId(), 0 /* unused */, inputMethodList).sendToTarget();
}
- @GuardedBy("mMethodMap")
+ @GuardedBy("ImfLock.class")
private void updateDefaultVoiceImeIfNeededLocked() {
final String systemSpeechRecognizer =
mContext.getString(com.android.internal.R.string.config_systemSpeechRecognizer);
@@ -4584,7 +4665,7 @@
intent.putExtra(Settings.EXTRA_INPUT_METHOD_ID, inputMethodId);
}
final int userId;
- synchronized (mMethodMap) {
+ synchronized (ImfLock.class) {
userId = mSettings.getCurrentUserId();
}
mContext.startActivityAsUser(intent, null, UserHandle.of(userId));
@@ -4608,7 +4689,7 @@
* @param enabled {@code true} if {@code id} needs to be enabled.
* @return {@code true} if the IME was previously enabled. {@code false} otherwise.
*/
- @GuardedBy("mMethodMap")
+ @GuardedBy("ImfLock.class")
private boolean setInputMethodEnabledLocked(String id, boolean enabled) {
List<Pair<String, ArrayList<String>>> enabledInputMethodsList = mSettings
.getEnabledInputMethodsAndSubtypeListLocked();
@@ -4644,10 +4725,11 @@
}
}
- @GuardedBy("mMethodMap")
+ @GuardedBy("ImfLock.class")
private void setSelectedInputMethodAndSubtypeLocked(InputMethodInfo imi, int subtypeId,
boolean setSubtypeOnly) {
- mSettings.saveCurrentInputMethodAndSubtypeToHistory(getSelectedMethodId(), mCurrentSubtype);
+ mSettings.saveCurrentInputMethodAndSubtypeToHistory(getSelectedMethodIdLocked(),
+ mCurrentSubtype);
// Set Subtype here
if (imi == null || subtypeId < 0) {
@@ -4671,7 +4753,7 @@
}
}
- @GuardedBy("mMethodMap")
+ @GuardedBy("ImfLock.class")
private void resetSelectedInputMethodAndSubtypeLocked(String newDefaultIme) {
InputMethodInfo imi = mMethodMap.get(newDefaultIme);
int lastSubtypeId = NOT_A_SUBTYPE_ID;
@@ -4695,7 +4777,7 @@
*/
@Override
public InputMethodSubtype getCurrentInputMethodSubtype() {
- synchronized (mMethodMap) {
+ synchronized (ImfLock.class) {
// TODO: Make this work even for non-current users?
if (!calledFromValidUserLocked()) {
return null;
@@ -4704,9 +4786,9 @@
}
}
- @GuardedBy("mMethodMap")
+ @GuardedBy("ImfLock.class")
InputMethodSubtype getCurrentInputMethodSubtypeLocked() {
- String selectedMethodId = getSelectedMethodId();
+ String selectedMethodId = getSelectedMethodIdLocked();
if (selectedMethodId == null) {
return null;
}
@@ -4745,19 +4827,14 @@
return mCurrentSubtype;
}
- @Nullable
- String getCurrentMethodId() {
- return getSelectedMethodId();
- }
-
private List<InputMethodInfo> getInputMethodListAsUser(@UserIdInt int userId) {
- synchronized (mMethodMap) {
- return getInputMethodListLocked(userId);
+ synchronized (ImfLock.class) {
+ return getInputMethodListLocked(userId, DirectBootAwareness.AUTO);
}
}
private List<InputMethodInfo> getEnabledInputMethodListAsUser(@UserIdInt int userId) {
- synchronized (mMethodMap) {
+ synchronized (ImfLock.class) {
return getEnabledInputMethodListLocked(userId);
}
}
@@ -4765,7 +4842,7 @@
private void onCreateInlineSuggestionsRequest(@UserIdInt int userId,
InlineSuggestionsRequestInfo requestInfo,
IInlineSuggestionsRequestCallback callback) {
- synchronized (mMethodMap) {
+ synchronized (ImfLock.class) {
onCreateInlineSuggestionsRequestLocked(userId, requestInfo, callback);
}
}
@@ -4777,12 +4854,12 @@
new ArrayMap<>();
AdditionalSubtypeUtils.load(additionalSubtypeMap, userId);
queryInputMethodServicesInternal(mContext, userId, additionalSubtypeMap,
- methodMap, methodList);
+ methodMap, methodList, DirectBootAwareness.AUTO);
return methodMap;
}
private boolean switchToInputMethod(String imeId, @UserIdInt int userId) {
- synchronized (mMethodMap) {
+ synchronized (ImfLock.class) {
if (userId == mSettings.getCurrentUserId()) {
if (!mMethodMap.containsKey(imeId)
|| !mSettings.getEnabledInputMethodListLocked()
@@ -4808,7 +4885,7 @@
}
private boolean setInputMethodEnabled(String imeId, boolean enabled, @UserIdInt int userId) {
- synchronized (mMethodMap) {
+ synchronized (ImfLock.class) {
if (userId == mSettings.getCurrentUserId()) {
if (!mMethodMap.containsKey(imeId)) {
return false; // IME is not found.
@@ -4840,7 +4917,7 @@
int displayId) {
//TODO(b/150843766): Check if Input Token is valid.
final IBinder curHostInputToken;
- synchronized (mMethodMap) {
+ synchronized (ImfLock.class) {
if (displayId != mCurTokenDisplayId || mCurHostInputToken == null) {
return false;
}
@@ -4850,7 +4927,7 @@
}
private void reportImeControl(@Nullable IBinder windowToken, boolean imeParentChanged) {
- synchronized (mMethodMap) {
+ synchronized (ImfLock.class) {
if (mCurFocusedWindow != windowToken) {
// mCurPerceptible was set by the focused window, but it is no longer in control,
// so we reset mCurPerceptible.
@@ -4958,13 +5035,13 @@
throw new InvalidParameterException("contentUri must have content scheme");
}
- synchronized (mMethodMap) {
+ synchronized (ImfLock.class) {
final int uid = Binder.getCallingUid();
- if (getSelectedMethodId() == null) {
+ if (getSelectedMethodIdLocked() == null) {
return null;
}
- if (getCurToken() != token) {
- Slog.e(TAG, "Ignoring createInputContentUriToken mCurToken=" + getCurToken()
+ if (getCurTokenLocked() != token) {
+ Slog.e(TAG, "Ignoring createInputContentUriToken mCurToken=" + getCurTokenLocked()
+ " token=" + token);
return null;
}
@@ -4997,7 +5074,7 @@
@BinderThread
private void reportFullscreenMode(@NonNull IBinder token, boolean fullscreen) {
- synchronized (mMethodMap) {
+ synchronized (ImfLock.class) {
if (!calledWithValidTokenLocked(token)) {
return;
}
@@ -5080,7 +5157,7 @@
final Printer p = new PrintWriterPrinter(pw);
- synchronized (mMethodMap) {
+ synchronized (ImfLock.class) {
p.println("Current Input Method Manager state:");
int N = mMethodList.size();
p.println(" Input Methods: mMethodMapUpdateCount=" + mMethodMapUpdateCount);
@@ -5099,24 +5176,24 @@
p.println(" sessionRequested=" + ci.sessionRequested);
p.println(" curSession=" + ci.curSession);
}
- p.println(" mCurMethodId=" + getSelectedMethodId());
+ p.println(" mCurMethodId=" + getSelectedMethodIdLocked());
client = mCurClient;
- p.println(" mCurClient=" + client + " mCurSeq=" + getSequenceNumber());
+ p.println(" mCurClient=" + client + " mCurSeq=" + getSequenceNumberLocked());
p.println(" mCurPerceptible=" + mCurPerceptible);
p.println(" mCurFocusedWindow=" + mCurFocusedWindow
+ " softInputMode=" +
InputMethodDebug.softInputModeToString(mCurFocusedWindowSoftInputMode)
+ " client=" + mCurFocusedWindowClient);
focusedWindowClient = mCurFocusedWindowClient;
- p.println(" mCurId=" + getCurId() + " mHaveConnection=" + hasConnection()
+ p.println(" mCurId=" + getCurIdLocked() + " mHaveConnection=" + hasConnectionLocked()
+ " mBoundToMethod=" + mBoundToMethod + " mVisibleBound="
+ mBindingController.isVisibleBound());
- p.println(" mCurToken=" + getCurToken());
+ p.println(" mCurToken=" + getCurTokenLocked());
p.println(" mCurTokenDisplayId=" + mCurTokenDisplayId);
p.println(" mCurHostInputToken=" + mCurHostInputToken);
- p.println(" mCurIntent=" + getCurIntent());
- method = getCurMethod();
- p.println(" mCurMethod=" + getCurMethod());
+ p.println(" mCurIntent=" + getCurIntentLocked());
+ method = getCurMethodLocked();
+ p.println(" mCurMethod=" + getCurMethodLocked());
p.println(" mEnabledSession=" + mEnabledSession);
p.println(" mShowRequested=" + mShowRequested
+ " mShowExplicitlyRequested=" + mShowExplicitlyRequested
@@ -5365,7 +5442,7 @@
@BinderThread
@ShellCommandResult
private int getLastSwitchUserId(@NonNull ShellCommand shellCommand) {
- synchronized (mMethodMap) {
+ synchronized (ImfLock.class) {
shellCommand.getOutPrintWriter().println(mLastSwitchUserId);
return ShellCommandResult.SUCCESS;
}
@@ -5400,13 +5477,13 @@
break;
}
}
- synchronized (mMethodMap) {
+ synchronized (ImfLock.class) {
final PrintWriter pr = shellCommand.getOutPrintWriter();
final int[] userIds = InputMethodUtils.resolveUserId(userIdToBeResolved,
mSettings.getCurrentUserId(), shellCommand.getErrPrintWriter());
for (int userId : userIds) {
final List<InputMethodInfo> methods = all
- ? getInputMethodListLocked(userId)
+ ? getInputMethodListLocked(userId, DirectBootAwareness.AUTO)
: getEnabledInputMethodListLocked(userId);
if (userIds.length > 1) {
pr.print("User #");
@@ -5442,7 +5519,7 @@
final PrintWriter out = shellCommand.getOutPrintWriter();
final PrintWriter error = shellCommand.getErrPrintWriter();
boolean hasFailed = false;
- synchronized (mMethodMap) {
+ synchronized (ImfLock.class) {
final int[] userIds = InputMethodUtils.resolveUserId(userIdToBeResolved,
mSettings.getCurrentUserId(), shellCommand.getErrPrintWriter());
for (int userId : userIds) {
@@ -5496,7 +5573,7 @@
* @return {@code false} if it fails to enable the IME. {@code false} otherwise.
*/
@BinderThread
- @GuardedBy("mMethodMap")
+ @GuardedBy("ImfLock.class")
private boolean handleShellCommandEnableDisableInputMethodInternalLocked(
@UserIdInt int userId, String imeId, boolean enabled, PrintWriter out,
PrintWriter error) {
@@ -5566,7 +5643,7 @@
final PrintWriter out = shellCommand.getOutPrintWriter();
final PrintWriter error = shellCommand.getErrPrintWriter();
boolean hasFailed = false;
- synchronized (mMethodMap) {
+ synchronized (ImfLock.class) {
final int[] userIds = InputMethodUtils.resolveUserId(userIdToBeResolved,
mSettings.getCurrentUserId(), shellCommand.getErrPrintWriter());
for (int userId : userIds) {
@@ -5604,7 +5681,7 @@
private int handleShellCommandResetInputMethod(@NonNull ShellCommand shellCommand) {
final PrintWriter out = shellCommand.getOutPrintWriter();
final int userIdToBeResolved = handleOptionsForCommandsThatOnlyHaveUserOption(shellCommand);
- synchronized (mMethodMap) {
+ synchronized (ImfLock.class) {
final int[] userIds = InputMethodUtils.resolveUserId(userIdToBeResolved,
mSettings.getCurrentUserId(), shellCommand.getErrPrintWriter());
for (int userId : userIds) {
@@ -5616,17 +5693,21 @@
if (userId == mSettings.getCurrentUserId()) {
hideCurrentInputLocked(mCurFocusedWindow, 0, null,
SoftInputShowHideReason.HIDE_RESET_SHELL_COMMAND);
- mBindingController.unbindCurrentMethodLocked();
+ mBindingController.unbindCurrentMethod();
// Reset the current IME
resetSelectedInputMethodAndSubtypeLocked(null);
// Also reset the settings of the current IME
mSettings.putSelectedInputMethod(null);
// Disable all enabled IMEs.
- mSettings.getEnabledInputMethodListLocked().forEach(
- imi -> setInputMethodEnabledLocked(imi.getId(), false));
+ for (InputMethodInfo inputMethodInfo :
+ mSettings.getEnabledInputMethodListLocked()) {
+ setInputMethodEnabledLocked(inputMethodInfo.getId(), false);
+ }
// Re-enable with default enabled IMEs.
- InputMethodUtils.getDefaultEnabledImes(mContext, mMethodList).forEach(
- imi -> setInputMethodEnabledLocked(imi.getId(), true));
+ for (InputMethodInfo imi :
+ InputMethodUtils.getDefaultEnabledImes(mContext, mMethodList)) {
+ setInputMethodEnabledLocked(imi.getId(), true);
+ }
updateInputMethodsFromSettingsLocked(true /* enabledMayChange */);
InputMethodUtils.setNonSelectedSystemImesDisabledUntilUsed(mIPackageManager,
mSettings.getEnabledInputMethodListLocked(),
@@ -5641,7 +5722,7 @@
new ArrayMap<>();
AdditionalSubtypeUtils.load(additionalSubtypeMap, userId);
queryInputMethodServicesInternal(mContext, userId, additionalSubtypeMap,
- methodMap, methodList);
+ methodMap, methodList, DirectBootAwareness.AUTO);
final InputMethodSettings settings = new InputMethodSettings(
mContext.getResources(), mContext.getContentResolver(), methodMap,
userId, false);
@@ -5678,7 +5759,7 @@
final PrintWriter pw = shellCommand.getOutPrintWriter();
switch (cmd) {
case "start":
- ImeTracing.getInstance().getInstance().startTrace(pw);
+ ImeTracing.getInstance().startTrace(pw);
break; // proceed to the next step to update the IME client processes.
case "stop":
ImeTracing.getInstance().stopTrace(pw);
@@ -5695,7 +5776,7 @@
}
boolean isImeTraceEnabled = ImeTracing.getInstance().isEnabled();
ArrayMap<IBinder, ClientState> clients;
- synchronized (mMethodMap) {
+ synchronized (ImfLock.class) {
clients = new ArrayMap<>(mClients);
}
for (ClientState state : clients.values()) {
diff --git a/services/core/java/com/android/server/inputmethod/InputMethodMenuController.java b/services/core/java/com/android/server/inputmethod/InputMethodMenuController.java
index f70ad05..132be7d 100644
--- a/services/core/java/com/android/server/inputmethod/InputMethodMenuController.java
+++ b/services/core/java/com/android/server/inputmethod/InputMethodMenuController.java
@@ -46,6 +46,7 @@
import android.widget.Switch;
import android.widget.TextView;
+import com.android.internal.annotations.GuardedBy;
import com.android.internal.annotations.VisibleForTesting;
import com.android.server.LocalServices;
import com.android.server.inputmethod.InputMethodSubtypeSwitchingController.ImeSubtypeListItem;
@@ -98,7 +99,7 @@
int lastInputMethodSubtypeId = mSettings.getSelectedInputMethodSubtypeId(lastInputMethodId);
if (DEBUG) Slog.v(TAG, "Current IME: " + lastInputMethodId);
- synchronized (mMethodMap) {
+ synchronized (ImfLock.class) {
final List<ImeSubtypeListItem> imList = mSwitchingController
.getSortedInputMethodAndSubtypeListForImeMenuLocked(
showAuxSubtypes, isScreenLocked);
@@ -112,7 +113,7 @@
final InputMethodSubtype currentSubtype =
mService.getCurrentInputMethodSubtypeLocked();
if (currentSubtype != null) {
- final String curMethodId = mService.getCurrentMethodId();
+ final String curMethodId = mService.getSelectedMethodIdLocked();
final InputMethodInfo currentImi = mMethodMap.get(curMethodId);
lastInputMethodSubtypeId = InputMethodUtils.getSubtypeIdFromHashCode(
currentImi, currentSubtype.hashCode());
@@ -175,7 +176,7 @@
final ImeSubtypeListAdapter adapter = new ImeSubtypeListAdapter(dialogContext,
com.android.internal.R.layout.input_method_switch_item, imList, checkedItem);
final DialogInterface.OnClickListener choiceListener = (dialog, which) -> {
- synchronized (mMethodMap) {
+ synchronized (ImfLock.class) {
if (mIms == null || mIms.length <= which || mSubtypeIds == null
|| mSubtypeIds.length <= which) {
return;
@@ -250,11 +251,12 @@
}
void hideInputMethodMenu() {
- synchronized (mMethodMap) {
+ synchronized (ImfLock.class) {
hideInputMethodMenuLocked();
}
}
+ @GuardedBy("ImfLock.class")
void hideInputMethodMenuLocked() {
if (DEBUG) Slog.v(TAG, "Hide switching menu");
@@ -299,7 +301,7 @@
if (DEBUG) {
Slog.w(TAG, "HardKeyboardStatusChanged: available=" + available);
}
- synchronized (mMethodMap) {
+ synchronized (ImfLock.class) {
if (mSwitchingDialog != null && mSwitchingDialogTitleView != null
&& mSwitchingDialog.isShowing()) {
mSwitchingDialogTitleView.findViewById(
diff --git a/services/core/java/com/android/server/inputmethod/InputMethodSubtypeSwitchingController.java b/services/core/java/com/android/server/inputmethod/InputMethodSubtypeSwitchingController.java
index c340a2b..f8894c6 100644
--- a/services/core/java/com/android/server/inputmethod/InputMethodSubtypeSwitchingController.java
+++ b/services/core/java/com/android/server/inputmethod/InputMethodSubtypeSwitchingController.java
@@ -39,7 +39,7 @@
* InputMethodSubtypeSwitchingController controls the switching behavior of the subtypes.
*
* <p>This class is designed to be used from and only from {@link InputMethodManagerService} by
- * using {@link InputMethodManagerService#mMethodMap} as a global lock.</p>
+ * using {@link ImfLock ImfLock.class} as a global lock.</p>
*/
final class InputMethodSubtypeSwitchingController {
private static final String TAG = InputMethodSubtypeSwitchingController.class.getSimpleName();
diff --git a/services/core/java/com/android/server/location/contexthub/ContextHubClientBroker.java b/services/core/java/com/android/server/location/contexthub/ContextHubClientBroker.java
index 0fd7cc1..e40d86a 100644
--- a/services/core/java/com/android/server/location/contexthub/ContextHubClientBroker.java
+++ b/services/core/java/com/android/server/location/contexthub/ContextHubClientBroker.java
@@ -331,15 +331,7 @@
mAppOpsManager = context.getSystemService(AppOpsManager.class);
startMonitoringOpChanges();
-
- HostEndpointInfo info = new HostEndpointInfo();
- info.hostEndpointId = (char) mHostEndPointId;
- info.packageName = mPackage;
- info.attributionTag = mAttributionTag;
- info.type = (mUid == Process.SYSTEM_UID)
- ? HostEndpointInfo.Type.TYPE_FRAMEWORK
- : HostEndpointInfo.Type.TYPE_APP;
- mContextHubProxy.onHostEndpointConnected(info);
+ sendHostEndpointConnectedEvent();
}
/* package */ ContextHubClientBroker(
@@ -556,6 +548,9 @@
/* package */ void onHubReset() {
invokeCallback(callback -> callback.onHubReset());
sendPendingIntent(() -> createIntent(ContextHubManager.EVENT_HUB_RESET));
+
+ // Re-send the host endpoint connected event as the Context Hub restarted.
+ sendHostEndpointConnectedEvent();
}
/**
@@ -895,6 +890,17 @@
}
}
+ private void sendHostEndpointConnectedEvent() {
+ HostEndpointInfo info = new HostEndpointInfo();
+ info.hostEndpointId = (char) mHostEndPointId;
+ info.packageName = mPackage;
+ info.attributionTag = mAttributionTag;
+ info.type = (mUid == Process.SYSTEM_UID)
+ ? HostEndpointInfo.Type.TYPE_FRAMEWORK
+ : HostEndpointInfo.Type.TYPE_APP;
+ mContextHubProxy.onHostEndpointConnected(info);
+ }
+
/**
* Dump debugging info as ClientBrokerProto
*
diff --git a/services/core/java/com/android/server/location/eventlog/LocalEventLog.java b/services/core/java/com/android/server/location/eventlog/LocalEventLog.java
index 47146c1..d08e5dc 100644
--- a/services/core/java/com/android/server/location/eventlog/LocalEventLog.java
+++ b/services/core/java/com/android/server/location/eventlog/LocalEventLog.java
@@ -21,26 +21,23 @@
import android.annotation.Nullable;
import com.android.internal.annotations.GuardedBy;
+import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.util.Preconditions;
import java.lang.reflect.Array;
+import java.util.ArrayList;
import java.util.Arrays;
+import java.util.ConcurrentModificationException;
import java.util.NoSuchElementException;
import java.util.Objects;
/**
* An in-memory event log to support historical event information. The log is of a constant size,
* and new events will overwrite old events as the log fills up.
- *
- * @param <T> log event type
*/
public class LocalEventLog<T> {
- /**
- * Consumer of log events for iterating over the log.
- *
- * @param <T> log event type
- */
+ /** Consumer of log events for iterating over the log. */
public interface LogConsumer<T> {
/** Invoked with a time and a logEvent. */
void acceptLog(long time, T logEvent);
@@ -48,12 +45,13 @@
// masks for the entries field. 1 bit is used to indicate whether this is a filler event or not,
// and 31 bits to store the time delta.
- private static final int IS_FILLER_MASK = 0b10000000000000000000000000000000;
+ private static final int IS_FILLER_MASK = 0b10000000000000000000000000000000;
private static final int TIME_DELTA_MASK = 0b01111111111111111111111111111111;
private static final int IS_FILLER_OFFSET = countTrailingZeros(IS_FILLER_MASK);
private static final int TIME_DELTA_OFFSET = countTrailingZeros(TIME_DELTA_MASK);
+ @VisibleForTesting
static final int MAX_TIME_DELTA = (1 << bitCount(TIME_DELTA_MASK)) - 1;
private static int countTrailingZeros(int i) {
@@ -79,7 +77,7 @@
return (entry & IS_FILLER_MASK) != 0;
}
- // circular buffer of log entries and events. each entry corrosponds to the log event at the
+ // circular buffer of log entries and events. each entry corresponds to the log event at the
// same index. the log entry holds the filler status and time delta according to the bit masks
// above, and the log event is the log event.
@@ -103,6 +101,9 @@
@GuardedBy("this")
long mLastLogTime;
+ @GuardedBy("this")
+ long mModificationCount;
+
@SuppressWarnings("unchecked")
public LocalEventLog(int size, Class<T> clazz) {
Preconditions.checkArgument(size > 0);
@@ -143,6 +144,7 @@
if (isEmpty()) {
mStartTime = time;
mLastLogTime = mStartTime;
+ mModificationCount++;
}
addLogEventInternal(false, (int) delta, logEvent);
@@ -156,6 +158,7 @@
if (mLogSize == mEntries.length) {
// if log is full, size will remain the same, but update the start time
mStartTime += getTimeDelta(mEntries[startIndex()]);
+ mModificationCount++;
} else {
// otherwise add an item
mLogSize++;
@@ -170,11 +173,12 @@
/** Clears the log of all entries. */
public synchronized void clear() {
- // clear entries to allow gc
+ // clear entries to aid gc
Arrays.fill(mLogEvents, null);
mLogEndIndex = 0;
mLogSize = 0;
+ mModificationCount++;
mStartTime = -1;
mLastLogTime = -1;
@@ -186,7 +190,10 @@
return mLogSize == 0;
}
- /** Iterates over the event log, passing each log string to the given consumer. */
+ /**
+ * Iterates over the event log, passing each log event to the given consumer. Locks the log
+ * while executing so that {@link ConcurrentModificationException}s cannot occur.
+ */
public synchronized void iterate(LogConsumer<? super T> consumer) {
LogIterator it = new LogIterator();
while (it.hasNext()) {
@@ -195,15 +202,53 @@
}
}
+ /**
+ * Iterates over all the given event logs in time order, passing each log event to the given
+ * consumer. It is the caller's responsibility to ensure that {@link
+ * ConcurrentModificationException}s cannot occur, whether through locking or other means.
+ */
+ @SafeVarargs
+ public static <T> void iterate(LogConsumer<? super T> consumer, LocalEventLog<T>... logs) {
+ ArrayList<LocalEventLog<T>.LogIterator> its = new ArrayList<>(logs.length);
+ for (LocalEventLog<T> log : logs) {
+ LocalEventLog<T>.LogIterator it = log.new LogIterator();
+ if (it.hasNext()) {
+ its.add(it);
+ it.next();
+ }
+ }
+
+ while (true) {
+ LocalEventLog<T>.LogIterator next = null;
+ for (LocalEventLog<T>.LogIterator it : its) {
+ if (it != null && (next == null || it.getTime() < next.getTime())) {
+ next = it;
+ }
+ }
+
+ if (next == null) {
+ return;
+ }
+
+ consumer.acceptLog(next.getTime(), next.getLog());
+
+ if (next.hasNext()) {
+ next.next();
+ } else {
+ its.remove(next);
+ }
+ }
+ }
+
// returns the index of the first element
@GuardedBy("this")
- private int startIndex() {
+ int startIndex() {
return wrapIndex(mLogEndIndex - mLogSize);
}
// returns the index after this one
@GuardedBy("this")
- private int incrementIndex(int index) {
+ int incrementIndex(int index) {
if (index == -1) {
return startIndex();
} else if (index >= 0) {
@@ -215,12 +260,15 @@
// rolls over the given index if necessary
@GuardedBy("this")
- private int wrapIndex(int index) {
+ int wrapIndex(int index) {
// java modulo will keep negative sign, we need to rollover
return (index % mEntries.length + mEntries.length) % mEntries.length;
}
- private class LogIterator {
+ /** Iterator over log times and events. */
+ protected final class LogIterator {
+
+ private final long mModificationCount;
private long mLogTime;
private int mIndex;
@@ -229,8 +277,10 @@
private long mCurrentTime;
private T mCurrentLogEvent;
- LogIterator() {
+ public LogIterator() {
synchronized (LocalEventLog.this) {
+ mModificationCount = LocalEventLog.this.mModificationCount;
+
mLogTime = mStartTime;
mIndex = -1;
mCount = -1;
@@ -241,6 +291,7 @@
public boolean hasNext() {
synchronized (LocalEventLog.this) {
+ checkModifications();
return mCount < mLogSize;
}
}
@@ -277,5 +328,12 @@
}
} while (mCount < mLogSize && isFiller(mEntries[mIndex]));
}
+
+ @GuardedBy("LocalEventLog.this")
+ private void checkModifications() {
+ if (mModificationCount != LocalEventLog.this.mModificationCount) {
+ throw new ConcurrentModificationException();
+ }
+ }
}
}
diff --git a/services/core/java/com/android/server/location/eventlog/LocationEventLog.java b/services/core/java/com/android/server/location/eventlog/LocationEventLog.java
index 94953e0..45436e7 100644
--- a/services/core/java/com/android/server/location/eventlog/LocationEventLog.java
+++ b/services/core/java/com/android/server/location/eventlog/LocationEventLog.java
@@ -52,16 +52,28 @@
if (D) {
return 600;
} else {
+ return 300;
+ }
+ }
+
+ private static int getLocationsLogSize() {
+ if (D) {
return 200;
+ } else {
+ return 100;
}
}
@GuardedBy("mAggregateStats")
private final ArrayMap<String, ArrayMap<CallerIdentity, AggregateStats>> mAggregateStats;
- public LocationEventLog() {
+ @GuardedBy("this")
+ private final LocationsEventLog mLocationsLog;
+
+ private LocationEventLog() {
super(getLogSize(), Object.class);
mAggregateStats = new ArrayMap<>(4);
+ mLocationsLog = new LocationsEventLog(getLocationsLogSize());
}
/** Copies out all aggregated stats. */
@@ -95,39 +107,39 @@
/** Logs a user switched event. */
public void logUserSwitched(int userIdFrom, int userIdTo) {
- addLogEvent(new UserSwitchedEvent(userIdFrom, userIdTo));
+ addLog(new UserSwitchedEvent(userIdFrom, userIdTo));
}
/** Logs a location enabled/disabled event. */
public void logLocationEnabled(int userId, boolean enabled) {
- addLogEvent(new LocationEnabledEvent(userId, enabled));
+ addLog(new LocationEnabledEvent(userId, enabled));
}
/** Logs a location enabled/disabled event. */
public void logAdasLocationEnabled(int userId, boolean enabled) {
- addLogEvent(new LocationAdasEnabledEvent(userId, enabled));
+ addLog(new LocationAdasEnabledEvent(userId, enabled));
}
/** Logs a location provider enabled/disabled event. */
public void logProviderEnabled(String provider, int userId, boolean enabled) {
- addLogEvent(new ProviderEnabledEvent(provider, userId, enabled));
+ addLog(new ProviderEnabledEvent(provider, userId, enabled));
}
/** Logs a location provider being replaced/unreplaced by a mock provider. */
public void logProviderMocked(String provider, boolean mocked) {
- addLogEvent(new ProviderMockedEvent(provider, mocked));
+ addLog(new ProviderMockedEvent(provider, mocked));
}
/** Logs a new client registration for a location provider. */
public void logProviderClientRegistered(String provider, CallerIdentity identity,
LocationRequest request) {
- addLogEvent(new ProviderClientRegisterEvent(provider, true, identity, request));
+ addLog(new ProviderClientRegisterEvent(provider, true, identity, request));
getAggregateStats(provider, identity).markRequestAdded(request.getIntervalMillis());
}
/** Logs a client unregistration for a location provider. */
public void logProviderClientUnregistered(String provider, CallerIdentity identity) {
- addLogEvent(new ProviderClientRegisterEvent(provider, false, identity, null));
+ addLog(new ProviderClientRegisterEvent(provider, false, identity, null));
getAggregateStats(provider, identity).markRequestRemoved();
}
@@ -144,7 +156,7 @@
/** Logs a client for a location provider entering the foreground state. */
public void logProviderClientForeground(String provider, CallerIdentity identity) {
if (D) {
- addLogEvent(new ProviderClientForegroundEvent(provider, true, identity));
+ addLog(new ProviderClientForegroundEvent(provider, true, identity));
}
getAggregateStats(provider, identity).markRequestForeground();
}
@@ -152,7 +164,7 @@
/** Logs a client for a location provider leaving the foreground state. */
public void logProviderClientBackground(String provider, CallerIdentity identity) {
if (D) {
- addLogEvent(new ProviderClientForegroundEvent(provider, false, identity));
+ addLog(new ProviderClientForegroundEvent(provider, false, identity));
}
getAggregateStats(provider, identity).markRequestBackground();
}
@@ -160,32 +172,34 @@
/** Logs a client for a location provider entering the permitted state. */
public void logProviderClientPermitted(String provider, CallerIdentity identity) {
if (D) {
- addLogEvent(new ProviderClientPermittedEvent(provider, true, identity));
+ addLog(new ProviderClientPermittedEvent(provider, true, identity));
}
}
/** Logs a client for a location provider leaving the permitted state. */
public void logProviderClientUnpermitted(String provider, CallerIdentity identity) {
if (D) {
- addLogEvent(new ProviderClientPermittedEvent(provider, false, identity));
+ addLog(new ProviderClientPermittedEvent(provider, false, identity));
}
}
/** Logs a change to the provider request for a location provider. */
public void logProviderUpdateRequest(String provider, ProviderRequest request) {
- addLogEvent(new ProviderUpdateEvent(provider, request));
+ addLog(new ProviderUpdateEvent(provider, request));
}
/** Logs a new incoming location for a location provider. */
public void logProviderReceivedLocations(String provider, int numLocations) {
- addLogEvent(new ProviderReceiveLocationEvent(provider, numLocations));
+ synchronized (this) {
+ mLocationsLog.logProviderReceivedLocations(provider, numLocations);
+ }
}
/** Logs a location deliver for a client of a location provider. */
public void logProviderDeliveredLocations(String provider, int numLocations,
CallerIdentity identity) {
- if (D) {
- addLogEvent(new ProviderDeliverLocationEvent(provider, numLocations, identity));
+ synchronized (this) {
+ mLocationsLog.logProviderDeliveredLocations(provider, numLocations, identity);
}
getAggregateStats(provider, identity).markLocationDelivered();
}
@@ -193,19 +207,24 @@
/** Logs that a provider has entered or exited stationary throttling. */
public void logProviderStationaryThrottled(String provider, boolean throttled,
ProviderRequest request) {
- addLogEvent(new ProviderStationaryThrottledEvent(provider, throttled, request));
+ addLog(new ProviderStationaryThrottledEvent(provider, throttled, request));
}
/** Logs that the location power save mode has changed. */
public void logLocationPowerSaveMode(
@LocationPowerSaveMode int locationPowerSaveMode) {
- addLogEvent(new LocationPowerSaveModeEvent(locationPowerSaveMode));
+ addLog(new LocationPowerSaveModeEvent(locationPowerSaveMode));
}
- private void addLogEvent(Object logEvent) {
+ private void addLog(Object logEvent) {
addLog(SystemClock.elapsedRealtime(), logEvent);
}
+ @Override
+ public synchronized void iterate(LogConsumer<? super Object> consumer) {
+ iterate(consumer, this, mLocationsLog);
+ }
+
public void iterate(Consumer<String> consumer) {
iterate(consumer, null);
}
@@ -488,6 +507,26 @@
}
}
+ private static final class LocationsEventLog extends LocalEventLog<Object> {
+
+ LocationsEventLog(int size) {
+ super(size, Object.class);
+ }
+
+ public void logProviderReceivedLocations(String provider, int numLocations) {
+ addLog(new ProviderReceiveLocationEvent(provider, numLocations));
+ }
+
+ public void logProviderDeliveredLocations(String provider, int numLocations,
+ CallerIdentity identity) {
+ addLog(new ProviderDeliverLocationEvent(provider, numLocations, identity));
+ }
+
+ private void addLog(Object logEvent) {
+ this.addLog(SystemClock.elapsedRealtime(), logEvent);
+ }
+ }
+
/**
* Aggregate statistics for a single package under a single provider.
*/
diff --git a/services/core/java/com/android/server/location/gnss/GnssMeasurementsProvider.java b/services/core/java/com/android/server/location/gnss/GnssMeasurementsProvider.java
index 699f143..7bb0d48 100644
--- a/services/core/java/com/android/server/location/gnss/GnssMeasurementsProvider.java
+++ b/services/core/java/com/android/server/location/gnss/GnssMeasurementsProvider.java
@@ -114,7 +114,8 @@
protected boolean registerWithService(GnssMeasurementRequest request,
Collection<GnssListenerRegistration> registrations) {
if (mGnssNative.startMeasurementCollection(request.isFullTracking(),
- request.isCorrelationVectorOutputsEnabled())) {
+ request.isCorrelationVectorOutputsEnabled(),
+ request.getIntervalMillis())) {
if (D) {
Log.d(TAG, "starting gnss measurements (" + request + ")");
}
diff --git a/services/core/java/com/android/server/location/gnss/hal/GnssNative.java b/services/core/java/com/android/server/location/gnss/hal/GnssNative.java
index 1eef0de..cc5dcf30 100644
--- a/services/core/java/com/android/server/location/gnss/hal/GnssNative.java
+++ b/services/core/java/com/android/server/location/gnss/hal/GnssNative.java
@@ -735,9 +735,10 @@
* Starts measurement collection.
*/
public boolean startMeasurementCollection(boolean enableFullTracking,
- boolean enableCorrVecOutputs) {
+ boolean enableCorrVecOutputs, int intervalMillis) {
Preconditions.checkState(mRegistered);
- return mGnssHal.startMeasurementCollection(enableFullTracking, enableCorrVecOutputs);
+ return mGnssHal.startMeasurementCollection(enableFullTracking, enableCorrVecOutputs,
+ intervalMillis);
}
/**
@@ -1310,8 +1311,9 @@
}
protected boolean startMeasurementCollection(boolean enableFullTracking,
- boolean enableCorrVecOutputs) {
- return native_start_measurement_collection(enableFullTracking, enableCorrVecOutputs);
+ boolean enableCorrVecOutputs, int intervalMillis) {
+ return native_start_measurement_collection(enableFullTracking, enableCorrVecOutputs,
+ intervalMillis);
}
protected boolean stopMeasurementCollection() {
@@ -1475,7 +1477,7 @@
private static native boolean native_is_measurement_supported();
private static native boolean native_start_measurement_collection(boolean enableFullTracking,
- boolean enableCorrVecOutputs);
+ boolean enableCorrVecOutputs, int intervalMillis);
private static native boolean native_stop_measurement_collection();
diff --git a/services/core/java/com/android/server/net/NetworkPolicyManagerService.java b/services/core/java/com/android/server/net/NetworkPolicyManagerService.java
index 0a47cbb..65c5b88 100644
--- a/services/core/java/com/android/server/net/NetworkPolicyManagerService.java
+++ b/services/core/java/com/android/server/net/NetworkPolicyManagerService.java
@@ -2005,7 +2005,7 @@
for (final NetworkStateSnapshot snapshot : snapshots) {
mNetIdToSubId.put(snapshot.getNetwork().getNetId(), parseSubId(snapshot));
- // Policies matched by NPMS only match by subscriber ID or by ssid. Thus subtype
+ // Policies matched by NPMS only match by subscriber ID or by network ID. Thus subtype
// in the object created here is never used and its value doesn't matter, so use
// NETWORK_TYPE_UNKNOWN.
final NetworkIdentity ident = NetworkIdentity.buildNetworkIdentity(mContext, snapshot,
@@ -2435,7 +2435,6 @@
}
final NetworkTemplate.Builder builder =
new NetworkTemplate.Builder(templateType)
- .setWifiNetworkKey(networkId)
.setMeteredness(templateMeteredness);
if (subscriberIdMatchRule
== NetworkStatsUtils.SUBSCRIBER_ID_MATCH_RULE_EXACT) {
@@ -2443,6 +2442,9 @@
ids.add(subscriberId);
builder.setSubscriberIds(ids);
}
+ if (networkId != null) {
+ builder.setWifiNetworkKeys(Set.of(networkId));
+ }
final NetworkTemplate template = builder.build();
if (NetworkPolicy.isTemplatePersistable(template)) {
mNetworkPolicy.put(template, new NetworkPolicy(template, cycleRule,
@@ -2593,35 +2595,39 @@
* into {@link WifiConfiguration}.
*/
private void upgradeWifiMeteredOverride() {
- final ArrayMap<String, Boolean> wifiNetworkIds = new ArrayMap<>();
+ final ArrayMap<String, Boolean> wifiNetworkKeys = new ArrayMap<>();
synchronized (mNetworkPoliciesSecondLock) {
for (int i = 0; i < mNetworkPolicy.size();) {
final NetworkPolicy policy = mNetworkPolicy.valueAt(i);
if (policy.template.getMatchRule() == NetworkTemplate.MATCH_WIFI
&& !policy.inferred) {
mNetworkPolicy.removeAt(i);
- wifiNetworkIds.put(policy.template.getNetworkId(), policy.metered);
+ final Set<String> keys = policy.template.getWifiNetworkKeys();
+ wifiNetworkKeys.put(keys.isEmpty() ? null : keys.iterator().next(),
+ policy.metered);
} else {
i++;
}
}
}
- if (wifiNetworkIds.isEmpty()) {
+ if (wifiNetworkKeys.isEmpty()) {
return;
}
final WifiManager wm = mContext.getSystemService(WifiManager.class);
final List<WifiConfiguration> configs = wm.getConfiguredNetworks();
for (int i = 0; i < configs.size(); ++i) {
final WifiConfiguration config = configs.get(i);
- final String networkId = resolveNetworkId(config);
- final Boolean metered = wifiNetworkIds.get(networkId);
- if (metered != null) {
- Slog.d(TAG, "Found network " + networkId + "; upgrading metered hint");
- config.meteredOverride = metered
- ? WifiConfiguration.METERED_OVERRIDE_METERED
- : WifiConfiguration.METERED_OVERRIDE_NOT_METERED;
- wm.updateNetwork(config);
+ for (String key : config.getAllPersistableNetworkKeys()) {
+ final Boolean metered = wifiNetworkKeys.get(key);
+ if (metered != null) {
+ Slog.d(TAG, "Found network " + key + "; upgrading metered hint");
+ config.meteredOverride = metered
+ ? WifiConfiguration.METERED_OVERRIDE_METERED
+ : WifiConfiguration.METERED_OVERRIDE_NOT_METERED;
+ wm.updateNetwork(config);
+ break;
+ }
}
}
@@ -2663,9 +2669,9 @@
? NetworkStatsUtils.SUBSCRIBER_ID_MATCH_RULE_ALL
: NetworkStatsUtils.SUBSCRIBER_ID_MATCH_RULE_EXACT;
writeIntAttribute(out, ATTR_SUBSCRIBER_ID_MATCH_RULE, subscriberIdMatchRule);
- final String networkId = template.getNetworkId();
- if (networkId != null) {
- out.attribute(null, ATTR_NETWORK_ID, networkId);
+ if (!template.getWifiNetworkKeys().isEmpty()) {
+ out.attribute(null, ATTR_NETWORK_ID,
+ template.getWifiNetworkKeys().iterator().next());
}
writeIntAttribute(out, ATTR_TEMPLATE_METERED,
template.getMeteredness());
diff --git a/services/core/java/com/android/server/pm/ComputerEngine.java b/services/core/java/com/android/server/pm/ComputerEngine.java
index 2128bea..c2e4145 100644
--- a/services/core/java/com/android/server/pm/ComputerEngine.java
+++ b/services/core/java/com/android/server/pm/ComputerEngine.java
@@ -320,10 +320,7 @@
private final WatchedArrayMap<String, AndroidPackage> mPackages;
private final WatchedArrayMap<ComponentName, ParsedInstrumentation>
mInstrumentation;
- private final WatchedArrayMap<String, WatchedLongSparseArray<SharedLibraryInfo>>
- mStaticLibsByDeclaringPackage;
- private final WatchedArrayMap<String, WatchedLongSparseArray<SharedLibraryInfo>>
- mSharedLibraries;
+ private final SharedLibrariesRead mSharedLibraries;
private final ComponentName mLocalResolveComponentName;
private final ActivityInfo mResolveActivity;
private final WatchedSparseBooleanArray mWebInstantAppsDisabled;
@@ -375,8 +372,7 @@
mSettings = new Settings(args.settings);
mIsolatedOwners = args.isolatedOwners;
mPackages = args.packages;
- mSharedLibraries = args.sharedLibs;
- mStaticLibsByDeclaringPackage = args.staticLibs;
+ mSharedLibraries = args.sharedLibraries;
mInstrumentation = args.instrumentation;
mWebInstantAppsDisabled = args.webInstantAppsDisabled;
mLocalResolveComponentName = args.resolveComponentName;
@@ -2006,8 +2002,7 @@
@Nullable
public final SharedLibraryInfo getSharedLibraryInfo(String name, long version) {
- return SharedLibraryHelper.getSharedLibraryInfo(
- name, version, mSharedLibraries, null);
+ return mSharedLibraries.getSharedLibraryInfo(name, version);
}
/**
@@ -2058,7 +2053,7 @@
// Is this a static library?
WatchedLongSparseArray<SharedLibraryInfo> versionedLib =
- mStaticLibsByDeclaringPackage.get(packageName);
+ mSharedLibraries.getStaticLibraryInfos(packageName);
if (versionedLib == null || versionedLib.size() <= 0) {
return packageName;
}
@@ -3056,54 +3051,8 @@
}
case DumpState.DUMP_LIBS:
- {
- boolean printedHeader = false;
- final int numSharedLibraries = mSharedLibraries.size();
- for (int index = 0; index < numSharedLibraries; index++) {
- final String libName = mSharedLibraries.keyAt(index);
- final WatchedLongSparseArray<SharedLibraryInfo> versionedLib =
- mSharedLibraries.get(libName);
- if (versionedLib == null) {
- continue;
- }
- final int versionCount = versionedLib.size();
- for (int i = 0; i < versionCount; i++) {
- SharedLibraryInfo libraryInfo = versionedLib.valueAt(i);
- if (!checkin) {
- if (!printedHeader) {
- if (dumpState.onTitlePrinted()) {
- pw.println();
- }
- pw.println("Libraries:");
- printedHeader = true;
- }
- pw.print(" ");
- } else {
- pw.print("lib,");
- }
- pw.print(libraryInfo.getName());
- if (libraryInfo.isStatic()) {
- pw.print(" version=" + libraryInfo.getLongVersion());
- }
- if (!checkin) {
- pw.print(" -> ");
- }
- if (libraryInfo.getPath() != null) {
- if (libraryInfo.isNative()) {
- pw.print(" (so) ");
- } else {
- pw.print(" (jar) ");
- }
- pw.print(libraryInfo.getPath());
- } else {
- pw.print(" (apk) ");
- pw.print(libraryInfo.getPackageName());
- }
- pw.println();
- }
- }
+ mSharedLibraries.dump(pw, dumpState);
break;
- }
case DumpState.DUMP_PREFERRED:
mSettings.dumpPreferred(pw, dumpState, packageName);
@@ -3544,7 +3493,7 @@
@NonNull
@Override
public WatchedArrayMap<String, WatchedLongSparseArray<SharedLibraryInfo>> getSharedLibraries() {
- return mSharedLibraries;
+ return mSharedLibraries.getAll();
}
@NonNull
diff --git a/services/core/java/com/android/server/pm/DeletePackageHelper.java b/services/core/java/com/android/server/pm/DeletePackageHelper.java
index e84d990..c26f3b3 100644
--- a/services/core/java/com/android/server/pm/DeletePackageHelper.java
+++ b/services/core/java/com/android/server/pm/DeletePackageHelper.java
@@ -547,7 +547,6 @@
true /*notLaunched*/,
false /*hidden*/,
0 /*distractionFlags*/,
- false /*suspended*/,
null /*suspendParams*/,
false /*instantApp*/,
false /*virtualPreload*/,
diff --git a/services/core/java/com/android/server/pm/DumpHelper.java b/services/core/java/com/android/server/pm/DumpHelper.java
index 47e94f3..55d1293 100644
--- a/services/core/java/com/android/server/pm/DumpHelper.java
+++ b/services/core/java/com/android/server/pm/DumpHelper.java
@@ -25,7 +25,6 @@
import android.content.pm.FeatureInfo;
import android.content.pm.PackageManager;
import android.content.pm.PackageManagerInternal;
-import android.content.pm.SharedLibraryInfo;
import android.os.Binder;
import android.os.UserHandle;
import android.os.incremental.PerUidReadTimeouts;
@@ -37,7 +36,6 @@
import com.android.internal.util.ArrayUtils;
import com.android.internal.util.IndentingPrintWriter;
import com.android.server.pm.verify.domain.proxy.DomainVerificationProxy;
-import com.android.server.utils.WatchedLongSparseArray;
import java.io.FileDescriptor;
import java.io.PrintWriter;
@@ -710,7 +708,7 @@
proto.end(verifierPackageToken);
}
- dumpSharedLibrariesProto(proto);
+ mPm.mInjector.getSharedLibrariesImpl().dumpProto(proto);
dumpFeaturesProto(proto);
mPm.mSettings.dumpPackagesProto(proto);
mPm.mSettings.dumpSharedUsersProto(proto);
@@ -728,33 +726,4 @@
}
}
}
-
- private void dumpSharedLibrariesProto(ProtoOutputStream proto) {
- final int count = mPm.mSharedLibraries.size();
- for (int i = 0; i < count; i++) {
- final String libName = mPm.mSharedLibraries.keyAt(i);
- WatchedLongSparseArray<SharedLibraryInfo> versionedLib =
- mPm.mSharedLibraries.get(libName);
- if (versionedLib == null) {
- continue;
- }
- final int versionCount = versionedLib.size();
- for (int j = 0; j < versionCount; j++) {
- final SharedLibraryInfo libraryInfo = versionedLib.valueAt(j);
- final long sharedLibraryToken =
- proto.start(PackageServiceDumpProto.SHARED_LIBRARIES);
- proto.write(PackageServiceDumpProto.SharedLibraryProto.NAME, libraryInfo.getName());
- final boolean isJar = (libraryInfo.getPath() != null);
- proto.write(PackageServiceDumpProto.SharedLibraryProto.IS_JAR, isJar);
- if (isJar) {
- proto.write(PackageServiceDumpProto.SharedLibraryProto.PATH,
- libraryInfo.getPath());
- } else {
- proto.write(PackageServiceDumpProto.SharedLibraryProto.APK,
- libraryInfo.getPackageName());
- }
- proto.end(sharedLibraryToken);
- }
- }
- }
}
diff --git a/services/core/java/com/android/server/pm/InstallPackageHelper.java b/services/core/java/com/android/server/pm/InstallPackageHelper.java
index 8573585..c595b97 100644
--- a/services/core/java/com/android/server/pm/InstallPackageHelper.java
+++ b/services/core/java/com/android/server/pm/InstallPackageHelper.java
@@ -207,6 +207,7 @@
private final PackageAbiHelper mPackageAbiHelper;
private final ViewCompiler mViewCompiler;
private final IBackupManager mIBackupManager;
+ private final SharedLibrariesImpl mSharedLibraries;
// TODO(b/198166813): remove PMS dependency
InstallPackageHelper(PackageManagerService pm, AppDataHelper appDataHelper) {
@@ -226,6 +227,7 @@
mPackageAbiHelper = pm.mInjector.getAbiHelper();
mViewCompiler = pm.mInjector.getViewCompiler();
mIBackupManager = pm.mInjector.getIBackupManager();
+ mSharedLibraries = pm.mInjector.getSharedLibrariesImpl();
}
InstallPackageHelper(PackageManagerService pm) {
@@ -325,7 +327,7 @@
}
if (reconciledPkg.mCollectedSharedLibraryInfos != null) {
- mPm.executeSharedLibrariesUpdateLPr(pkg, pkgSetting, null, null,
+ mSharedLibraries.executeSharedLibrariesUpdateLPw(pkg, pkgSetting, null, null,
reconciledPkg.mCollectedSharedLibraryInfos, allUsers);
}
@@ -388,13 +390,13 @@
synchronized (mPm.mLock) {
if (!ArrayUtils.isEmpty(reconciledPkg.mAllowedSharedLibraryInfos)) {
for (SharedLibraryInfo info : reconciledPkg.mAllowedSharedLibraryInfos) {
- mPm.commitSharedLibraryInfoLocked(info);
+ mSharedLibraries.commitSharedLibraryInfoLPw(info);
}
final Map<String, AndroidPackage> combinedSigningDetails =
reconciledPkg.getCombinedAvailablePackages();
try {
// Shared libraries for the package need to be updated.
- mPm.updateSharedLibrariesLocked(pkg, pkgSetting, null, null,
+ mSharedLibraries.updateSharedLibrariesLPw(pkg, pkgSetting, null, null,
combinedSigningDetails);
} catch (PackageManagerException e) {
Slog.e(TAG, "updateSharedLibrariesLPr failed: ", e);
@@ -402,7 +404,7 @@
// Update all applications that use this library. Skip when booting
// since this will be done after all packages are scaned.
if ((scanFlags & SCAN_BOOTING) == 0) {
- clientLibPkgs = mPm.updateAllSharedLibrariesLocked(pkg, pkgSetting,
+ clientLibPkgs = mSharedLibraries.updateAllSharedLibrariesLPw(pkg, pkgSetting,
combinedSigningDetails);
}
}
@@ -855,7 +857,7 @@
mPm.notifyInstallObserver(request.mInstallResult, request.mArgs.mObserver);
}
- @GuardedBy("mInstallLock")
+ @GuardedBy("mPm.mInstallLock")
private void installPackagesTracedLI(List<InstallRequest> requests) {
try {
Trace.traceBegin(TRACE_TAG_PACKAGE_MANAGER, "installPackages");
@@ -884,7 +886,7 @@
*
* Failure at any phase will result in a full failure to install all packages.
*/
- @GuardedBy("mInstallLock")
+ @GuardedBy("mPm.mInstallLock")
private void installPackagesLI(List<InstallRequest> requests) {
final Map<String, ScanResult> preparedScans = new ArrayMap<>(requests.size());
final Map<String, InstallArgs> installArgs = new ArrayMap<>(requests.size());
@@ -945,7 +947,7 @@
if (result.mStaticSharedLibraryInfo != null
|| result.mSdkSharedLibraryInfo != null) {
final PackageSetting sharedLibLatestVersionSetting =
- mPm.getSharedLibLatestVersionSetting(result);
+ mSharedLibraries.getSharedLibLatestVersionSetting(result);
if (sharedLibLatestVersionSetting != null) {
lastStaticSharedLibSettings.put(
result.mPkgSetting.getPkg().getPackageName(),
@@ -961,7 +963,7 @@
reconcileRequest = new ReconcileRequest(preparedScans, installArgs,
installResults,
prepareResults,
- mPm.mSharedLibraries,
+ mSharedLibraries.getAll(),
Collections.unmodifiableMap(mPm.mPackages), versionInfos,
lastStaticSharedLibSettings);
CommitRequest commitRequest = null;
@@ -1036,7 +1038,7 @@
}
}
- @GuardedBy("mInstallLock")
+ @GuardedBy("mPm.mInstallLock")
private PrepareResult preparePackageLI(InstallArgs args, PackageInstalledInfo res)
throws PrepareFailure {
final int installFlags = args.mInstallFlags;
@@ -1207,7 +1209,7 @@
// the package setting for the latest library version.
PackageSetting signatureCheckPs = ps;
if (parsedPackage.isStaticSharedLibrary()) {
- SharedLibraryInfo libraryInfo = mPm.getLatestSharedLibraVersionLPr(
+ SharedLibraryInfo libraryInfo = mSharedLibraries.getLatestSharedLibraVersionLPr(
parsedPackage);
if (libraryInfo != null) {
signatureCheckPs = mPm.mSettings.getPackageLPr(
@@ -1880,7 +1882,7 @@
}
}
- @GuardedBy("mLock")
+ @GuardedBy("mPm.mLock")
private void commitPackagesLocked(final CommitRequest request) {
// TODO: remove any expected failures from this method; this should only be able to fail due
// to unavoidable errors (I/O, etc.)
@@ -1997,7 +1999,7 @@
ApplicationPackageManager.invalidateGetPackagesForUidCache();
}
- @GuardedBy("mLock")
+ @GuardedBy("mPm.mLock")
private boolean disableSystemPackageLPw(AndroidPackage oldPkg) {
return mPm.mSettings.disableSystemPackageLPw(oldPkg.getPackageName(), true);
}
@@ -2969,7 +2971,8 @@
synchronized (mPm.mLock) {
mAppDataHelper.prepareAppDataAfterInstallLIF(pkg);
try {
- mPm.updateSharedLibrariesLocked(pkg, stubPkgSetting, null, null,
+ mSharedLibraries.updateSharedLibrariesLPw(
+ pkg, stubPkgSetting, null, null,
Collections.unmodifiableMap(mPm.mPackages));
} catch (PackageManagerException e) {
Slog.w(TAG, "updateAllSharedLibrariesLPw failed: ", e);
@@ -3180,7 +3183,7 @@
try {
// update shared libraries for the newly re-installed system package
- mPm.updateSharedLibrariesLocked(pkg, pkgSetting, null, null,
+ mSharedLibraries.updateSharedLibrariesLPw(pkg, pkgSetting, null, null,
Collections.unmodifiableMap(mPm.mPackages));
} catch (PackageManagerException e) {
Slog.e(TAG, "updateAllSharedLibrariesLPw failed: " + e.getMessage());
@@ -3559,15 +3562,14 @@
ReconcilePackageUtils.reconcilePackages(
new ReconcileRequest(
Collections.singletonMap(pkgName, scanResult),
- mPm.mSharedLibraries,
+ mSharedLibraries.getAll(),
mPm.mPackages,
Collections.singletonMap(
pkgName,
mPm.getSettingsVersionForPackage(
parsedPackage)),
- Collections.singletonMap(pkgName,
- mPm.getSharedLibLatestVersionSetting(
- scanResult))),
+ Collections.singletonMap(pkgName, mSharedLibraries
+ .getSharedLibLatestVersionSetting(scanResult))),
mPm.mSettings.getKeySetManagerService(), mPm.mInjector);
appIdCreated = optimisticallyRegisterAppId(scanResult);
commitReconciledScanResultLocked(
@@ -4192,8 +4194,8 @@
long minVersionCode = Long.MIN_VALUE;
long maxVersionCode = Long.MAX_VALUE;
- WatchedLongSparseArray<SharedLibraryInfo> versionedLib = mPm.mSharedLibraries.get(
- pkg.getStaticSharedLibName());
+ WatchedLongSparseArray<SharedLibraryInfo> versionedLib =
+ mSharedLibraries.getSharedLibraryInfos(pkg.getStaticSharedLibName());
if (versionedLib != null) {
final int versionCount = versionedLib.size();
for (int i = 0; i < versionCount; i++) {
diff --git a/services/core/java/com/android/server/pm/PackageHandler.java b/services/core/java/com/android/server/pm/PackageHandler.java
index 217bc23..d1ea41a 100644
--- a/services/core/java/com/android/server/pm/PackageHandler.java
+++ b/services/core/java/com/android/server/pm/PackageHandler.java
@@ -388,7 +388,8 @@
}
case PRUNE_UNUSED_STATIC_SHARED_LIBRARIES: {
try {
- mPm.pruneUnusedStaticSharedLibraries(Long.MAX_VALUE,
+ mPm.mInjector.getSharedLibrariesImpl().pruneUnusedStaticSharedLibraries(
+ Long.MAX_VALUE,
Settings.Global.getLong(mPm.mContext.getContentResolver(),
Settings.Global.UNUSED_STATIC_SHARED_LIB_MIN_CACHE_PERIOD,
DEFAULT_UNUSED_STATIC_SHARED_LIB_MIN_CACHE_PERIOD));
diff --git a/services/core/java/com/android/server/pm/PackageInstallerService.java b/services/core/java/com/android/server/pm/PackageInstallerService.java
index f474044..cfcf199 100644
--- a/services/core/java/com/android/server/pm/PackageInstallerService.java
+++ b/services/core/java/com/android/server/pm/PackageInstallerService.java
@@ -404,7 +404,11 @@
if (age >= MAX_SESSION_AGE_ON_LOW_STORAGE_MILLIS) {
// Aggressively close old sessions because we are running low on storage
// Their staging dirs will be removed too
- session.abandon();
+ PackageInstallerSession root = !session.hasParentSessionId()
+ ? session : mSessions.get(session.getParentSessionId());
+ if (!root.isDestroyed()) {
+ root.abandon();
+ }
} else {
// Session is new enough, so it deserves to be kept even on low storage
unclaimedStagingDirsOnVolume.remove(session.stageDir);
@@ -1623,7 +1627,7 @@
progress);
}
- public void onStagedSessionChanged(PackageInstallerSession session) {
+ public void onSessionChanged(PackageInstallerSession session) {
session.markUpdated();
mSettingsWriteRequest.schedule();
if (mOkToSendBroadcasts && !session.isDestroyed()) {
diff --git a/services/core/java/com/android/server/pm/PackageInstallerSession.java b/services/core/java/com/android/server/pm/PackageInstallerSession.java
index a94985c..f45e54b 100644
--- a/services/core/java/com/android/server/pm/PackageInstallerSession.java
+++ b/services/core/java/com/android/server/pm/PackageInstallerSession.java
@@ -81,7 +81,7 @@
import android.content.pm.PackageInfo;
import android.content.pm.PackageInstaller;
import android.content.pm.PackageInstaller.SessionInfo;
-import android.content.pm.PackageInstaller.SessionInfo.StagedSessionErrorCode;
+import android.content.pm.PackageInstaller.SessionInfo.SessionErrorCode;
import android.content.pm.PackageInstaller.SessionParams;
import android.content.pm.PackageManager;
import android.content.pm.PackageManagerInternal;
@@ -151,7 +151,6 @@
import com.android.internal.util.IndentingPrintWriter;
import com.android.internal.util.Preconditions;
import com.android.server.LocalServices;
-import com.android.server.SystemConfig;
import com.android.server.pm.Installer.InstallerException;
import com.android.server.pm.dex.DexManager;
import com.android.server.pm.parsing.pkg.AndroidPackage;
@@ -229,8 +228,8 @@
private static final String ATTR_IS_READY = "isReady";
private static final String ATTR_IS_FAILED = "isFailed";
private static final String ATTR_IS_APPLIED = "isApplied";
- private static final String ATTR_STAGED_SESSION_ERROR_CODE = "errorCode";
- private static final String ATTR_STAGED_SESSION_ERROR_MESSAGE = "errorMessage";
+ private static final String ATTR_SESSION_ERROR_CODE = "errorCode";
+ private static final String ATTR_SESSION_ERROR_MESSAGE = "errorMessage";
private static final String ATTR_MODE = "mode";
private static final String ATTR_INSTALL_FLAGS = "installFlags";
private static final String ATTR_INSTALL_LOCATION = "installLocation";
@@ -454,22 +453,22 @@
@GuardedBy("mLock")
private ArrayMap<String, PerFileChecksum> mChecksums = new ArrayMap<>();
+ @GuardedBy("mLock")
+ private boolean mSessionApplied;
+ @GuardedBy("mLock")
+ private boolean mSessionReady;
+ @GuardedBy("mLock")
+ private boolean mSessionFailed;
+ @GuardedBy("mLock")
+ private int mSessionErrorCode = SessionInfo.STAGED_SESSION_NO_ERROR;
+ @GuardedBy("mLock")
+ private String mSessionErrorMessage;
+
@Nullable
final StagedSession mStagedSession;
@VisibleForTesting
public class StagedSession implements StagingManager.StagedSession {
- @GuardedBy("mLock")
- private boolean mSessionApplied;
- @GuardedBy("mLock")
- private boolean mSessionReady;
- @GuardedBy("mLock")
- private boolean mSessionFailed;
- @GuardedBy("mLock")
- private int mSessionErrorCode = SessionInfo.STAGED_SESSION_NO_ERROR;
- @GuardedBy("mLock")
- private String mSessionErrorMessage;
-
/**
* The callback to run when pre-reboot verification has ended. Used by {@link #abandon()}
* to delay session clean-up until it is safe to do so.
@@ -478,15 +477,6 @@
@Nullable
private Runnable mPendingAbandonCallback;
- StagedSession(boolean isReady, boolean isApplied, boolean isFailed, int errorCode,
- String errorMessage) {
- mSessionReady = isReady;
- mSessionApplied = isApplied;
- mSessionFailed = isFailed;
- mSessionErrorCode = errorCode;
- mSessionErrorMessage = errorMessage != null ? errorMessage : "";
- }
-
@Override
public List<StagingManager.StagedSession> getChildSessions() {
if (!params.isMultiPackage) {
@@ -534,52 +524,17 @@
@Override
public void setSessionReady() {
- synchronized (mLock) {
- // Do not allow destroyed/failed staged session to change state
- if (mDestroyed || mSessionFailed) return;
- mSessionReady = true;
- mSessionApplied = false;
- mSessionFailed = false;
- mSessionErrorCode = SessionInfo.STAGED_SESSION_NO_ERROR;
- mSessionErrorMessage = "";
- }
- mCallback.onStagedSessionChanged(PackageInstallerSession.this);
+ PackageInstallerSession.this.setSessionReady();
}
@Override
public void setSessionFailed(int errorCode, String errorMessage) {
- List<PackageInstallerSession> childSessions;
- synchronized (mLock) {
- // Do not allow destroyed/failed staged session to change state
- if (mDestroyed || mSessionFailed) return;
- mSessionReady = false;
- mSessionApplied = false;
- mSessionFailed = true;
- mSessionErrorCode = errorCode;
- mSessionErrorMessage = errorMessage;
- Slog.d(TAG, "Marking session " + sessionId + " as failed: " + errorMessage);
- childSessions = getChildSessionsLocked();
- }
- destroy();
- mCallback.onStagedSessionChanged(PackageInstallerSession.this);
+ PackageInstallerSession.this.setSessionFailed(errorCode, errorMessage);
}
@Override
public void setSessionApplied() {
- List<PackageInstallerSession> childSessions;
- synchronized (mLock) {
- // Do not allow destroyed/failed staged session to change state
- if (mDestroyed || mSessionFailed) return;
- mSessionReady = false;
- mSessionApplied = true;
- mSessionFailed = false;
- mSessionErrorCode = SessionInfo.STAGED_SESSION_NO_ERROR;
- mSessionErrorMessage = "";
- Slog.d(TAG, "Marking session " + sessionId + " as applied");
- childSessions = getChildSessionsLocked();
- }
- destroy();
- mCallback.onStagedSessionChanged(PackageInstallerSession.this);
+ PackageInstallerSession.this.setSessionApplied();
}
@Override
@@ -656,35 +611,17 @@
@Override
public boolean isSessionReady() {
- synchronized (mLock) {
- return mSessionReady;
- }
+ return PackageInstallerSession.this.isSessionReady();
}
@Override
public boolean isSessionApplied() {
- synchronized (mLock) {
- return mSessionApplied;
- }
+ return PackageInstallerSession.this.isSessionApplied();
}
@Override
public boolean isSessionFailed() {
- synchronized (mLock) {
- return mSessionFailed;
- }
- }
-
- @StagedSessionErrorCode int getSessionErrorCode() {
- synchronized (mLock) {
- return mSessionErrorCode;
- }
- }
-
- String getSessionErrorMessage() {
- synchronized (mLock) {
- return mSessionErrorMessage;
- }
+ return PackageInstallerSession.this.isSessionFailed();
}
@Override
@@ -714,7 +651,7 @@
if (mStageDirInUse) {
// Pre-reboot verification is ongoing, not safe to clean up the session yet.
mPendingAbandonCallback = r;
- mCallback.onStagedSessionChanged(PackageInstallerSession.this);
+ mCallback.onSessionChanged(PackageInstallerSession.this);
return;
}
}
@@ -1015,8 +952,8 @@
ArrayMap<String, PerFileChecksum> checksums,
boolean prepared, boolean committed, boolean destroyed, boolean sealed,
@Nullable int[] childSessionIds, int parentSessionId, boolean isReady,
- boolean isFailed, boolean isApplied, int stagedSessionErrorCode,
- String stagedSessionErrorMessage) {
+ boolean isFailed, boolean isApplied, int sessionErrorCode,
+ String sessionErrorMessage) {
mCallback = callback;
mContext = context;
mPm = pm;
@@ -1071,8 +1008,13 @@
mPrepared = prepared;
mCommitted.set(committed);
mDestroyed = destroyed;
- mStagedSession = params.isStaged ? new StagedSession(isReady, isApplied, isFailed,
- stagedSessionErrorCode, stagedSessionErrorMessage) : null;
+ mSessionReady = isReady;
+ mSessionApplied = isApplied;
+ mSessionFailed = isFailed;
+ mSessionErrorCode = sessionErrorCode;
+ mSessionErrorMessage =
+ sessionErrorMessage != null ? sessionErrorMessage : "";
+ mStagedSession = params.isStaged ? new StagedSession() : null;
if (isDataLoaderInstallation()) {
if (isApexSession()) {
@@ -1173,11 +1115,10 @@
info.rollbackDataPolicy = params.rollbackDataPolicy;
info.parentSessionId = mParentSessionId;
info.childSessionIds = getChildSessionIdsLocked();
- info.isStagedSessionApplied = isStagedSessionApplied();
- info.isStagedSessionReady = isStagedSessionReady();
- info.isStagedSessionFailed = isStagedSessionFailed();
- info.setStagedSessionErrorCode(getStagedSessionErrorCode(),
- getStagedSessionErrorMessage());
+ info.isSessionApplied = mSessionApplied;
+ info.isSessionReady = mSessionReady;
+ info.isSessionFailed = mSessionFailed;
+ info.setSessionErrorCode(mSessionErrorCode, mSessionErrorMessage);
info.createdMillis = createdMillis;
info.updatedMillis = updatedMillis;
info.requireUserAction = params.requireUserAction;
@@ -2229,7 +2170,7 @@
final PackageInstallerSession root = hasParentSessionId()
? allSessions.get(getParentSessionId())
: this;
- if (root != null) {
+ if (root != null && !root.isStagedAndInTerminalState()) {
if (isApexSession()) {
validateApexInstallLocked();
} else {
@@ -2357,28 +2298,6 @@
return;
}
-
- // Check if APEX update is allowed. We do this check in handleInstall, since this is one of
- // the places that:
- // * Shared between staged and non-staged APEX update flows.
- // * Only is called after boot completes.
- // The later is important, since isApexUpdateAllowed check depends on the
- // ModuleInfoProvider, which is only populated after device has booted.
- if (isApexSession()) {
- boolean checkApexUpdateAllowed =
- (params.installFlags & PackageManager.INSTALL_DISABLE_ALLOWED_APEX_UPDATE_CHECK)
- == 0;
- synchronized (mLock) {
- if (checkApexUpdateAllowed && !isApexUpdateAllowed(mPackageName,
- mInstallSource.installerPackageName)) {
- onSessionValidationFailure(PackageManager.INSTALL_FAILED_VERIFICATION_FAILURE,
- "Update of APEX package " + mPackageName + " is not allowed for "
- + mInstallSource.installerPackageName);
- return;
- }
- }
- }
-
if (params.isStaged) {
// TODO(b/136257624): CTS test fails if we don't send session finished broadcast, even
// though ideally, we just need to send session committed broadcast.
@@ -2825,25 +2744,6 @@
return sessionContains((s) -> !s.isApexSession());
}
- private boolean isApexUpdateAllowed(String apexPackageName, String installerPackageName) {
- if (mPm.getModuleInfo(apexPackageName, 0) != null) {
- final String modulesInstaller =
- SystemConfig.getInstance().getModulesInstallerPackageName();
- if (modulesInstaller == null) {
- Slog.w(TAG, "No modules installer defined");
- return false;
- }
- return modulesInstaller.equals(installerPackageName);
- }
- final String vendorApexInstaller =
- SystemConfig.getInstance().getAllowedVendorApexes().get(apexPackageName);
- if (vendorApexInstaller == null) {
- Slog.w(TAG, apexPackageName + " is not allowed to be updated");
- return false;
- }
- return vendorApexInstaller.equals(installerPackageName);
- }
-
/**
* Validate apex install.
* <p>
@@ -4261,30 +4161,83 @@
}
}
- /** {@hide} */
- boolean isStagedSessionReady() {
- return params.isStaged && mStagedSession.isSessionReady();
+ private void setSessionReady() {
+ synchronized (mLock) {
+ // Do not allow destroyed/failed session to change state
+ if (mDestroyed || mSessionFailed) return;
+ mSessionReady = true;
+ mSessionApplied = false;
+ mSessionFailed = false;
+ mSessionErrorCode = SessionInfo.STAGED_SESSION_NO_ERROR;
+ mSessionErrorMessage = "";
+ }
+ mCallback.onSessionChanged(this);
+ }
+
+ private void setSessionFailed(int errorCode, String errorMessage) {
+ synchronized (mLock) {
+ // Do not allow destroyed/failed session to change state
+ if (mDestroyed || mSessionFailed) return;
+ mSessionReady = false;
+ mSessionApplied = false;
+ mSessionFailed = true;
+ mSessionErrorCode = errorCode;
+ mSessionErrorMessage = errorMessage;
+ Slog.d(TAG, "Marking session " + sessionId + " as failed: " + errorMessage);
+ }
+ destroy();
+ mCallback.onSessionChanged(this);
+ }
+
+ private void setSessionApplied() {
+ synchronized (mLock) {
+ // Do not allow destroyed/failed session to change state
+ if (mDestroyed || mSessionFailed) return;
+ mSessionReady = false;
+ mSessionApplied = true;
+ mSessionFailed = false;
+ mSessionErrorCode = SessionInfo.STAGED_SESSION_NO_ERROR;
+ mSessionErrorMessage = "";
+ Slog.d(TAG, "Marking session " + sessionId + " as applied");
+ }
+ destroy();
+ mCallback.onSessionChanged(this);
}
/** {@hide} */
- boolean isStagedSessionApplied() {
- return params.isStaged && mStagedSession.isSessionApplied();
+ boolean isSessionReady() {
+ synchronized (mLock) {
+ return mSessionReady;
+ }
}
/** {@hide} */
- boolean isStagedSessionFailed() {
- return params.isStaged && mStagedSession.isSessionFailed();
+ boolean isSessionApplied() {
+ synchronized (mLock) {
+ return mSessionApplied;
+ }
}
/** {@hide} */
- @StagedSessionErrorCode int getStagedSessionErrorCode() {
- return params.isStaged ? mStagedSession.getSessionErrorCode()
- : SessionInfo.STAGED_SESSION_NO_ERROR;
+ boolean isSessionFailed() {
+ synchronized (mLock) {
+ return mSessionFailed;
+ }
}
/** {@hide} */
- String getStagedSessionErrorMessage() {
- return params.isStaged ? mStagedSession.getSessionErrorMessage() : "";
+ @SessionErrorCode
+ int getSessionErrorCode() {
+ synchronized (mLock) {
+ return mSessionErrorCode;
+ }
+ }
+
+ /** {@hide} */
+ String getSessionErrorMessage() {
+ synchronized (mLock) {
+ return mSessionErrorMessage;
+ }
}
/**
@@ -4386,11 +4339,11 @@
pw.printPair("params.isStaged", params.isStaged);
pw.printPair("mParentSessionId", mParentSessionId);
pw.printPair("mChildSessionIds", getChildSessionIdsLocked());
- pw.printPair("mStagedSessionApplied", isStagedSessionApplied());
- pw.printPair("mStagedSessionFailed", isStagedSessionFailed());
- pw.printPair("mStagedSessionReady", isStagedSessionReady());
- pw.printPair("mStagedSessionErrorCode", getStagedSessionErrorCode());
- pw.printPair("mStagedSessionErrorMessage", getStagedSessionErrorMessage());
+ pw.printPair("mSessionApplied", mSessionApplied);
+ pw.printPair("mSessionFailed", mSessionFailed);
+ pw.printPair("mSessionReady", mSessionReady);
+ pw.printPair("mSessionErrorCode", mSessionErrorCode);
+ pw.printPair("mSessionErrorMessage", mSessionErrorMessage);
pw.println();
pw.decreaseIndent();
@@ -4556,12 +4509,11 @@
writeBooleanAttribute(out, ATTR_MULTI_PACKAGE, params.isMultiPackage);
writeBooleanAttribute(out, ATTR_STAGED_SESSION, params.isStaged);
- writeBooleanAttribute(out, ATTR_IS_READY, isStagedSessionReady());
- writeBooleanAttribute(out, ATTR_IS_FAILED, isStagedSessionFailed());
- writeBooleanAttribute(out, ATTR_IS_APPLIED, isStagedSessionApplied());
- out.attributeInt(null, ATTR_STAGED_SESSION_ERROR_CODE, getStagedSessionErrorCode());
- writeStringAttribute(out, ATTR_STAGED_SESSION_ERROR_MESSAGE,
- getStagedSessionErrorMessage());
+ writeBooleanAttribute(out, ATTR_IS_READY, mSessionReady);
+ writeBooleanAttribute(out, ATTR_IS_FAILED, mSessionFailed);
+ writeBooleanAttribute(out, ATTR_IS_APPLIED, mSessionApplied);
+ out.attributeInt(null, ATTR_SESSION_ERROR_CODE, mSessionErrorCode);
+ writeStringAttribute(out, ATTR_SESSION_ERROR_MESSAGE, mSessionErrorMessage);
// TODO(patb,109941548): avoid writing to xml and instead infer / validate this after
// we've read all sessions.
out.attributeInt(null, ATTR_PARENT_SESSION_ID, mParentSessionId);
@@ -4752,10 +4704,9 @@
final boolean isReady = in.getAttributeBoolean(null, ATTR_IS_READY, false);
final boolean isFailed = in.getAttributeBoolean(null, ATTR_IS_FAILED, false);
final boolean isApplied = in.getAttributeBoolean(null, ATTR_IS_APPLIED, false);
- final int stagedSessionErrorCode = in.getAttributeInt(null, ATTR_STAGED_SESSION_ERROR_CODE,
+ final int sessionErrorCode = in.getAttributeInt(null, ATTR_SESSION_ERROR_CODE,
SessionInfo.STAGED_SESSION_NO_ERROR);
- final String stagedSessionErrorMessage = readStringAttribute(in,
- ATTR_STAGED_SESSION_ERROR_MESSAGE);
+ final String sessionErrorMessage = readStringAttribute(in, ATTR_SESSION_ERROR_MESSAGE);
if (!isStagedSessionStateValid(isReady, isApplied, isFailed)) {
throw new IllegalArgumentException("Can't restore staged session with invalid state.");
@@ -4869,6 +4820,6 @@
installerUid, installSource, params, createdMillis, committedMillis, stageDir,
stageCid, fileArray, checksumsMap, prepared, committed, destroyed, sealed,
childSessionIdsArray, parentSessionId, isReady, isFailed, isApplied,
- stagedSessionErrorCode, stagedSessionErrorMessage);
+ sessionErrorCode, sessionErrorMessage);
}
}
diff --git a/services/core/java/com/android/server/pm/PackageManagerService.java b/services/core/java/com/android/server/pm/PackageManagerService.java
index f3fe2c0..248944e 100644
--- a/services/core/java/com/android/server/pm/PackageManagerService.java
+++ b/services/core/java/com/android/server/pm/PackageManagerService.java
@@ -239,6 +239,9 @@
import com.android.server.pm.pkg.PackageUserState;
import com.android.server.pm.pkg.PackageUserStateInternal;
import com.android.server.pm.pkg.SuspendParams;
+import com.android.server.pm.pkg.mutate.PackageStateMutator;
+import com.android.server.pm.pkg.mutate.PackageStateWrite;
+import com.android.server.pm.pkg.mutate.PackageUserStateWrite;
import com.android.server.pm.verify.domain.DomainVerificationManagerInternal;
import com.android.server.pm.verify.domain.DomainVerificationService;
import com.android.server.pm.verify.domain.proxy.DomainVerificationProxy;
@@ -249,7 +252,6 @@
import com.android.server.utils.Watchable;
import com.android.server.utils.Watched;
import com.android.server.utils.WatchedArrayMap;
-import com.android.server.utils.WatchedLongSparseArray;
import com.android.server.utils.WatchedSparseBooleanArray;
import com.android.server.utils.WatchedSparseIntArray;
import com.android.server.utils.Watcher;
@@ -279,7 +281,6 @@
import java.util.Collections;
import java.util.HashMap;
import java.util.Iterator;
-import java.util.LinkedHashSet;
import java.util.List;
import java.util.Map;
import java.util.Objects;
@@ -289,7 +290,6 @@
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicBoolean;
import java.util.concurrent.atomic.AtomicInteger;
-import java.util.function.BiConsumer;
import java.util.function.Consumer;
import java.util.function.Function;
import java.util.function.Predicate;
@@ -597,6 +597,16 @@
// the suffix "Locked". Some methods may use the legacy suffix "LP"
final PackageManagerTracedLock mLock;
+ // Lock alias for doing package state mutation
+ private final PackageManagerTracedLock mPackageStateWriteLock;
+
+ // Lock alias to track syncing a consistent Computer
+ private final PackageManagerTracedLock mLiveComputerSyncLock;
+
+ private final PackageStateMutator mPackageStateMutator = new PackageStateMutator(
+ this::getPackageSettingForMutation,
+ this::getDisabledPackageSettingForMutation);
+
// Keys are String (package name), values are Package.
@Watched
@GuardedBy("mLock")
@@ -757,19 +767,7 @@
// Currently known shared libraries.
@Watched
- final WatchedArrayMap<String, WatchedLongSparseArray<SharedLibraryInfo>>
- mSharedLibraries = new WatchedArrayMap<>();
- private final SnapshotCache<WatchedArrayMap<String, WatchedLongSparseArray<SharedLibraryInfo>>>
- mSharedLibrariesSnapshot =
- new SnapshotCache.Auto<>(mSharedLibraries, mSharedLibraries,
- "PackageManagerService.mSharedLibraries");
- @Watched
- final WatchedArrayMap<String, WatchedLongSparseArray<SharedLibraryInfo>>
- mStaticLibsByDeclaringPackage = new WatchedArrayMap<>();
- private final SnapshotCache<WatchedArrayMap<String, WatchedLongSparseArray<SharedLibraryInfo>>>
- mStaticLibsByDeclaringPackageSnapshot =
- new SnapshotCache.Auto<>(mStaticLibsByDeclaringPackage, mStaticLibsByDeclaringPackage,
- "PackageManagerService.mStaticLibsByDeclaringPackage");
+ private final SharedLibrariesImpl mSharedLibraries;
// Mapping from instrumentation class names to info about them.
@Watched
@@ -1002,8 +1000,6 @@
public final Settings settings;
public final WatchedSparseIntArray isolatedOwners;
public final WatchedArrayMap<String, AndroidPackage> packages;
- public final WatchedArrayMap<String, WatchedLongSparseArray<SharedLibraryInfo>> sharedLibs;
- public final WatchedArrayMap<String, WatchedLongSparseArray<SharedLibraryInfo>> staticLibs;
public final WatchedArrayMap<ComponentName, ParsedInstrumentation> instrumentation;
public final WatchedSparseBooleanArray webInstantAppsDisabled;
public final ComponentName resolveComponentName;
@@ -1017,14 +1013,13 @@
public final ComponentResolver componentResolver;
public final PackageManagerService service;
public final WatchedArrayMap<String, Integer> frozenPackages;
+ public final SharedLibrariesRead sharedLibraries;
Snapshot(int type) {
if (type == Snapshot.SNAPPED) {
settings = mSettings.snapshot();
isolatedOwners = mIsolatedOwnersSnapshot.snapshot();
packages = mPackagesSnapshot.snapshot();
- sharedLibs = mSharedLibrariesSnapshot.snapshot();
- staticLibs = mStaticLibsByDeclaringPackageSnapshot.snapshot();
instrumentation = mInstrumentationSnapshot.snapshot();
resolveComponentName = mResolveComponentName.clone();
resolveActivity = new ActivityInfo(mResolveActivity);
@@ -1043,12 +1038,11 @@
appsFilter = mAppsFilter.snapshot();
componentResolver = mComponentResolver.snapshot();
frozenPackages = mFrozenPackagesSnapshot.snapshot();
+ sharedLibraries = mSharedLibraries.snapshot();
} else if (type == Snapshot.LIVE) {
settings = mSettings;
isolatedOwners = mIsolatedOwners;
packages = mPackages;
- sharedLibs = mSharedLibraries;
- staticLibs = mStaticLibsByDeclaringPackage;
instrumentation = mInstrumentation;
resolveComponentName = mResolveComponentName;
resolveActivity = mResolveActivity;
@@ -1061,6 +1055,7 @@
appsFilter = mAppsFilter;
componentResolver = mComponentResolver;
frozenPackages = mFrozenPackages;
+ sharedLibraries = mSharedLibraries;
} else {
throw new IllegalArgumentException();
}
@@ -1522,7 +1517,8 @@
context::getSystemService,
(i, pm) -> new BackgroundDexOptService(i.getContext(), i.getDexManager(), pm),
(i, pm) -> IBackupManager.Stub.asInterface(ServiceManager.getService(
- Context.BACKUP_SERVICE)));
+ Context.BACKUP_SERVICE)),
+ (i, pm) -> new SharedLibrariesImpl(pm, i));
if (Build.VERSION.SDK_INT <= 0) {
Slog.w(TAG, "**** ro.build.version.sdk not set!");
@@ -1594,7 +1590,6 @@
private void registerObserver() {
mPackages.registerObserver(mWatcher);
mSharedLibraries.registerObserver(mWatcher);
- mStaticLibsByDeclaringPackage.registerObserver(mWatcher);
mInstrumentation.registerObserver(mWatcher);
mWebInstantAppsDisabled.registerObserver(mWatcher);
mAppsFilter.registerObserver(mWatcher);
@@ -1626,11 +1621,14 @@
mInstaller = injector.getInstaller();
mInstallLock = injector.getInstallLock();
mLock = injector.getLock();
+ mPackageStateWriteLock = mLock;
+ mLiveComputerSyncLock = mLock;
mPermissionManager = injector.getPermissionManagerServiceInternal();
mSettings = injector.getSettings();
mUserManager = injector.getUserManagerService();
mDomainVerificationManager = injector.getDomainVerificationManagerInternal();
mHandler = injector.getHandler();
+ mSharedLibraries = injector.getSharedLibrariesImpl();
mApexManager = testParams.apexManager;
mArtManagerService = testParams.artManagerService;
@@ -1708,6 +1706,7 @@
mPreferredActivityHelper = testParams.preferredActivityHelper;
mResolveIntentHelper = testParams.resolveIntentHelper;
mDexOptHelper = testParams.dexOptHelper;
+ mSharedLibraries.setDeletePackageHelper(mDeletePackageHelper);
invalidatePackageInfoCache();
}
@@ -1729,6 +1728,8 @@
mInjector.bootstrap(this);
mLock = injector.getLock();
+ mPackageStateWriteLock = mLock;
+ mLiveComputerSyncLock = mLock;
mInstallLock = injector.getInstallLock();
LockGuard.installLock(mLock, LockGuard.INDEX_PACKAGES);
EventLog.writeEvent(EventLogTags.BOOT_PROGRESS_PMS_START,
@@ -1817,6 +1818,7 @@
mArtManagerService = injector.getArtManagerService();
mMoveCallbacks = new MovePackageHelper.MoveCallbacks(FgThread.get().getLooper());
mViewCompiler = injector.getViewCompiler();
+ mSharedLibraries = mInjector.getSharedLibrariesImpl();
mContext.getSystemService(DisplayManager.class)
.getDisplay(Display.DEFAULT_DISPLAY).getMetrics(mMetrics);
@@ -1846,6 +1848,7 @@
mInitAndSystemPackageHelper = new InitAndSystemPackageHelper(this);
mDeletePackageHelper = new DeletePackageHelper(this, mRemovePackageHelper,
mAppDataHelper);
+ mSharedLibraries.setDeletePackageHelper(mDeletePackageHelper);
mPreferredActivityHelper = new PreferredActivityHelper(this);
mResolveIntentHelper = new ResolveIntentHelper(this, mPreferredActivityHelper);
mDexOptHelper = new DexOptHelper(this);
@@ -1877,7 +1880,7 @@
= systemConfig.getSharedLibraries();
final int builtInLibCount = libConfig.size();
for (int i = 0; i < builtInLibCount; i++) {
- addBuiltInSharedLibraryLocked(libConfig.valueAt(i));
+ mSharedLibraries.addBuiltInSharedLibraryLPw(libConfig.valueAt(i));
}
// Now that we have added all the libraries, iterate again to add dependency
@@ -1996,7 +1999,8 @@
// Now that we know all of the shared libraries, update all clients to have
// the correct library paths.
- updateAllSharedLibrariesLocked(null, null, Collections.unmodifiableMap(mPackages));
+ mSharedLibraries.updateAllSharedLibrariesLPw(
+ null, null, Collections.unmodifiableMap(mPackages));
for (SharedUserSetting setting : mSettings.getAllSharedUsersLPw()) {
// NOTE: We ignore potential failures here during a system scan (like
@@ -2835,7 +2839,7 @@
if (file.getUsableSpace() >= bytes) return;
// 5. Consider shared libraries with refcount=0 and age>min cache period
- if (internalVolume && pruneUnusedStaticSharedLibraries(bytes,
+ if (internalVolume && mSharedLibraries.pruneUnusedStaticSharedLibraries(bytes,
android.provider.Settings.Global.getLong(mContext.getContentResolver(),
Global.UNUSED_STATIC_SHARED_LIB_MIN_CACHE_PERIOD,
FREE_STORAGE_UNUSED_STATIC_SHARED_LIB_MIN_CACHE_PERIOD))) {
@@ -2897,85 +2901,6 @@
throw new IOException("Failed to free " + bytes + " on storage device at " + file);
}
- private PackageSetting getLibraryPackage(SharedLibraryInfo libInfo) {
- final VersionedPackage declaringPackage = libInfo.getDeclaringPackage();
- if (libInfo.isStatic()) {
- // Resolve the package name - we use synthetic package names internally
- final String internalPackageName = resolveInternalPackageNameLPr(
- declaringPackage.getPackageName(),
- declaringPackage.getLongVersionCode());
- return mSettings.getPackageLPr(internalPackageName);
- }
- if (libInfo.isSdk()) {
- return mSettings.getPackageLPr(declaringPackage.getPackageName());
- }
- return null;
- }
-
- boolean pruneUnusedStaticSharedLibraries(long neededSpace, long maxCachePeriod)
- throws IOException {
- final StorageManager storage = mInjector.getSystemService(StorageManager.class);
- final File volume = storage.findPathForUuid(StorageManager.UUID_PRIVATE_INTERNAL);
-
- List<VersionedPackage> packagesToDelete = null;
- final long now = System.currentTimeMillis();
-
- // Important: We skip shared libs used for some user since
- // in such a case we need to keep the APK on the device. The check for
- // a lib being used for any user is performed by the uninstall call.
- synchronized (mLock) {
- final int libCount = mSharedLibraries.size();
- for (int i = 0; i < libCount; i++) {
- final WatchedLongSparseArray<SharedLibraryInfo> versionedLib
- = mSharedLibraries.valueAt(i);
- if (versionedLib == null) {
- continue;
- }
- final int versionCount = versionedLib.size();
- for (int j = 0; j < versionCount; j++) {
- SharedLibraryInfo libInfo = versionedLib.valueAt(j);
- final PackageSetting ps = getLibraryPackage(libInfo);
- if (ps == null) {
- continue;
- }
- // Skip unused libs cached less than the min period to prevent pruning a lib
- // needed by a subsequently installed package.
- if (now - ps.getLastUpdateTime() < maxCachePeriod) {
- continue;
- }
-
- if (ps.getPkg().isSystem()) {
- continue;
- }
-
- if (packagesToDelete == null) {
- packagesToDelete = new ArrayList<>();
- }
- packagesToDelete.add(new VersionedPackage(ps.getPkg().getPackageName(),
- libInfo.getDeclaringPackage().getLongVersionCode()));
- }
- }
- }
-
- if (packagesToDelete != null) {
- final int packageCount = packagesToDelete.size();
- for (int i = 0; i < packageCount; i++) {
- final VersionedPackage pkgToDelete = packagesToDelete.get(i);
- // Delete the package synchronously (will fail of the lib used for any user).
- if (mDeletePackageHelper.deletePackageX(pkgToDelete.getPackageName(),
- pkgToDelete.getLongVersionCode(), UserHandle.USER_SYSTEM,
- PackageManager.DELETE_ALL_USERS,
- true /*removedBySystem*/) == PackageManager.DELETE_SUCCEEDED) {
- if (volume.getUsableSpace() >= neededSpace) {
- return true;
- }
- }
- }
- }
-
- return false;
- }
-
/**
* Update given flags when being used to request {@link PackageInfo}.
*/
@@ -3961,40 +3886,6 @@
return mComputer.getSharedLibraryInfo(name, version);
}
- SharedLibraryInfo getLatestSharedLibraVersionLPr(AndroidPackage pkg) {
- WatchedLongSparseArray<SharedLibraryInfo> versionedLib = mSharedLibraries.get(
- pkg.getStaticSharedLibName());
- if (versionedLib == null) {
- return null;
- }
- long previousLibVersion = -1;
- final int versionCount = versionedLib.size();
- for (int i = 0; i < versionCount; i++) {
- final long libVersion = versionedLib.keyAt(i);
- if (libVersion < pkg.getStaticSharedLibVersion()) {
- previousLibVersion = Math.max(previousLibVersion, libVersion);
- }
- }
- if (previousLibVersion >= 0) {
- return versionedLib.get(previousLibVersion);
- }
- return null;
- }
-
- @Nullable
- PackageSetting getSharedLibLatestVersionSetting(@NonNull ScanResult scanResult) {
- PackageSetting sharedLibPackage = null;
- synchronized (mLock) {
- final SharedLibraryInfo latestSharedLibraVersionLPr =
- getLatestSharedLibraVersionLPr(scanResult.mRequest.mParsedPackage);
- if (latestSharedLibraVersionLPr != null) {
- sharedLibPackage = mSettings.getPackageLPr(
- latestSharedLibraVersionLPr.getPackageName());
- }
- }
- return sharedLibPackage;
- }
-
public void shutdown() {
mCompilerStats.writeNow();
mDexManager.writePackageDexUsageNow();
@@ -4049,253 +3940,6 @@
return (userId == UserHandle.USER_ALL) ? mUserManager.getUserIds() : new int[] { userId };
}
- @GuardedBy("mLock")
- private void applyDefiningSharedLibraryUpdateLocked(
- AndroidPackage pkg, SharedLibraryInfo libInfo,
- BiConsumer<SharedLibraryInfo, SharedLibraryInfo> action) {
- // Note that libraries defined by this package may be null if:
- // - Package manager was unable to create the shared library. The package still
- // gets installed, but the shared library does not get created.
- // Or:
- // - Package manager is in a state where package isn't scanned yet. This will
- // get called again after scanning to fix the dependencies.
- if (AndroidPackageUtils.isLibrary(pkg)) {
- if (pkg.getSdkLibName() != null) {
- SharedLibraryInfo definedLibrary = getSharedLibraryInfo(
- pkg.getSdkLibName(), pkg.getSdkLibVersionMajor());
- if (definedLibrary != null) {
- action.accept(definedLibrary, libInfo);
- }
- } else if (pkg.getStaticSharedLibName() != null) {
- SharedLibraryInfo definedLibrary = getSharedLibraryInfo(
- pkg.getStaticSharedLibName(), pkg.getStaticSharedLibVersion());
- if (definedLibrary != null) {
- action.accept(definedLibrary, libInfo);
- }
- } else {
- for (String libraryName : pkg.getLibraryNames()) {
- SharedLibraryInfo definedLibrary = getSharedLibraryInfo(
- libraryName, SharedLibraryInfo.VERSION_UNDEFINED);
- if (definedLibrary != null) {
- action.accept(definedLibrary, libInfo);
- }
- }
- }
- }
- }
-
- @GuardedBy("mLock")
- private void addSharedLibraryLPr(AndroidPackage pkg, Set<String> usesLibraryFiles,
- SharedLibraryInfo libInfo, @Nullable AndroidPackage changingLib,
- @Nullable PackageSetting changingLibSetting) {
- if (libInfo.getPath() != null) {
- usesLibraryFiles.add(libInfo.getPath());
- return;
- }
- AndroidPackage pkgForCodePaths = mPackages.get(libInfo.getPackageName());
- PackageSetting pkgSetting = mSettings.getPackageLPr(libInfo.getPackageName());
- if (changingLib != null && changingLib.getPackageName().equals(libInfo.getPackageName())) {
- // If we are doing this while in the middle of updating a library apk,
- // then we need to make sure to use that new apk for determining the
- // dependencies here. (We haven't yet finished committing the new apk
- // to the package manager state.)
- if (pkgForCodePaths == null
- || pkgForCodePaths.getPackageName().equals(changingLib.getPackageName())) {
- pkgForCodePaths = changingLib;
- pkgSetting = changingLibSetting;
- }
- }
- if (pkgForCodePaths != null) {
- usesLibraryFiles.addAll(AndroidPackageUtils.getAllCodePaths(pkgForCodePaths));
- // If the package provides libraries, add the dependency to them.
- applyDefiningSharedLibraryUpdateLocked(pkg, libInfo, SharedLibraryInfo::addDependency);
- if (pkgSetting != null) {
- usesLibraryFiles.addAll(pkgSetting.getPkgState().getUsesLibraryFiles());
- }
- }
- }
-
- @GuardedBy("mLock")
- void updateSharedLibrariesLocked(AndroidPackage pkg, PackageSetting pkgSetting,
- @Nullable AndroidPackage changingLib, @Nullable PackageSetting changingLibSetting,
- Map<String, AndroidPackage> availablePackages)
- throws PackageManagerException {
- final ArrayList<SharedLibraryInfo> sharedLibraryInfos =
- SharedLibraryHelper.collectSharedLibraryInfos(
- pkgSetting.getPkg(), availablePackages, mSharedLibraries,
- null /* newLibraries */, mInjector.getCompatibility());
- executeSharedLibrariesUpdateLPr(pkg, pkgSetting, changingLib, changingLibSetting,
- sharedLibraryInfos, mUserManager.getUserIds());
- }
-
- void executeSharedLibrariesUpdateLPr(AndroidPackage pkg,
- @NonNull PackageSetting pkgSetting, @Nullable AndroidPackage changingLib,
- @Nullable PackageSetting changingLibSetting,
- ArrayList<SharedLibraryInfo> usesLibraryInfos, int[] allUsers) {
- // If the package provides libraries, clear their old dependencies.
- // This method will set them up again.
- applyDefiningSharedLibraryUpdateLocked(pkg, null, (definingLibrary, dependency) -> {
- definingLibrary.clearDependencies();
- });
- if (usesLibraryInfos != null) {
- pkgSetting.getPkgState().setUsesLibraryInfos(usesLibraryInfos);
- // Use LinkedHashSet to preserve the order of files added to
- // usesLibraryFiles while eliminating duplicates.
- Set<String> usesLibraryFiles = new LinkedHashSet<>();
- for (SharedLibraryInfo libInfo : usesLibraryInfos) {
- addSharedLibraryLPr(pkg, usesLibraryFiles, libInfo, changingLib,
- changingLibSetting);
- }
- pkgSetting.setPkgStateLibraryFiles(usesLibraryFiles);
-
- // let's make sure we mark all static shared libraries as installed for the same users
- // that its dependent packages are installed for.
- int[] installedUsers = new int[allUsers.length];
- int installedUserCount = 0;
- for (int u = 0; u < allUsers.length; u++) {
- if (pkgSetting.getInstalled(allUsers[u])) {
- installedUsers[installedUserCount++] = allUsers[u];
- }
- }
- for (SharedLibraryInfo sharedLibraryInfo : usesLibraryInfos) {
- if (!sharedLibraryInfo.isStatic()) {
- continue;
- }
- final PackageSetting staticLibPkgSetting =
- getPackageSettingForMutation(sharedLibraryInfo.getPackageName());
- if (staticLibPkgSetting == null) {
- Slog.wtf(TAG, "Shared lib without setting: " + sharedLibraryInfo);
- continue;
- }
- for (int u = 0; u < installedUserCount; u++) {
- staticLibPkgSetting.setInstalled(true, installedUsers[u]);
- }
- }
- } else {
- pkgSetting.getPkgState().setUsesLibraryInfos(Collections.emptyList())
- .setUsesLibraryFiles(Collections.emptyList());
- }
- }
-
- private static boolean hasString(List<String> list, List<String> which) {
- if (list == null || which == null) {
- return false;
- }
- for (int i=list.size()-1; i>=0; i--) {
- for (int j=which.size()-1; j>=0; j--) {
- if (which.get(j).equals(list.get(i))) {
- return true;
- }
- }
- }
- return false;
- }
-
- @GuardedBy("mLock")
- ArrayList<AndroidPackage> updateAllSharedLibrariesLocked(
- @Nullable AndroidPackage updatedPkg, @Nullable PackageSetting updatedPkgSetting,
- Map<String, AndroidPackage> availablePackages) {
- ArrayList<AndroidPackage> resultList = null;
- // Set of all descendants of a library; used to eliminate cycles
- ArraySet<String> descendants = null;
- // The current list of packages that need updating
- List<Pair<AndroidPackage, PackageSetting>> needsUpdating = null;
- if (updatedPkg != null && updatedPkgSetting != null) {
- needsUpdating = new ArrayList<>(1);
- needsUpdating.add(Pair.create(updatedPkg, updatedPkgSetting));
- }
- do {
- final Pair<AndroidPackage, PackageSetting> changingPkgPair =
- (needsUpdating == null) ? null : needsUpdating.remove(0);
- final AndroidPackage changingPkg = changingPkgPair != null
- ? changingPkgPair.first : null;
- final PackageSetting changingPkgSetting = changingPkgPair != null
- ? changingPkgPair.second : null;
- for (int i = mPackages.size() - 1; i >= 0; --i) {
- final AndroidPackage pkg = mPackages.valueAt(i);
- final PackageSetting pkgSetting = mSettings.getPackageLPr(pkg.getPackageName());
- if (changingPkg != null
- && !hasString(pkg.getUsesLibraries(), changingPkg.getLibraryNames())
- && !hasString(pkg.getUsesOptionalLibraries(), changingPkg.getLibraryNames())
- && !ArrayUtils.contains(pkg.getUsesStaticLibraries(),
- changingPkg.getStaticSharedLibName())
- && !ArrayUtils.contains(pkg.getUsesSdkLibraries(),
- changingPkg.getSdkLibName())) {
- continue;
- }
- if (resultList == null) {
- resultList = new ArrayList<>();
- }
- resultList.add(pkg);
- // if we're updating a shared library, all of its descendants must be updated
- if (changingPkg != null) {
- if (descendants == null) {
- descendants = new ArraySet<>();
- }
- if (!descendants.contains(pkg.getPackageName())) {
- descendants.add(pkg.getPackageName());
- needsUpdating.add(Pair.create(pkg, pkgSetting));
- }
- }
- try {
- updateSharedLibrariesLocked(pkg, pkgSetting, changingPkg,
- changingPkgSetting, availablePackages);
- } catch (PackageManagerException e) {
- // If a system app update or an app and a required lib missing we
- // delete the package and for updated system apps keep the data as
- // it is better for the user to reinstall than to be in an limbo
- // state. Also libs disappearing under an app should never happen
- // - just in case.
- if (!pkg.isSystem() || pkgSetting.getPkgState().isUpdatedSystemApp()) {
- final int flags = pkgSetting.getPkgState().isUpdatedSystemApp()
- ? PackageManager.DELETE_KEEP_DATA : 0;
- mDeletePackageHelper.deletePackageLIF(pkg.getPackageName(), null, true,
- mUserManager.getUserIds(), flags, null,
- true);
- }
- Slog.e(TAG, "updateAllSharedLibrariesLPw failed: " + e.getMessage());
- }
- }
- } while (needsUpdating != null && needsUpdating.size() > 0);
- return resultList;
- }
-
- @GuardedBy("mLock")
- private void addBuiltInSharedLibraryLocked(SystemConfig.SharedLibraryEntry entry) {
- if (nonStaticSharedLibExistsLocked(entry.name)) {
- return;
- }
-
- SharedLibraryInfo libraryInfo = new SharedLibraryInfo(entry.filename, null, null,
- entry.name, SharedLibraryInfo.VERSION_UNDEFINED,
- SharedLibraryInfo.TYPE_BUILTIN,
- new VersionedPackage(PLATFORM_PACKAGE_NAME, (long)0), null, null,
- entry.isNative);
-
- commitSharedLibraryInfoLocked(libraryInfo);
- }
-
- @GuardedBy("mLock")
- private boolean nonStaticSharedLibExistsLocked(String name) {
- return SharedLibraryHelper.sharedLibExists(name, SharedLibraryInfo.VERSION_UNDEFINED,
- mSharedLibraries);
- }
-
- @GuardedBy("mLock")
- void commitSharedLibraryInfoLocked(SharedLibraryInfo libraryInfo) {
- final String name = libraryInfo.getName();
- WatchedLongSparseArray<SharedLibraryInfo> versionedLib = mSharedLibraries.get(name);
- if (versionedLib == null) {
- versionedLib = new WatchedLongSparseArray<>();
- mSharedLibraries.put(name, versionedLib);
- }
- final String declaringPackageName = libraryInfo.getDeclaringPackage().getPackageName();
- if (libraryInfo.getType() == SharedLibraryInfo.TYPE_STATIC) {
- mStaticLibsByDeclaringPackage.put(declaringPackageName, versionedLib);
- }
- versionedLib.put(libraryInfo.getLongVersion(), libraryInfo);
- }
-
@Override
public Property getProperty(String propertyName, String packageName, String className) {
Objects.requireNonNull(propertyName);
@@ -4926,8 +4570,8 @@
if (pus.isSuspended()) {
for (int i = 0; i < pus.getSuspendParams().size(); i++) {
final SuspendParams params = pus.getSuspendParams().valueAt(i);
- if (params != null && params.appExtras != null) {
- allExtras.putAll(params.appExtras);
+ if (params != null && params.getAppExtras() != null) {
+ allExtras.putAll(params.getAppExtras());
}
}
}
@@ -5000,9 +4644,9 @@
synchronized (mLock) {
for (String packageName : packagesToChange) {
final PackageSetting ps = mSettings.getPackageLPr(packageName);
- if (ps != null && ps.getSuspended(userId)) {
+ if (ps != null && ps.getUserStateOrDefault(userId).isSuspended()) {
ps.removeSuspension(suspendingPackagePredicate, userId);
- if (!ps.getSuspended(userId)) {
+ if (!ps.getUserStateOrDefault(userId).isSuspended()) {
unsuspendedPackages.add(ps.getPackageName());
unsuspendedUids.add(UserHandle.getUid(userId, ps.getAppId()));
}
@@ -7786,8 +7430,8 @@
if (userState.isSuspended()) {
for (int i = 0; i < userState.getSuspendParams().size(); i++) {
final SuspendParams params = userState.getSuspendParams().valueAt(i);
- if (params != null && params.launcherExtras != null) {
- allExtras.putAll(params.launcherExtras);
+ if (params != null && params.getLauncherExtras() != null) {
+ allExtras.putAll(params.getLauncherExtras());
}
}
}
@@ -7876,7 +7520,7 @@
}
final SuspendParams suspendParams = suspendParamsMap.get(suspendingPackage);
- return (suspendParams != null) ? suspendParams.dialogInfo : null;
+ return (suspendParams != null) ? suspendParams.getDialogInfo() : null;
}
@Override
@@ -8322,7 +7966,13 @@
@Override
public void forEachInstalledPackage(@NonNull Consumer<AndroidPackage> actionLocked,
@UserIdInt int userId) {
- PackageManagerService.this.forEachInstalledPackage(actionLocked, userId);
+ forEachInstalledPackage(true, actionLocked, userId);
+ }
+
+ @Override
+ public void forEachInstalledPackage(boolean locked,
+ @NonNull Consumer<AndroidPackage> action, int userId) {
+ PackageManagerService.this.forEachInstalledPackage(locked, action, userId);
}
@Override
@@ -8595,51 +8245,24 @@
@Override
public void withPackageSettingsSnapshot(
@NonNull Consumer<Function<String, PackageStateInternal>> block) {
- final Computer snapshot = snapshotComputer();
-
- // This method needs to either lock or not lock consistently throughout the method,
- // so if the live computer is returned, force a wrapping sync block.
- if (snapshot == mLiveComputer) {
- synchronized (mLock) {
- block.accept(snapshot::getPackageStateInternal);
- }
- } else {
- block.accept(snapshot::getPackageStateInternal);
- }
+ executeWithConsistentComputer(computer ->
+ block.accept(computer::getPackageStateInternal));
}
@Override
public <Output> Output withPackageSettingsSnapshotReturning(
@NonNull FunctionalUtils.ThrowingFunction<Function<String, PackageStateInternal>,
Output> block) {
- final Computer snapshot = snapshotComputer();
-
- // This method needs to either lock or not lock consistently throughout the method,
- // so if the live computer is returned, force a wrapping sync block.
- if (snapshot == mLiveComputer) {
- synchronized (mLock) {
- return block.apply(snapshot::getPackageStateInternal);
- }
- } else {
- return block.apply(snapshot::getPackageStateInternal);
- }
+ return executeWithConsistentComputerReturning(computer ->
+ block.apply(computer::getPackageStateInternal));
}
@Override
public <ExceptionType extends Exception> void withPackageSettingsSnapshotThrowing(
@NonNull FunctionalUtils.ThrowingCheckedConsumer<Function<String,
PackageStateInternal>, ExceptionType> block) throws ExceptionType {
- final Computer snapshot = snapshotComputer();
-
- // This method needs to either lock or not lock consistently throughout the method,
- // so if the live computer is returned, force a wrapping sync block.
- if (snapshot == mLiveComputer) {
- synchronized (mLock) {
- block.accept(snapshot::getPackageStateInternal);
- }
- } else {
- block.accept(snapshot::getPackageStateInternal);
- }
+ executeWithConsistentComputerThrowing(computer ->
+ block.accept(computer::getPackageStateInternal));
}
@Override
@@ -8649,17 +8272,9 @@
Function<String, PackageStateInternal>, ExceptionOne,
ExceptionTwo> block)
throws ExceptionOne, ExceptionTwo {
- final Computer snapshot = snapshotComputer();
-
- // This method needs to either lock or not lock consistently throughout the method,
- // so if the live computer is returned, force a wrapping sync block.
- if (snapshot == mLiveComputer) {
- synchronized (mLock) {
- block.accept(snapshot::getPackageStateInternal);
- }
- } else {
- block.accept(snapshot::getPackageStateInternal);
- }
+ executeWithConsistentComputerThrowing2(
+ (FunctionalUtils.ThrowingChecked2Consumer<Computer, ExceptionOne,
+ ExceptionTwo>) computer -> block.accept(computer::getPackageStateInternal));
}
@Override
@@ -8669,17 +8284,8 @@
Function<String, PackageStateInternal>, Output,
ExceptionType> block)
throws ExceptionType {
- final Computer snapshot = snapshotComputer();
-
- // This method needs to either lock or not lock consistently throughout the method,
- // so if the live computer is returned, force a wrapping sync block.
- if (snapshot == mLiveComputer) {
- synchronized (mLock) {
- return block.apply(snapshot::getPackageStateInternal);
- }
- } else {
- return block.apply(snapshot::getPackageStateInternal);
- }
+ return executeWithConsistentComputerReturningThrowing(computer ->
+ block.apply(computer::getPackageStateInternal));
}
@Override
@@ -8688,6 +8294,20 @@
PackageManagerService.this.mAppDataHelper.reconcileAppsData(userId, flags,
migrateAppsData);
}
+
+ @NonNull
+ @Override
+ public PackageStateMutator.InitialState recordInitialState() {
+ return PackageManagerService.this.recordInitialState();
+ }
+
+ @Nullable
+ @Override
+ public PackageStateMutator.Result commitPackageStateMutation(
+ @Nullable PackageStateMutator.InitialState state,
+ @NonNull Consumer<PackageStateMutator> consumer) {
+ return PackageManagerService.this.commitPackageStateMutation(state, consumer);
+ }
}
@Override
@@ -8730,7 +8350,15 @@
@Nullable
@GuardedBy("mLock")
PackageSetting getPackageSettingForMutation(String packageName) {
- return (PackageSetting) mComputer.getPackageStateInternal(packageName);
+ return mSettings.getPackageLPr(packageName);
+ }
+
+ // TODO: Remove
+ @Deprecated
+ @Nullable
+ @GuardedBy("mLock")
+ PackageSetting getDisabledPackageSettingForMutation(String packageName) {
+ return mSettings.getDisabledSystemPkgLPr(packageName);
}
@VisibleForTesting(visibility = Visibility.PRIVATE)
@@ -8738,7 +8366,7 @@
PackageStateInternal getPackageStateInternal(String packageName) {
Computer computer = snapshotComputer();
if (computer == mLiveComputer) {
- synchronized (mLock) {
+ synchronized (mLiveComputerSyncLock) {
PackageSetting pkgSetting =
(PackageSetting) computer.getPackageStateInternal(packageName);
if (pkgSetting == null) {
@@ -8747,15 +8375,16 @@
return new PackageSetting(pkgSetting);
}
+ } else {
+ return computer.getPackageStateInternal(packageName);
}
- return computer.getPackageStateInternal(packageName);
}
@Nullable
PackageStateInternal getPackageStateInternal(String packageName, int callingUid) {
Computer computer = snapshotComputer();
if (computer == mLiveComputer) {
- synchronized (mLock) {
+ synchronized (mLiveComputerSyncLock) {
PackageSetting pkgSetting =
(PackageSetting) computer.getPackageStateInternal(packageName, callingUid);
if (pkgSetting == null) {
@@ -8764,8 +8393,9 @@
return new PackageSetting(pkgSetting);
}
+ } else {
+ return computer.getPackageStateInternal(packageName, callingUid);
}
- return computer.getPackageStateInternal(packageName, callingUid);
}
@Nullable
@@ -8773,7 +8403,7 @@
int callingUid, @UserIdInt int userId) {
Computer computer = snapshotComputer();
if (computer == mLiveComputer) {
- synchronized (mLock) {
+ synchronized (mLiveComputerSyncLock) {
PackageSetting pkgSetting =
(PackageSetting) filterPackageStateForInstalledAndFiltered(computer,
packageName, callingUid, userId);
@@ -8782,9 +8412,10 @@
}
return new PackageSetting(pkgSetting);
}
+ } else {
+ return filterPackageStateForInstalledAndFiltered(computer, packageName, callingUid,
+ userId);
}
-
- return filterPackageStateForInstalledAndFiltered(computer, packageName, callingUid, userId);
}
@Nullable
@@ -8812,16 +8443,8 @@
Computer computer = snapshotComputer();
if (computer == mLiveComputer) {
return new ArrayMap<>(computer.getPackageStates());
- }
- return computer.getPackageStates();
- }
-
- void forEachPackage(Consumer<AndroidPackage> actionLocked) {
- synchronized (mLock) {
- int numPackages = mPackages.size();
- for (int i = 0; i < numPackages; i++) {
- actionLocked.accept(mPackages.valueAt(i));
- }
+ } else {
+ return computer.getPackageStates();
}
}
@@ -8834,17 +8457,31 @@
}
}
- private void forEachPackageState(boolean locked, Consumer<PackageStateInternal> action) {
+ void forEachPackageState(boolean locked, Consumer<PackageStateInternal> action) {
if (locked) {
- forEachPackageSetting(action::accept);
+ synchronized (mLiveComputerSyncLock) {
+ forEachPackageState(mComputer.getPackageStates(), action);
+ }
} else {
Computer computer = snapshotComputer();
if (computer == mLiveComputer) {
- synchronized (mLock) {
+ synchronized (mLiveComputerSyncLock) {
forEachPackageState(computer.getPackageStates(), action);
- };
+ }
+ } else {
+ forEachPackageState(computer.getPackageStates(), action);
}
- forEachPackageState(computer.getPackageStates(), action);
+ }
+ }
+
+ void forEachPackage(Consumer<AndroidPackage> action) {
+ Computer computer = snapshotComputer();
+ if (computer == mLiveComputer) {
+ synchronized (mLiveComputerSyncLock) {
+ forEachPackage(computer.getPackageStates(), action);
+ }
+ } else {
+ forEachPackage(computer.getPackageStates(), action);
}
}
@@ -8858,21 +8495,106 @@
}
}
- void forEachInstalledPackage(@NonNull Consumer<AndroidPackage> actionLocked,
- @UserIdInt int userId) {
- synchronized (mLock) {
- int numPackages = mPackages.size();
- for (int i = 0; i < numPackages; i++) {
- AndroidPackage pkg = mPackages.valueAt(i);
- PackageSetting setting = mSettings.getPackageLPr(pkg.getPackageName());
- if (setting == null || !setting.getInstalled(userId)) {
- continue;
- }
- actionLocked.accept(pkg);
+ private void forEachPackage(
+ @NonNull ArrayMap<String, ? extends PackageStateInternal> packageStates,
+ @NonNull Consumer<AndroidPackage> consumer) {
+ int size = packageStates.size();
+ for (int index = 0; index < size; index++) {
+ PackageStateInternal packageState = packageStates.valueAt(index);
+ if (packageState.getPkg() != null) {
+ consumer.accept(packageState.getPkg());
}
}
}
+ void forEachInstalledPackage(boolean locked, @NonNull Consumer<AndroidPackage> action,
+ @UserIdInt int userId) {
+ Consumer<PackageStateInternal> actionWrapped = packageState -> {
+ if (packageState.getPkg() != null
+ && packageState.getUserStateOrDefault(userId).isInstalled()) {
+ action.accept(packageState.getPkg());
+ }
+ };
+ if (locked) {
+ synchronized (mLiveComputerSyncLock) {
+ forEachPackageState(mComputer.getPackageStates(), actionWrapped);
+ }
+ } else {
+ Computer computer = snapshotComputer();
+ if (computer == mLiveComputer) {
+ synchronized (mLiveComputerSyncLock) {
+ forEachPackageState(computer.getPackageStates(), actionWrapped);
+ }
+ } else {
+ forEachPackageState(computer.getPackageStates(), actionWrapped);
+ }
+ }
+ }
+
+ private void executeWithConsistentComputer(
+ @NonNull FunctionalUtils.ThrowingConsumer<Computer> consumer) {
+ Computer computer = snapshotComputer();
+ if (computer == mLiveComputer) {
+ synchronized (mLiveComputerSyncLock) {
+ consumer.accept(computer);
+ }
+ } else {
+ consumer.accept(computer);
+ }
+ }
+
+ private <T> T executeWithConsistentComputerReturning(
+ @NonNull FunctionalUtils.ThrowingFunction<Computer, T> function) {
+ Computer computer = snapshotComputer();
+ if (computer == mLiveComputer) {
+ synchronized (mLiveComputerSyncLock) {
+ return function.apply(computer);
+ }
+ } else {
+ return function.apply(computer);
+ }
+ }
+
+ private <ExceptionType extends Exception> void executeWithConsistentComputerThrowing(
+ @NonNull FunctionalUtils.ThrowingCheckedConsumer<Computer, ExceptionType> consumer)
+ throws ExceptionType {
+ Computer computer = snapshotComputer();
+ if (computer == mLiveComputer) {
+ synchronized (mLiveComputerSyncLock) {
+ consumer.accept(computer);
+ }
+ } else {
+ consumer.accept(computer);
+ }
+ }
+
+ private <ExceptionOne extends Exception, ExceptionTwo extends Exception> void
+ executeWithConsistentComputerThrowing2(
+ @NonNull FunctionalUtils.ThrowingChecked2Consumer<Computer, ExceptionOne,
+ ExceptionTwo> consumer) throws ExceptionOne, ExceptionTwo {
+ Computer computer = snapshotComputer();
+ if (computer == mLiveComputer) {
+ synchronized (mLiveComputerSyncLock) {
+ consumer.accept(computer);
+ }
+ } else {
+ consumer.accept(computer);
+ }
+ }
+
+ private <T, ExceptionType extends Exception> T executeWithConsistentComputerReturningThrowing(
+ @NonNull FunctionalUtils.ThrowingCheckedFunction<Computer, T, ExceptionType> function)
+ throws ExceptionType {
+ Computer computer = snapshotComputer();
+ if (computer == mLiveComputer) {
+ synchronized (mLiveComputerSyncLock) {
+ return function.apply(computer);
+ }
+ } else {
+ return function.apply(computer);
+ }
+ }
+
boolean isHistoricalPackageUsageAvailable() {
return mPackageUsage.isHistoricalPackageUsageAvailable();
}
@@ -9068,7 +8790,8 @@
enforceOwnerRights(packageName, Binder.getCallingUid());
final boolean changed;
synchronized (mLock) {
- changed = mSettings.getPackageLPr(packageName).setMimeGroup(mimeGroup, mimeTypes);
+ changed = mSettings.getPackageLPr(packageName).setMimeGroup(mimeGroup,
+ new ArraySet<>(mimeTypes));
}
if (changed) {
applyMimeGroupChanges(packageName, mimeGroup);
@@ -9571,4 +9294,62 @@
return new Pair<>(rescanFlags, reparseFlags);
}
+
+ /**
+ * @see PackageManagerInternal#recordInitialState()
+ */
+ @NonNull
+ public PackageStateMutator.InitialState recordInitialState() {
+ return mPackageStateMutator.initialState(mChangedPackagesSequenceNumber);
+ }
+
+ /**
+ * @see PackageManagerInternal#commitPackageStateMutation(PackageStateMutator.InitialState,
+ * Consumer)
+ */
+ @NonNull
+ public PackageStateMutator.Result commitPackageStateMutation(
+ @Nullable PackageStateMutator.InitialState initialState,
+ @NonNull Consumer<PackageStateMutator> consumer) {
+ synchronized (mPackageStateWriteLock) {
+ final PackageStateMutator.Result result = mPackageStateMutator.generateResult(
+ initialState, mChangedPackagesSequenceNumber);
+ if (result != PackageStateMutator.Result.SUCCESS) {
+ return result;
+ }
+
+ consumer.accept(mPackageStateMutator);
+ onChanged();
+ }
+
+ return PackageStateMutator.Result.SUCCESS;
+ }
+
+ /**
+ * @see PackageManagerInternal#commitPackageStateMutation(PackageStateMutator.InitialState,
+ * Consumer)
+ */
+ @NonNull
+ public PackageStateMutator.Result commitPackageStateMutation(
+ @Nullable PackageStateMutator.InitialState initialState, @NonNull String packageName,
+ @NonNull Consumer<PackageStateWrite> consumer) {
+ synchronized (mPackageStateWriteLock) {
+ final PackageStateMutator.Result result = mPackageStateMutator.generateResult(
+ initialState, mChangedPackagesSequenceNumber);
+ if (result != PackageStateMutator.Result.SUCCESS) {
+ return result;
+ }
+
+ PackageStateWrite state = mPackageStateMutator.forPackage(packageName);
+ if (state == null) {
+ return PackageStateMutator.Result.SPECIFIC_PACKAGE_NULL;
+ } else {
+ consumer.accept(state);
+ }
+
+ onChanged();
+ }
+
+ return PackageStateMutator.Result.SUCCESS;
+ }
}
diff --git a/services/core/java/com/android/server/pm/PackageManagerServiceInjector.java b/services/core/java/com/android/server/pm/PackageManagerServiceInjector.java
index d14cc1f..05bb01e 100644
--- a/services/core/java/com/android/server/pm/PackageManagerServiceInjector.java
+++ b/services/core/java/com/android/server/pm/PackageManagerServiceInjector.java
@@ -137,6 +137,7 @@
private final Singleton<Handler> mHandlerProducer;
private final Singleton<BackgroundDexOptService> mBackgroundDexOptService;
private final Singleton<IBackupManager> mIBackupManager;
+ private final Singleton<SharedLibrariesImpl> mSharedLibrariesProducer;
PackageManagerServiceInjector(Context context, PackageManagerTracedLock lock,
Installer installer, Object installLock, PackageAbiHelper abiHelper,
@@ -173,7 +174,8 @@
ServiceProducer getLocalServiceProducer,
ServiceProducer getSystemServiceProducer,
Producer<BackgroundDexOptService> backgroundDexOptService,
- Producer<IBackupManager> iBackupManager) {
+ Producer<IBackupManager> iBackupManager,
+ Producer<SharedLibrariesImpl> sharedLibrariesProducer) {
mContext = context;
mLock = lock;
mInstaller = installer;
@@ -224,6 +226,7 @@
mHandlerProducer = new Singleton<>(handlerProducer);
mBackgroundDexOptService = new Singleton<>(backgroundDexOptService);
mIBackupManager = new Singleton<>(iBackupManager);
+ mSharedLibrariesProducer = new Singleton<>(sharedLibrariesProducer);
}
/**
@@ -392,6 +395,10 @@
return mIBackupManager.get(this, mPackageManager);
}
+ public SharedLibrariesImpl getSharedLibrariesImpl() {
+ return mSharedLibrariesProducer.get(this, mPackageManager);
+ }
+
/** Provides an abstraction to static access to system state. */
public interface SystemWrapper {
void disablePackageCaches();
diff --git a/services/core/java/com/android/server/pm/PackageSessionVerifier.java b/services/core/java/com/android/server/pm/PackageSessionVerifier.java
index 4f21d0e..a532fe3 100644
--- a/services/core/java/com/android/server/pm/PackageSessionVerifier.java
+++ b/services/core/java/com/android/server/pm/PackageSessionVerifier.java
@@ -45,6 +45,7 @@
import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.content.PackageHelper;
import com.android.server.LocalServices;
+import com.android.server.SystemConfig;
import com.android.server.pm.parsing.PackageParser2;
import com.android.server.pm.parsing.pkg.ParsedPackage;
import com.android.server.rollback.RollbackManagerInternal;
@@ -99,9 +100,11 @@
storeSession(session.mStagedSession);
if (session.isMultiPackage()) {
for (PackageInstallerSession child : session.getChildSessions()) {
+ checkApexUpdateAllowed(child);
checkRebootlessApex(child);
}
} else {
+ checkApexUpdateAllowed(session);
checkRebootlessApex(session);
}
verifyAPK(session, callback);
@@ -203,7 +206,7 @@
}
private void onVerificationFailure(StagingManager.StagedSession session, Callback callback,
- @SessionInfo.StagedSessionErrorCode int errorCode, String errorMessage) {
+ @SessionInfo.SessionErrorCode int errorCode, String errorMessage) {
if (!ensureActiveApexSessionIsAborted(session)) {
Slog.e(TAG, "Failed to abort apex session " + session.sessionId());
// Safe to ignore active apex session abortion failure since session will be marked
@@ -461,6 +464,51 @@
return mApexManager.abortStagedSession(sessionId);
}
+ private boolean isApexUpdateAllowed(String apexPackageName, String installerPackageName) {
+ if (mPm.getModuleInfo(apexPackageName, 0) != null) {
+ final String modulesInstaller =
+ SystemConfig.getInstance().getModulesInstallerPackageName();
+ if (modulesInstaller == null) {
+ Slog.w(TAG, "No modules installer defined");
+ return false;
+ }
+ return modulesInstaller.equals(installerPackageName);
+ }
+ final String vendorApexInstaller =
+ SystemConfig.getInstance().getAllowedVendorApexes().get(apexPackageName);
+ if (vendorApexInstaller == null) {
+ Slog.w(TAG, apexPackageName + " is not allowed to be updated");
+ return false;
+ }
+ return vendorApexInstaller.equals(installerPackageName);
+ }
+
+ /**
+ * Checks if APEX update is allowed.
+ *
+ * This phase is shared between staged and non-staged sessions and should be called after
+ * boot is completed since this check depends on the ModuleInfoProvider, which is only populated
+ * after device has booted.
+ */
+ private void checkApexUpdateAllowed(PackageInstallerSession session)
+ throws PackageManagerException {
+ if (!session.isApexSession()) {
+ return;
+ }
+ final int installFlags = session.params.installFlags;
+ if ((installFlags & PackageManager.INSTALL_DISABLE_ALLOWED_APEX_UPDATE_CHECK) != 0) {
+ return;
+ }
+ final String packageName = session.getPackageName();
+ final String installerPackageName = session.getInstallSource().installerPackageName;
+ if (!isApexUpdateAllowed(packageName, installerPackageName)) {
+ throw new PackageManagerException(
+ PackageManager.INSTALL_FAILED_VERIFICATION_FAILURE,
+ "Update of APEX package " + packageName + " is not allowed for "
+ + installerPackageName);
+ }
+ }
+
/**
* Fails this rebootless APEX session if the same package name found in any staged sessions.
*/
diff --git a/services/core/java/com/android/server/pm/PackageSetting.java b/services/core/java/com/android/server/pm/PackageSetting.java
index 923a133..0da57bc 100644
--- a/services/core/java/com/android/server/pm/PackageSetting.java
+++ b/services/core/java/com/android/server/pm/PackageSetting.java
@@ -395,14 +395,13 @@
return this;
}
- public boolean setMimeGroup(String mimeGroup, List<String> mimeTypes) {
+ public boolean setMimeGroup(String mimeGroup, ArraySet<String> newMimeTypes) {
Set<String> oldMimeTypes = mimeGroups == null ? null : mimeGroups.get(mimeGroup);
if (oldMimeTypes == null) {
throw new IllegalArgumentException("Unknown MIME group " + mimeGroup
+ " for package " + mName);
}
- ArraySet<String> newMimeTypes = new ArraySet<>(mimeTypes);
boolean hasChanges = !newMimeTypes.equals(oldMimeTypes);
mimeGroups.put(mimeGroup, newMimeTypes);
if (hasChanges) {
@@ -670,6 +669,15 @@
return state;
}
+ public PackageUserStateImpl getOrCreateUserState(@UserIdInt int userId) {
+ PackageUserStateImpl state = mUserStates.get(userId);
+ if (state == null) {
+ state = new PackageUserStateImpl();
+ mUserStates.put(userId, state);
+ }
+ return state;
+ }
+
@NonNull
public PackageUserStateInternal readUserState(int userId) {
PackageUserStateInternal state = mUserStates.get(userId);
@@ -832,7 +840,6 @@
}
final SuspendParams oldSuspendParams =
existingUserState.getSuspendParams().put(suspendingPackage, newSuspendParams);
- existingUserState.setSuspended(true);
onChanged();
return !Objects.equals(oldSuspendParams, newSuspendParams);
}
@@ -848,7 +855,6 @@
existingUserState.setSuspendParams(null);
}
}
- existingUserState.setSuspended((existingUserState.getSuspendParams() != null));
onChanged();
return wasModified;
}
@@ -866,7 +872,6 @@
existingUserState.setSuspendParams(null);
}
}
- existingUserState.setSuspended((existingUserState.getSuspendParams() != null));
onChanged();
}
@@ -889,7 +894,7 @@
}
void setUserState(int userId, long ceDataInode, int enabled, boolean installed, boolean stopped,
- boolean notLaunched, boolean hidden, int distractionFlags, boolean suspended,
+ boolean notLaunched, boolean hidden, int distractionFlags,
ArrayMap<String, SuspendParams> suspendParams, boolean instantApp,
boolean virtualPreload, String lastDisableAppCaller,
ArraySet<String> enabledComponents, ArraySet<String> disabledComponents,
@@ -904,7 +909,6 @@
.setNotLaunched(notLaunched)
.setHidden(hidden)
.setDistractionFlags(distractionFlags)
- .setSuspended(suspended)
.setLastDisableAppCaller(lastDisableAppCaller)
.setEnabledComponents(enabledComponents)
.setDisabledComponents(disabledComponents)
@@ -919,11 +923,9 @@
void setUserState(int userId, PackageUserStateInternal otherState) {
setUserState(userId, otherState.getCeDataInode(), otherState.getEnabledState(),
- otherState.isInstalled(),
- otherState.isStopped(), otherState.isNotLaunched(), otherState.isHidden(),
- otherState.getDistractionFlags(), otherState.isSuspended(),
- otherState.getSuspendParams(),
- otherState.isInstantApp(),
+ otherState.isInstalled(), otherState.isStopped(), otherState.isNotLaunched(),
+ otherState.isHidden(), otherState.getDistractionFlags(),
+ otherState.getSuspendParams(), otherState.isInstantApp(),
otherState.isVirtualPreload(), otherState.getLastDisableAppCaller(),
new ArraySet<>(otherState.getEnabledComponentsNoCopy()),
new ArraySet<>(otherState.getDisabledComponentsNoCopy()),
@@ -1553,10 +1555,10 @@
}
@DataClass.Generated(
- time = 1635870549646L,
+ time = 1636145878985L,
codegenVersion = "1.0.23",
sourceFile = "frameworks/base/services/core/java/com/android/server/pm/PackageSetting.java",
- inputSignatures = "private int sharedUserId\nprivate @android.annotation.Nullable java.util.Map<java.lang.String,java.util.Set<java.lang.String>> mimeGroups\nprivate @java.lang.Deprecated @android.annotation.Nullable java.util.Set<java.lang.String> mOldCodePaths\nprivate @android.annotation.Nullable java.lang.String[] usesStaticLibraries\nprivate @android.annotation.Nullable long[] usesStaticLibrariesVersions\nprivate @android.annotation.Nullable @java.lang.Deprecated java.lang.String legacyNativeLibraryPath\nprivate @android.annotation.NonNull java.lang.String mName\nprivate @android.annotation.Nullable java.lang.String mRealName\nprivate int mAppId\nprivate @android.annotation.Nullable com.android.server.pm.parsing.pkg.AndroidPackage pkg\nprivate @android.annotation.Nullable com.android.server.pm.SharedUserSetting sharedUser\nprivate @android.annotation.NonNull java.io.File mPath\nprivate @android.annotation.NonNull java.lang.String mPathString\nprivate float mLoadingProgress\nprivate @android.annotation.Nullable java.lang.String mPrimaryCpuAbi\nprivate @android.annotation.Nullable java.lang.String mSecondaryCpuAbi\nprivate @android.annotation.Nullable java.lang.String mCpuAbiOverride\nprivate long mLastModifiedTime\nprivate long firstInstallTime\nprivate long lastUpdateTime\nprivate long versionCode\nprivate @android.annotation.NonNull com.android.server.pm.PackageSignatures signatures\nprivate boolean installPermissionsFixed\nprivate @android.annotation.NonNull com.android.server.pm.PackageKeySetData keySetData\nprivate final @android.annotation.NonNull android.util.SparseArray<com.android.server.pm.pkg.PackageUserStateImpl> mUserStates\nprivate @android.annotation.NonNull com.android.server.pm.InstallSource installSource\nprivate @android.annotation.Nullable java.lang.String volumeUuid\nprivate int categoryOverride\nprivate boolean updateAvailable\nprivate boolean forceQueryableOverride\nprivate @android.annotation.NonNull com.android.server.pm.pkg.PackageStateUnserialized pkgState\nprivate @android.annotation.NonNull java.util.UUID mDomainSetId\nprivate final @android.annotation.NonNull com.android.server.utils.SnapshotCache<com.android.server.pm.PackageSetting> mSnapshot\nprivate com.android.server.utils.SnapshotCache<com.android.server.pm.PackageSetting> makeCache()\npublic com.android.server.pm.PackageSetting snapshot()\npublic void dumpDebug(android.util.proto.ProtoOutputStream,long,java.util.List<android.content.pm.UserInfo>,com.android.server.pm.permission.LegacyPermissionDataProvider)\npublic java.util.List<java.lang.String> getMimeGroup(java.lang.String)\nprivate java.util.Set<java.lang.String> getMimeGroupInternal(java.lang.String)\npublic boolean isSharedUser()\npublic com.android.server.pm.PackageSetting setAppId(int)\npublic com.android.server.pm.PackageSetting setCpuAbiOverride(java.lang.String)\npublic com.android.server.pm.PackageSetting setFirstInstallTime(long)\npublic com.android.server.pm.PackageSetting setForceQueryableOverride(boolean)\npublic com.android.server.pm.PackageSetting setInstallerPackageName(java.lang.String)\npublic com.android.server.pm.PackageSetting setInstallSource(com.android.server.pm.InstallSource)\n com.android.server.pm.PackageSetting removeInstallerPackage(java.lang.String)\npublic com.android.server.pm.PackageSetting setIsOrphaned(boolean)\npublic com.android.server.pm.PackageSetting setKeySetData(com.android.server.pm.PackageKeySetData)\npublic com.android.server.pm.PackageSetting setLastModifiedTime(long)\npublic com.android.server.pm.PackageSetting setLastUpdateTime(long)\npublic com.android.server.pm.PackageSetting setLongVersionCode(long)\npublic boolean setMimeGroup(java.lang.String,java.util.List<java.lang.String>)\npublic com.android.server.pm.PackageSetting setPkg(com.android.server.pm.parsing.pkg.AndroidPackage)\npublic com.android.server.pm.PackageSetting setPrimaryCpuAbi(java.lang.String)\npublic com.android.server.pm.PackageSetting setSecondaryCpuAbi(java.lang.String)\npublic com.android.server.pm.PackageSetting setSignatures(com.android.server.pm.PackageSignatures)\npublic com.android.server.pm.PackageSetting setVolumeUuid(java.lang.String)\npublic @java.lang.Override boolean isExternalStorage()\npublic com.android.server.pm.PackageSetting setUpdateAvailable(boolean)\npublic int getSharedUserIdInt()\npublic @java.lang.Override java.lang.String toString()\nprotected void copyMimeGroups(java.util.Map<java.lang.String,java.util.Set<java.lang.String>>)\npublic void updateFrom(com.android.server.pm.PackageSetting)\n com.android.server.pm.PackageSetting updateMimeGroups(java.util.Set<java.lang.String>)\npublic @java.lang.Deprecated @java.lang.Override com.android.server.pm.permission.LegacyPermissionState getLegacyPermissionState()\npublic com.android.server.pm.PackageSetting setInstallPermissionsFixed(boolean)\npublic boolean isPrivileged()\npublic boolean isOem()\npublic boolean isVendor()\npublic boolean isProduct()\npublic @java.lang.Override boolean isRequiredForSystemUser()\npublic boolean isSystemExt()\npublic boolean isOdm()\npublic boolean isSystem()\npublic android.content.pm.SigningDetails getSigningDetails()\npublic com.android.server.pm.PackageSetting setSigningDetails(android.content.pm.SigningDetails)\npublic void copyPackageSetting(com.android.server.pm.PackageSetting)\n @com.android.internal.annotations.VisibleForTesting com.android.server.pm.pkg.PackageUserStateImpl modifyUserState(int)\npublic @android.annotation.NonNull com.android.server.pm.pkg.PackageUserStateInternal readUserState(int)\n void setEnabled(int,int,java.lang.String)\n int getEnabled(int)\n java.lang.String getLastDisabledAppCaller(int)\n void setInstalled(boolean,int)\n boolean getInstalled(int)\n int getInstallReason(int)\n void setInstallReason(int,int)\n int getUninstallReason(int)\n void setUninstallReason(int,int)\n boolean setOverlayPaths(android.content.pm.overlay.OverlayPaths,int)\n @android.annotation.NonNull android.content.pm.overlay.OverlayPaths getOverlayPaths(int)\n boolean setOverlayPathsForLibrary(java.lang.String,android.content.pm.overlay.OverlayPaths,int)\n @android.annotation.NonNull java.util.Map<java.lang.String,android.content.pm.overlay.OverlayPaths> getOverlayPathsForLibrary(int)\n boolean isAnyInstalled(int[])\n int[] queryInstalledUsers(int[],boolean)\n long getCeDataInode(int)\n void setCeDataInode(long,int)\n boolean getStopped(int)\n void setStopped(boolean,int)\n boolean getNotLaunched(int)\n void setNotLaunched(boolean,int)\n boolean getHidden(int)\n void setHidden(boolean,int)\n int getDistractionFlags(int)\n void setDistractionFlags(int,int)\n boolean getSuspended(int)\n boolean addOrUpdateSuspension(java.lang.String,android.content.pm.SuspendDialogInfo,android.os.PersistableBundle,android.os.PersistableBundle,int)\n boolean removeSuspension(java.lang.String,int)\n void removeSuspension(java.util.function.Predicate<java.lang.String>,int)\npublic boolean getInstantApp(int)\n void setInstantApp(boolean,int)\n boolean getVirtualPreload(int)\n void setVirtualPreload(boolean,int)\n void setUserState(int,long,int,boolean,boolean,boolean,boolean,int,boolean,android.util.ArrayMap<java.lang.String,com.android.server.pm.pkg.SuspendParams>,boolean,boolean,java.lang.String,android.util.ArraySet<java.lang.String>,android.util.ArraySet<java.lang.String>,int,int,java.lang.String,java.lang.String)\n void setUserState(int,com.android.server.pm.pkg.PackageUserStateInternal)\n android.util.ArraySet<java.lang.String> getEnabledComponents(int)\n android.util.ArraySet<java.lang.String> getDisabledComponents(int)\n void setEnabledComponents(android.util.ArraySet<java.lang.String>,int)\n void setDisabledComponents(android.util.ArraySet<java.lang.String>,int)\n void setEnabledComponentsCopy(android.util.ArraySet<java.lang.String>,int)\n void setDisabledComponentsCopy(android.util.ArraySet<java.lang.String>,int)\n com.android.server.pm.pkg.PackageUserStateImpl modifyUserStateComponents(int,boolean,boolean)\n void addDisabledComponent(java.lang.String,int)\n void addEnabledComponent(java.lang.String,int)\n boolean enableComponentLPw(java.lang.String,int)\n boolean disableComponentLPw(java.lang.String,int)\n boolean restoreComponentLPw(java.lang.String,int)\n int getCurrentEnabledStateLPr(java.lang.String,int)\n void removeUser(int)\npublic int[] getNotInstalledUserIds()\n void writePackageUserPermissionsProto(android.util.proto.ProtoOutputStream,long,java.util.List<android.content.pm.UserInfo>,com.android.server.pm.permission.LegacyPermissionDataProvider)\nprotected void writeUsersInfoToProto(android.util.proto.ProtoOutputStream,long)\n void setHarmfulAppWarning(int,java.lang.String)\n java.lang.String getHarmfulAppWarning(int)\n com.android.server.pm.PackageSetting setPath(java.io.File)\npublic @com.android.internal.annotations.VisibleForTesting boolean overrideNonLocalizedLabelAndIcon(android.content.ComponentName,java.lang.String,java.lang.Integer,int)\npublic void resetOverrideComponentLabelIcon(int)\npublic void setSplashScreenTheme(int,java.lang.String)\npublic @android.annotation.Nullable java.lang.String getSplashScreenTheme(int)\npublic boolean isLoading()\npublic com.android.server.pm.PackageSetting setLoadingProgress(float)\npublic @android.annotation.NonNull @java.lang.Override long getVersionCode()\npublic @android.annotation.Nullable @java.lang.Override java.util.Map<java.lang.String,java.util.Set<java.lang.String>> getMimeGroups()\npublic @android.annotation.NonNull @java.lang.Override java.lang.String getPackageName()\npublic @android.annotation.Nullable @java.lang.Override com.android.server.pm.pkg.AndroidPackageApi getAndroidPackage()\npublic @android.annotation.Nullable @java.lang.Override java.lang.Integer getSharedUserId()\npublic @android.annotation.NonNull android.content.pm.SigningInfo getSigningInfo()\npublic @android.annotation.NonNull @java.lang.Override java.lang.String[] getUsesStaticLibraries()\npublic @android.annotation.NonNull @java.lang.Override long[] getUsesStaticLibrariesVersions()\npublic @android.annotation.NonNull @java.lang.Override java.util.List<android.content.pm.SharedLibraryInfo> getUsesLibraryInfos()\npublic @android.annotation.NonNull @java.lang.Override java.util.List<java.lang.String> getUsesLibraryFiles()\npublic @java.lang.Override boolean isHiddenUntilInstalled()\npublic @android.annotation.NonNull @java.lang.Override long[] getLastPackageUsageTime()\npublic @java.lang.Override boolean isUpdatedSystemApp()\npublic com.android.server.pm.PackageSetting setDomainSetId(java.util.UUID)\npublic com.android.server.pm.PackageSetting setSharedUser(com.android.server.pm.SharedUserSetting)\npublic com.android.server.pm.PackageSetting setCategoryOverride(int)\npublic com.android.server.pm.PackageSetting setLegacyNativeLibraryPath(java.lang.String)\npublic com.android.server.pm.PackageSetting setMimeGroups(java.util.Map<java.lang.String,java.util.Set<java.lang.String>>)\npublic com.android.server.pm.PackageSetting setOldCodePaths(java.util.Set<java.lang.String>)\npublic com.android.server.pm.PackageSetting setUsesStaticLibraries(java.lang.String[])\npublic com.android.server.pm.PackageSetting setUsesStaticLibrariesVersions(long[])\npublic @android.annotation.NonNull @java.lang.Override com.android.server.pm.pkg.PackageStateUnserialized getTransientState()\npublic @android.annotation.NonNull android.util.SparseArray<? extends PackageUserStateInternal> getUserStates()\nclass PackageSetting extends com.android.server.pm.SettingBase implements [com.android.server.pm.pkg.PackageStateInternal]\n@com.android.internal.util.DataClass(genGetters=true, genConstructor=false, genSetters=false, genBuilder=false)")
+ inputSignatures = "private int sharedUserId\nprivate @android.annotation.Nullable java.util.Map<java.lang.String,java.util.Set<java.lang.String>> mimeGroups\nprivate @java.lang.Deprecated @android.annotation.Nullable java.util.Set<java.lang.String> mOldCodePaths\nprivate @android.annotation.Nullable java.lang.String[] usesStaticLibraries\nprivate @android.annotation.Nullable long[] usesStaticLibrariesVersions\nprivate @android.annotation.Nullable @java.lang.Deprecated java.lang.String legacyNativeLibraryPath\nprivate @android.annotation.NonNull java.lang.String mName\nprivate @android.annotation.Nullable java.lang.String mRealName\nprivate int mAppId\nprivate @android.annotation.Nullable com.android.server.pm.parsing.pkg.AndroidPackage pkg\nprivate @android.annotation.Nullable com.android.server.pm.SharedUserSetting sharedUser\nprivate @android.annotation.NonNull java.io.File mPath\nprivate @android.annotation.NonNull java.lang.String mPathString\nprivate float mLoadingProgress\nprivate @android.annotation.Nullable java.lang.String mPrimaryCpuAbi\nprivate @android.annotation.Nullable java.lang.String mSecondaryCpuAbi\nprivate @android.annotation.Nullable java.lang.String mCpuAbiOverride\nprivate long mLastModifiedTime\nprivate long firstInstallTime\nprivate long lastUpdateTime\nprivate long versionCode\nprivate @android.annotation.NonNull com.android.server.pm.PackageSignatures signatures\nprivate boolean installPermissionsFixed\nprivate @android.annotation.NonNull com.android.server.pm.PackageKeySetData keySetData\nprivate final @android.annotation.NonNull android.util.SparseArray<com.android.server.pm.pkg.PackageUserStateImpl> mUserStates\nprivate @android.annotation.NonNull com.android.server.pm.InstallSource installSource\nprivate @android.annotation.Nullable java.lang.String volumeUuid\nprivate int categoryOverride\nprivate boolean updateAvailable\nprivate boolean forceQueryableOverride\nprivate @android.annotation.NonNull com.android.server.pm.pkg.PackageStateUnserialized pkgState\nprivate @android.annotation.NonNull java.util.UUID mDomainSetId\nprivate final @android.annotation.NonNull com.android.server.utils.SnapshotCache<com.android.server.pm.PackageSetting> mSnapshot\nprivate com.android.server.utils.SnapshotCache<com.android.server.pm.PackageSetting> makeCache()\npublic com.android.server.pm.PackageSetting snapshot()\npublic void dumpDebug(android.util.proto.ProtoOutputStream,long,java.util.List<android.content.pm.UserInfo>,com.android.server.pm.permission.LegacyPermissionDataProvider)\npublic boolean isSharedUser()\npublic com.android.server.pm.PackageSetting setAppId(int)\npublic com.android.server.pm.PackageSetting setCpuAbiOverride(java.lang.String)\npublic com.android.server.pm.PackageSetting setFirstInstallTime(long)\npublic com.android.server.pm.PackageSetting setForceQueryableOverride(boolean)\npublic com.android.server.pm.PackageSetting setInstallerPackageName(java.lang.String)\npublic com.android.server.pm.PackageSetting setInstallSource(com.android.server.pm.InstallSource)\n com.android.server.pm.PackageSetting removeInstallerPackage(java.lang.String)\npublic com.android.server.pm.PackageSetting setIsOrphaned(boolean)\npublic com.android.server.pm.PackageSetting setKeySetData(com.android.server.pm.PackageKeySetData)\npublic com.android.server.pm.PackageSetting setLastModifiedTime(long)\npublic com.android.server.pm.PackageSetting setLastUpdateTime(long)\npublic com.android.server.pm.PackageSetting setLongVersionCode(long)\npublic boolean setMimeGroup(java.lang.String,java.util.List<java.lang.String>)\npublic com.android.server.pm.PackageSetting setPkg(com.android.server.pm.parsing.pkg.AndroidPackage)\npublic com.android.server.pm.PackageSetting setPrimaryCpuAbi(java.lang.String)\npublic com.android.server.pm.PackageSetting setSecondaryCpuAbi(java.lang.String)\npublic com.android.server.pm.PackageSetting setSignatures(com.android.server.pm.PackageSignatures)\npublic com.android.server.pm.PackageSetting setVolumeUuid(java.lang.String)\npublic @java.lang.Override boolean isExternalStorage()\npublic com.android.server.pm.PackageSetting setUpdateAvailable(boolean)\npublic int getSharedUserIdInt()\npublic @java.lang.Override java.lang.String toString()\nprotected void copyMimeGroups(java.util.Map<java.lang.String,java.util.Set<java.lang.String>>)\npublic void updateFrom(com.android.server.pm.PackageSetting)\n com.android.server.pm.PackageSetting updateMimeGroups(java.util.Set<java.lang.String>)\npublic @java.lang.Deprecated @java.lang.Override com.android.server.pm.permission.LegacyPermissionState getLegacyPermissionState()\npublic com.android.server.pm.PackageSetting setInstallPermissionsFixed(boolean)\npublic boolean isPrivileged()\npublic boolean isOem()\npublic boolean isVendor()\npublic boolean isProduct()\npublic @java.lang.Override boolean isRequiredForSystemUser()\npublic boolean isSystemExt()\npublic boolean isOdm()\npublic boolean isSystem()\npublic android.content.pm.SigningDetails getSigningDetails()\npublic com.android.server.pm.PackageSetting setSigningDetails(android.content.pm.SigningDetails)\npublic void copyPackageSetting(com.android.server.pm.PackageSetting)\n @com.android.internal.annotations.VisibleForTesting com.android.server.pm.pkg.PackageUserStateImpl modifyUserState(int)\npublic com.android.server.pm.pkg.PackageUserStateImpl getOrCreateUserState(int)\npublic @android.annotation.NonNull com.android.server.pm.pkg.PackageUserStateInternal readUserState(int)\n void setEnabled(int,int,java.lang.String)\n int getEnabled(int)\n java.lang.String getLastDisabledAppCaller(int)\n void setInstalled(boolean,int)\n boolean getInstalled(int)\n int getInstallReason(int)\n void setInstallReason(int,int)\n int getUninstallReason(int)\n void setUninstallReason(int,int)\n boolean setOverlayPaths(android.content.pm.overlay.OverlayPaths,int)\n @android.annotation.NonNull android.content.pm.overlay.OverlayPaths getOverlayPaths(int)\n boolean setOverlayPathsForLibrary(java.lang.String,android.content.pm.overlay.OverlayPaths,int)\n @android.annotation.NonNull java.util.Map<java.lang.String,android.content.pm.overlay.OverlayPaths> getOverlayPathsForLibrary(int)\n boolean isAnyInstalled(int[])\n int[] queryInstalledUsers(int[],boolean)\n long getCeDataInode(int)\n void setCeDataInode(long,int)\n boolean getStopped(int)\n void setStopped(boolean,int)\n boolean getNotLaunched(int)\n void setNotLaunched(boolean,int)\n boolean getHidden(int)\n void setHidden(boolean,int)\n int getDistractionFlags(int)\n void setDistractionFlags(int,int)\npublic boolean getInstantApp(int)\n void setInstantApp(boolean,int)\n boolean getVirtualPreload(int)\n void setVirtualPreload(boolean,int)\n void setUserState(int,long,int,boolean,boolean,boolean,boolean,int,android.util.ArrayMap<java.lang.String,com.android.server.pm.pkg.SuspendParams>,boolean,boolean,java.lang.String,android.util.ArraySet<java.lang.String>,android.util.ArraySet<java.lang.String>,int,int,java.lang.String,java.lang.String)\n void setUserState(int,com.android.server.pm.pkg.PackageUserStateInternal)\n android.util.ArraySet<java.lang.String> getEnabledComponents(int)\n android.util.ArraySet<java.lang.String> getDisabledComponents(int)\n void setEnabledComponents(android.util.ArraySet<java.lang.String>,int)\n void setDisabledComponents(android.util.ArraySet<java.lang.String>,int)\n void setEnabledComponentsCopy(android.util.ArraySet<java.lang.String>,int)\n void setDisabledComponentsCopy(android.util.ArraySet<java.lang.String>,int)\n com.android.server.pm.pkg.PackageUserStateImpl modifyUserStateComponents(int,boolean,boolean)\n void addDisabledComponent(java.lang.String,int)\n void addEnabledComponent(java.lang.String,int)\n boolean enableComponentLPw(java.lang.String,int)\n boolean disableComponentLPw(java.lang.String,int)\n boolean restoreComponentLPw(java.lang.String,int)\n int getCurrentEnabledStateLPr(java.lang.String,int)\n void removeUser(int)\npublic int[] getNotInstalledUserIds()\n void writePackageUserPermissionsProto(android.util.proto.ProtoOutputStream,long,java.util.List<android.content.pm.UserInfo>,com.android.server.pm.permission.LegacyPermissionDataProvider)\nprotected void writeUsersInfoToProto(android.util.proto.ProtoOutputStream,long)\n void setHarmfulAppWarning(int,java.lang.String)\n java.lang.String getHarmfulAppWarning(int)\n com.android.server.pm.PackageSetting setPath(java.io.File)\npublic @com.android.internal.annotations.VisibleForTesting boolean overrideNonLocalizedLabelAndIcon(android.content.ComponentName,java.lang.String,java.lang.Integer,int)\npublic void resetOverrideComponentLabelIcon(int)\npublic void setSplashScreenTheme(int,java.lang.String)\npublic @android.annotation.Nullable java.lang.String getSplashScreenTheme(int)\npublic boolean isLoading()\npublic com.android.server.pm.PackageSetting setLoadingProgress(float)\npublic @android.annotation.NonNull @java.lang.Override long getVersionCode()\npublic @android.annotation.Nullable @java.lang.Override java.util.Map<java.lang.String,java.util.Set<java.lang.String>> getMimeGroups()\npublic @android.annotation.NonNull @java.lang.Override java.lang.String getPackageName()\npublic @android.annotation.Nullable @java.lang.Override com.android.server.pm.pkg.AndroidPackageApi getAndroidPackage()\npublic @android.annotation.Nullable @java.lang.Override java.lang.Integer getSharedUserId()\npublic @android.annotation.NonNull android.content.pm.SigningInfo getSigningInfo()\npublic @android.annotation.NonNull @java.lang.Override java.lang.String[] getUsesStaticLibraries()\npublic @android.annotation.NonNull @java.lang.Override long[] getUsesStaticLibrariesVersions()\npublic @android.annotation.NonNull @java.lang.Override java.util.List<android.content.pm.SharedLibraryInfo> getUsesLibraryInfos()\npublic @android.annotation.NonNull @java.lang.Override java.util.List<java.lang.String> getUsesLibraryFiles()\npublic @java.lang.Override boolean isHiddenUntilInstalled()\npublic @android.annotation.NonNull @java.lang.Override long[] getLastPackageUsageTime()\npublic @java.lang.Override boolean isUpdatedSystemApp()\npublic com.android.server.pm.PackageSetting setDomainSetId(java.util.UUID)\npublic com.android.server.pm.PackageSetting setSharedUser(com.android.server.pm.SharedUserSetting)\npublic com.android.server.pm.PackageSetting setCategoryOverride(int)\npublic com.android.server.pm.PackageSetting setLegacyNativeLibraryPath(java.lang.String)\npublic com.android.server.pm.PackageSetting setMimeGroups(java.util.Map<java.lang.String,java.util.Set<java.lang.String>>)\npublic com.android.server.pm.PackageSetting setOldCodePaths(java.util.Set<java.lang.String>)\npublic com.android.server.pm.PackageSetting setUsesStaticLibraries(java.lang.String[])\npublic com.android.server.pm.PackageSetting setUsesStaticLibrariesVersions(long[])\npublic @android.annotation.NonNull @java.lang.Override com.android.server.pm.pkg.PackageStateUnserialized getTransientState()\npublic @android.annotation.NonNull android.util.SparseArray<? extends PackageUserStateInternal> getUserStates()\nclass PackageSetting extends com.android.server.pm.SettingBase implements [com.android.server.pm.pkg.PackageStateInternal]\n@com.android.internal.util.DataClass(genGetters=true, genConstructor=false, genSetters=false, genBuilder=false)")
@Deprecated
private void __metadata() {}
diff --git a/services/core/java/com/android/server/pm/RemovePackageHelper.java b/services/core/java/com/android/server/pm/RemovePackageHelper.java
index 749495c..d60d019 100644
--- a/services/core/java/com/android/server/pm/RemovePackageHelper.java
+++ b/services/core/java/com/android/server/pm/RemovePackageHelper.java
@@ -29,10 +29,7 @@
import android.annotation.NonNull;
import android.content.pm.PackageManager;
-import android.content.pm.SharedLibraryInfo;
-import android.content.pm.VersionedPackage;
import android.content.pm.parsing.component.ParsedInstrumentation;
-import android.os.Process;
import android.os.UserHandle;
import android.os.incremental.IncrementalManager;
import android.util.Log;
@@ -46,7 +43,6 @@
import com.android.server.pm.parsing.pkg.PackageImpl;
import com.android.server.pm.permission.PermissionManagerServiceInternal;
import com.android.server.pm.pkg.PackageStateInternal;
-import com.android.server.utils.WatchedLongSparseArray;
import java.io.File;
import java.util.Collections;
@@ -62,6 +58,7 @@
private final Installer mInstaller;
private final UserManagerInternal mUserManagerInternal;
private final PermissionManagerServiceInternal mPermissionManager;
+ private final SharedLibrariesImpl mSharedLibraries;
private final AppDataHelper mAppDataHelper;
// TODO(b/198166813): remove PMS dependency
@@ -71,6 +68,7 @@
mInstaller = mPm.mInjector.getInstaller();
mUserManagerInternal = mPm.mInjector.getUserManagerInternal();
mPermissionManager = mPm.mInjector.getPermissionManagerServiceInternal();
+ mSharedLibraries = mPm.mInjector.getSharedLibrariesImpl();
mAppDataHelper = appDataHelper;
}
@@ -174,7 +172,7 @@
final int libraryNamesSize = pkg.getLibraryNames().size();
for (i = 0; i < libraryNamesSize; i++) {
String name = pkg.getLibraryNames().get(i);
- if (removeSharedLibraryLPw(name, 0)) {
+ if (mSharedLibraries.removeSharedLibraryLPw(name, 0)) {
if (DEBUG_REMOVE && chatty) {
if (r == null) {
r = new StringBuilder(256);
@@ -191,7 +189,8 @@
// Any package can hold SDK or static shared libraries.
if (pkg.getSdkLibName() != null) {
- if (removeSharedLibraryLPw(pkg.getSdkLibName(), pkg.getSdkLibVersionMajor())) {
+ if (mSharedLibraries.removeSharedLibraryLPw(
+ pkg.getSdkLibName(), pkg.getSdkLibVersionMajor())) {
if (DEBUG_REMOVE && chatty) {
if (r == null) {
r = new StringBuilder(256);
@@ -203,7 +202,7 @@
}
}
if (pkg.getStaticSharedLibName() != null) {
- if (removeSharedLibraryLPw(pkg.getStaticSharedLibName(),
+ if (mSharedLibraries.removeSharedLibraryLPw(pkg.getStaticSharedLibName(),
pkg.getStaticSharedLibVersion())) {
if (DEBUG_REMOVE && chatty) {
if (r == null) {
@@ -221,44 +220,6 @@
}
}
- private boolean removeSharedLibraryLPw(String name, long version) {
- WatchedLongSparseArray<SharedLibraryInfo> versionedLib = mPm.mSharedLibraries.get(name);
- if (versionedLib == null) {
- return false;
- }
- final int libIdx = versionedLib.indexOfKey(version);
- if (libIdx < 0) {
- return false;
- }
- SharedLibraryInfo libraryInfo = versionedLib.valueAt(libIdx);
-
- // Remove the shared library overlays from its dependent packages.
- for (int currentUserId : UserManagerService.getInstance().getUserIds()) {
- final List<VersionedPackage> dependents = mPm.getPackagesUsingSharedLibrary(
- libraryInfo, 0, Process.SYSTEM_UID, currentUserId);
- if (dependents == null) {
- continue;
- }
- for (VersionedPackage dependentPackage : dependents) {
- final PackageSetting ps = mPm.mSettings.getPackageLPr(
- dependentPackage.getPackageName());
- if (ps != null) {
- ps.setOverlayPathsForLibrary(libraryInfo.getName(), null, currentUserId);
- }
- }
- }
-
- versionedLib.remove(version);
- if (versionedLib.size() <= 0) {
- mPm.mSharedLibraries.remove(name);
- if (libraryInfo.getType() == SharedLibraryInfo.TYPE_STATIC) {
- mPm.mStaticLibsByDeclaringPackage.remove(libraryInfo.getDeclaringPackage()
- .getPackageName());
- }
- }
- return true;
- }
-
/*
* This method deletes the package from internal data structures. If the DELETE_KEEP_DATA
* flag is not set, the data directory is removed as well.
diff --git a/services/core/java/com/android/server/pm/SettingBase.java b/services/core/java/com/android/server/pm/SettingBase.java
index ed85ff9..4345d51 100644
--- a/services/core/java/com/android/server/pm/SettingBase.java
+++ b/services/core/java/com/android/server/pm/SettingBase.java
@@ -22,6 +22,7 @@
import com.android.internal.annotations.VisibleForTesting;
import com.android.server.pm.permission.LegacyPermissionState;
+import com.android.server.pm.pkg.mutate.PackageStateMutator;
import com.android.server.utils.Snappable;
import com.android.server.utils.Watchable;
import com.android.server.utils.WatchableImpl;
@@ -88,6 +89,7 @@
* Notify listeners that this object has changed.
*/
protected void onChanged() {
+ PackageStateMutator.onPackageStateChanged();
dispatchChange(this);
}
@@ -122,7 +124,7 @@
return mLegacyPermissionsState;
}
- SettingBase setFlags(int pkgFlags) {
+ public SettingBase setFlags(int pkgFlags) {
this.mPkgFlags = pkgFlags
& (ApplicationInfo.FLAG_SYSTEM
| ApplicationInfo.FLAG_EXTERNAL_STORAGE
@@ -131,7 +133,7 @@
return this;
}
- SettingBase setPrivateFlags(int pkgPrivateFlags) {
+ public SettingBase setPrivateFlags(int pkgPrivateFlags) {
this.mPkgPrivateFlags = pkgPrivateFlags
& (ApplicationInfo.PRIVATE_FLAG_PRIVILEGED
| ApplicationInfo.PRIVATE_FLAG_OEM
diff --git a/services/core/java/com/android/server/pm/Settings.java b/services/core/java/com/android/server/pm/Settings.java
index b1ce6a2..6c47eb0 100644
--- a/services/core/java/com/android/server/pm/Settings.java
+++ b/services/core/java/com/android/server/pm/Settings.java
@@ -114,6 +114,7 @@
import com.android.server.pm.permission.LegacyPermissionSettings;
import com.android.server.pm.permission.LegacyPermissionState;
import com.android.server.pm.permission.LegacyPermissionState.PermissionState;
+import com.android.server.pm.pkg.PackageStateInternal;
import com.android.server.pm.pkg.PackageUserState;
import com.android.server.pm.pkg.PackageUserStateInternal;
import com.android.server.pm.pkg.SuspendParams;
@@ -162,6 +163,7 @@
import java.util.Objects;
import java.util.Set;
import java.util.UUID;
+import java.util.function.Consumer;
/**
* Holds information about dynamic settings.
@@ -257,7 +259,7 @@
public static final int SIGNATURE_MALFORMED_RECOVER = 3;
}
- private static final boolean DEBUG_STOPPED = false;
+ static final boolean DEBUG_STOPPED = false;
private static final boolean DEBUG_MU = false;
private static final boolean DEBUG_KERNEL = false;
private static final boolean DEBUG_PARSER = false;
@@ -625,7 +627,15 @@
mOtherAppIds = new WatchedSparseArray<>();
mPermissions = new LegacyPermissionSettings(lock);
mRuntimePermissionsPersistence = new RuntimePermissionPersistence(
- runtimePermissionsPersistence);
+ runtimePermissionsPersistence, new Consumer<Integer>() {
+ @Override
+ public void accept(Integer userId) {
+ synchronized (mLock) {
+ mRuntimePermissionsPersistence.writeStateForUserSync(userId,
+ mPermissionDataProvider, mPackages, mSharedUsers);
+ }
+ }
+ });
mPermissionDataProvider = permissionDataProvider;
mSystemDir = new File(dataDir, "system");
@@ -989,7 +999,6 @@
true /*notLaunched*/,
false /*hidden*/,
0 /*distractionFlags*/,
- false /*suspended*/,
null /*suspendParams*/,
instantApp,
virtualPreload,
@@ -1423,7 +1432,7 @@
void writeAllRuntimePermissionsLPr() {
for (int userId : UserManagerService.getInstance().getUserIds()) {
- mRuntimePermissionsPersistence.writeStateForUserAsyncLPr(userId);
+ mRuntimePermissionsPersistence.writeStateForUserAsync(userId);
}
}
@@ -1432,15 +1441,15 @@
}
void updateRuntimePermissionsFingerprintLPr(@UserIdInt int userId) {
- mRuntimePermissionsPersistence.updateRuntimePermissionsFingerprintLPr(userId);
+ mRuntimePermissionsPersistence.updateRuntimePermissionsFingerprint(userId);
}
int getDefaultRuntimePermissionsVersionLPr(int userId) {
- return mRuntimePermissionsPersistence.getVersionLPr(userId);
+ return mRuntimePermissionsPersistence.getVersion(userId);
}
void setDefaultRuntimePermissionsVersionLPr(int version, int userId) {
- mRuntimePermissionsPersistence.setVersionLPr(version, userId);
+ mRuntimePermissionsPersistence.setVersion(version, userId);
}
void setPermissionControllerVersion(long version) {
@@ -1646,7 +1655,6 @@
false /*notLaunched*/,
false /*hidden*/,
0 /*distractionFlags*/,
- false /*suspended*/,
null /*suspendParams*/,
false /*instantApp*/,
false /*virtualPreload*/,
@@ -1816,10 +1824,9 @@
setBlockUninstallLPw(userId, name, true);
}
ps.setUserState(userId, ceDataInode, enabled, installed, stopped, notLaunched,
- hidden, distractionFlags, suspended, suspendParamsMap,
- instantApp, virtualPreload, enabledCaller, enabledComponents,
- disabledComponents, installReason, uninstallReason, harmfulAppWarning,
- splashScreenTheme);
+ hidden, distractionFlags, suspendParamsMap, instantApp, virtualPreload,
+ enabledCaller, enabledComponents, disabledComponents, installReason,
+ uninstallReason, harmfulAppWarning, splashScreenTheme);
mDomainVerificationManager.setLegacyUserState(name, userId, verifState);
} else if (tagName.equals("preferred-activities")) {
@@ -3100,7 +3107,8 @@
}
for (UserInfo user : users) {
- mRuntimePermissionsPersistence.readStateForUserSyncLPr(user.id);
+ mRuntimePermissionsPersistence.readStateForUserSync(user.id, getInternalVersion(),
+ mPackages, mSharedUsers, getUserRuntimePermissionsFile(user.id));
}
/*
@@ -3124,7 +3132,8 @@
}
void readPermissionStateForUserSyncLPr(@UserIdInt int userId) {
- mRuntimePermissionsPersistence.readStateForUserSyncLPr(userId);
+ mRuntimePermissionsPersistence.readStateForUserSync(userId, getInternalVersion(),
+ mPackages, mSharedUsers, getUserRuntimePermissionsFile(userId));
}
void applyDefaultPreferredAppsLPw(int userId) {
@@ -4120,7 +4129,7 @@
file.delete();
removeCrossProfileIntentFiltersLPw(userId);
- mRuntimePermissionsPersistence.onUserRemovedLPw(userId);
+ mRuntimePermissionsPersistence.onUserRemoved(userId);
mDomainVerificationManager.clearUser(userId);
writePackageListLPr();
@@ -4485,24 +4494,25 @@
}
}
for (UserInfo user : users) {
+ final PackageUserStateInternal userState = ps.getUserStateOrDefault(user.id);
pw.print(checkinTag);
pw.print("-");
pw.print("usr");
pw.print(",");
pw.print(user.id);
pw.print(",");
- pw.print(ps.getInstalled(user.id) ? "I" : "i");
- pw.print(ps.getHidden(user.id) ? "B" : "b");
- pw.print(ps.getSuspended(user.id) ? "SU" : "su");
- pw.print(ps.getStopped(user.id) ? "S" : "s");
- pw.print(ps.getNotLaunched(user.id) ? "l" : "L");
- pw.print(ps.getInstantApp(user.id) ? "IA" : "ia");
- pw.print(ps.getVirtualPreload(user.id) ? "VPI" : "vpi");
- String harmfulAppWarning = ps.getHarmfulAppWarning(user.id);
+ pw.print(userState.isInstalled() ? "I" : "i");
+ pw.print(userState.isHidden() ? "B" : "b");
+ pw.print(userState.isSuspended() ? "SU" : "su");
+ pw.print(userState.isStopped() ? "S" : "s");
+ pw.print(userState.isNotLaunched() ? "l" : "L");
+ pw.print(userState.isInstantApp() ? "IA" : "ia");
+ pw.print(userState.isVirtualPreload() ? "VPI" : "vpi");
+ String harmfulAppWarning = userState.getHarmfulAppWarning();
pw.print(harmfulAppWarning != null ? "HA" : "ha");
pw.print(",");
- pw.print(ps.getEnabled(user.id));
- String lastDisabledAppCaller = ps.getLastDisabledAppCaller(user.id);
+ pw.print(userState.getEnabledState());
+ String lastDisabledAppCaller = userState.getLastDisableAppCaller();
pw.print(",");
pw.print(lastDisabledAppCaller != null ? lastDisabledAppCaller : "?");
pw.print(",");
@@ -4819,48 +4829,48 @@
}
for (UserInfo user : users) {
+ final PackageUserStateInternal userState = ps.getUserStateOrDefault(user.id);
pw.print(prefix); pw.print(" User "); pw.print(user.id); pw.print(": ");
pw.print("ceDataInode=");
- pw.print(ps.getCeDataInode(user.id));
+ pw.print(userState.getCeDataInode());
pw.print(" installed=");
- pw.print(ps.getInstalled(user.id));
+ pw.print(userState.isInstalled());
pw.print(" hidden=");
- pw.print(ps.getHidden(user.id));
+ pw.print(userState.isHidden());
pw.print(" suspended=");
- pw.print(ps.getSuspended(user.id));
+ pw.print(userState.isSuspended());
pw.print(" distractionFlags=");
- pw.print(ps.getDistractionFlags(user.id));
+ pw.print(userState.getDistractionFlags());
pw.print(" stopped=");
- pw.print(ps.getStopped(user.id));
+ pw.print(userState.isStopped());
pw.print(" notLaunched=");
- pw.print(ps.getNotLaunched(user.id));
+ pw.print(userState.isNotLaunched());
pw.print(" enabled=");
- pw.print(ps.getEnabled(user.id));
+ pw.print(userState.getEnabledState());
pw.print(" instant=");
- pw.print(ps.getInstantApp(user.id));
+ pw.print(userState.isInstantApp());
pw.print(" virtual=");
- pw.print(ps.getVirtualPreload(user.id));
+ pw.println(userState.isVirtualPreload());
pw.print(" installReason=");
- pw.println(ps.getInstallReason(user.id));
+ pw.println(userState.getInstallReason());
- if (ps.getSuspended(user.id)) {
+ if (userState.isSuspended()) {
pw.print(prefix);
pw.println(" Suspend params:");
- final PackageUserStateInternal pus = ps.readUserState(user.id);
- for (int i = 0; i < pus.getSuspendParams().size(); i++) {
+ for (int i = 0; i < userState.getSuspendParams().size(); i++) {
pw.print(prefix);
pw.print(" suspendingPackage=");
- pw.print(pus.getSuspendParams().keyAt(i));
- final SuspendParams params = pus.getSuspendParams().valueAt(i);
+ pw.print(userState.getSuspendParams().keyAt(i));
+ final SuspendParams params = userState.getSuspendParams().valueAt(i);
if (params != null) {
pw.print(" dialogInfo=");
- pw.print(params.dialogInfo);
+ pw.print(params.getDialogInfo());
}
pw.println();
}
}
- final OverlayPaths overlayPaths = ps.getOverlayPaths(user.id);
+ final OverlayPaths overlayPaths = userState.getOverlayPaths();
if (overlayPaths != null) {
if (!overlayPaths.getOverlayPaths().isEmpty()) {
pw.print(prefix);
@@ -4883,7 +4893,7 @@
}
final Map<String, OverlayPaths> sharedLibraryOverlayPaths =
- ps.getOverlayPathsForLibrary(user.id);
+ userState.getSharedLibraryOverlayPaths();
if (sharedLibraryOverlayPaths != null) {
for (Map.Entry<String, OverlayPaths> libOverlayPaths :
sharedLibraryOverlayPaths.entrySet()) {
@@ -4916,7 +4926,7 @@
}
}
- String lastDisabledAppCaller = ps.getLastDisabledAppCaller(user.id);
+ String lastDisabledAppCaller = userState.getLastDisableAppCaller();
if (lastDisabledAppCaller != null) {
pw.print(prefix); pw.print(" lastDisabledCaller: ");
pw.println(lastDisabledAppCaller);
@@ -4929,21 +4939,21 @@
.getPermissionStates(user.id), dumpAll);
}
- String harmfulAppWarning = ps.getHarmfulAppWarning(user.id);
+ String harmfulAppWarning = userState.getHarmfulAppWarning();
if (harmfulAppWarning != null) {
pw.print(prefix); pw.print(" harmfulAppWarning: ");
pw.println(harmfulAppWarning);
}
if (permissionNames == null) {
- ArraySet<String> cmp = ps.getDisabledComponents(user.id);
+ Set<String> cmp = userState.getDisabledComponents();
if (cmp != null && cmp.size() > 0) {
pw.print(prefix); pw.println(" disabledComponents:");
for (String s : cmp) {
pw.print(prefix); pw.print(" "); pw.println(s);
}
}
- cmp = ps.getEnabledComponents(user.id);
+ cmp = userState.getEnabledComponents();
if (cmp != null && cmp.size() > 0) {
pw.print(prefix); pw.println(" enabledComponents:");
for (String s : cmp) {
@@ -5291,9 +5301,10 @@
public void writePermissionStateForUserLPr(int userId, boolean sync) {
if (sync) {
- mRuntimePermissionsPersistence.writeStateForUserSyncLPr(userId);
+ mRuntimePermissionsPersistence.writeStateForUserSync(userId, mPermissionDataProvider,
+ mPackages, mSharedUsers);
} else {
- mRuntimePermissionsPersistence.writeStateForUserAsyncLPr(userId);
+ mRuntimePermissionsPersistence.writeStateForUserAsync(userId);
}
}
@@ -5368,7 +5379,7 @@
}
}
- private final class RuntimePermissionPersistence {
+ private static final class RuntimePermissionPersistence {
private static final long WRITE_PERMISSIONS_DELAY_MILLIS = 200;
private static final long MAX_WRITE_PERMISSIONS_DELAY_MILLIS = 2000;
@@ -5381,6 +5392,8 @@
private final Handler mHandler = new MyHandler();
+ private final Object mLock = new Object();
+
@GuardedBy("mLock")
private final SparseBooleanArray mWriteScheduled = new SparseBooleanArray();
@@ -5400,45 +5413,58 @@
// The mapping keys are user ids.
private final SparseBooleanArray mPermissionUpgradeNeeded = new SparseBooleanArray();
- public RuntimePermissionPersistence(RuntimePermissionsPersistence persistence) {
+ // This is a hack to allow this class to invoke a write using Settings's data structures,
+ // to facilitate moving to a finer scoped lock without a significant refactor.
+ private final Consumer<Integer> mInvokeWriteUserStateAsyncCallback;
+
+ public RuntimePermissionPersistence(RuntimePermissionsPersistence persistence,
+ Consumer<Integer> invokeWriteUserStateAsyncCallback) {
mPersistence = persistence;
+ mInvokeWriteUserStateAsyncCallback = invokeWriteUserStateAsyncCallback;
}
- @GuardedBy("Settings.this.mLock")
- int getVersionLPr(int userId) {
- return mVersions.get(userId, INITIAL_VERSION);
- }
-
- @GuardedBy("Settings.this.mLock")
- void setVersionLPr(int version, int userId) {
- mVersions.put(userId, version);
- writeStateForUserAsyncLPr(userId);
- }
-
- @GuardedBy("Settings.this.mLock")
- public boolean isPermissionUpgradeNeeded(int userId) {
- return mPermissionUpgradeNeeded.get(userId, true);
- }
-
- @GuardedBy("Settings.this.mLock")
- public void updateRuntimePermissionsFingerprintLPr(@UserIdInt int userId) {
- if (mExtendedFingerprint == null) {
- throw new RuntimeException("The version of the permission controller hasn't been "
- + "set before trying to update the fingerprint.");
+ int getVersion(int userId) {
+ synchronized (mLock) {
+ return mVersions.get(userId, INITIAL_VERSION);
}
- mFingerprints.put(userId, mExtendedFingerprint);
- writeStateForUserAsyncLPr(userId);
+ }
+
+ void setVersion(int version, int userId) {
+ synchronized (mLock) {
+ mVersions.put(userId, version);
+ writeStateForUserAsync(userId);
+ }
+ }
+
+ public boolean isPermissionUpgradeNeeded(int userId) {
+ synchronized (mLock) {
+ return mPermissionUpgradeNeeded.get(userId, true);
+ }
+ }
+
+ public void updateRuntimePermissionsFingerprint(@UserIdInt int userId) {
+ synchronized (mLock) {
+ if (mExtendedFingerprint == null) {
+ throw new RuntimeException(
+ "The version of the permission controller hasn't been "
+ + "set before trying to update the fingerprint.");
+ }
+ mFingerprints.put(userId, mExtendedFingerprint);
+ writeStateForUserAsync(userId);
+ }
}
public void setPermissionControllerVersion(long version) {
- int numUser = mFingerprints.size();
- mExtendedFingerprint = getExtendedFingerprint(version);
+ synchronized (mLock) {
+ int numUser = mFingerprints.size();
+ mExtendedFingerprint = getExtendedFingerprint(version);
- for (int i = 0; i < numUser; i++) {
- int userId = mFingerprints.keyAt(i);
- String fingerprint = mFingerprints.valueAt(i);
- mPermissionUpgradeNeeded.put(userId,
- !TextUtils.equals(mExtendedFingerprint, fingerprint));
+ for (int i = 0; i < numUser; i++) {
+ int userId = mFingerprints.keyAt(i);
+ String fingerprint = mFingerprints.valueAt(i);
+ mPermissionUpgradeNeeded.put(userId,
+ !TextUtils.equals(mExtendedFingerprint, fingerprint));
+ }
}
}
@@ -5446,84 +5472,92 @@
return PackagePartitions.FINGERPRINT + "?pc_version=" + version;
}
- public void writeStateForUserAsyncLPr(int userId) {
- final long currentTimeMillis = SystemClock.uptimeMillis();
+ public void writeStateForUserAsync(int userId) {
+ synchronized (mLock) {
+ final long currentTimeMillis = SystemClock.uptimeMillis();
- if (mWriteScheduled.get(userId)) {
- mHandler.removeMessages(userId);
+ if (mWriteScheduled.get(userId)) {
+ mHandler.removeMessages(userId);
- // If enough time passed, write without holding off anymore.
- final long lastNotWrittenMutationTimeMillis = mLastNotWrittenMutationTimesMillis
- .get(userId);
- final long timeSinceLastNotWrittenMutationMillis = currentTimeMillis
- - lastNotWrittenMutationTimeMillis;
- if (timeSinceLastNotWrittenMutationMillis >= MAX_WRITE_PERMISSIONS_DELAY_MILLIS) {
- mHandler.obtainMessage(userId).sendToTarget();
- return;
+ // If enough time passed, write without holding off anymore.
+ final long lastNotWrittenMutationTimeMillis = mLastNotWrittenMutationTimesMillis
+ .get(userId);
+ final long timeSinceLastNotWrittenMutationMillis = currentTimeMillis
+ - lastNotWrittenMutationTimeMillis;
+ if (timeSinceLastNotWrittenMutationMillis
+ >= MAX_WRITE_PERMISSIONS_DELAY_MILLIS) {
+ mHandler.obtainMessage(userId).sendToTarget();
+ return;
+ }
+
+ // Hold off a bit more as settings are frequently changing.
+ final long maxDelayMillis = Math.max(lastNotWrittenMutationTimeMillis
+ + MAX_WRITE_PERMISSIONS_DELAY_MILLIS - currentTimeMillis, 0);
+ final long writeDelayMillis = Math.min(WRITE_PERMISSIONS_DELAY_MILLIS,
+ maxDelayMillis);
+
+ Message message = mHandler.obtainMessage(userId);
+ mHandler.sendMessageDelayed(message, writeDelayMillis);
+ } else {
+ mLastNotWrittenMutationTimesMillis.put(userId, currentTimeMillis);
+ Message message = mHandler.obtainMessage(userId);
+ mHandler.sendMessageDelayed(message, WRITE_PERMISSIONS_DELAY_MILLIS);
+ mWriteScheduled.put(userId, true);
}
-
- // Hold off a bit more as settings are frequently changing.
- final long maxDelayMillis = Math.max(lastNotWrittenMutationTimeMillis
- + MAX_WRITE_PERMISSIONS_DELAY_MILLIS - currentTimeMillis, 0);
- final long writeDelayMillis = Math.min(WRITE_PERMISSIONS_DELAY_MILLIS,
- maxDelayMillis);
-
- Message message = mHandler.obtainMessage(userId);
- mHandler.sendMessageDelayed(message, writeDelayMillis);
- } else {
- mLastNotWrittenMutationTimesMillis.put(userId, currentTimeMillis);
- Message message = mHandler.obtainMessage(userId);
- mHandler.sendMessageDelayed(message, WRITE_PERMISSIONS_DELAY_MILLIS);
- mWriteScheduled.put(userId, true);
}
}
- public void writeStateForUserSyncLPr(int userId) {
- mHandler.removeMessages(userId);
- mWriteScheduled.delete(userId);
+ public void writeStateForUserSync(int userId, @NonNull LegacyPermissionDataProvider
+ legacyPermissionDataProvider,
+ @NonNull WatchedArrayMap<String, ? extends PackageStateInternal> packageStates,
+ @NonNull WatchedArrayMap<String, SharedUserSetting> sharedUsers) {
+ synchronized (mLock) {
+ mHandler.removeMessages(userId);
+ mWriteScheduled.delete(userId);
- mPermissionDataProvider.writeLegacyPermissionStateTEMP();
+ legacyPermissionDataProvider.writeLegacyPermissionStateTEMP();
- int version = mVersions.get(userId, INITIAL_VERSION);
+ int version = mVersions.get(userId, INITIAL_VERSION);
- String fingerprint = mFingerprints.get(userId);
+ String fingerprint = mFingerprints.get(userId);
- Map<String, List<RuntimePermissionsState.PermissionState>> packagePermissions =
- new ArrayMap<>();
- int packagesSize = mPackages.size();
- for (int i = 0; i < packagesSize; i++) {
- String packageName = mPackages.keyAt(i);
- PackageSetting packageSetting = mPackages.valueAt(i);
- if (packageSetting.getSharedUser() == null) {
+ Map<String, List<RuntimePermissionsState.PermissionState>> packagePermissions =
+ new ArrayMap<>();
+ int packagesSize = packageStates.size();
+ for (int i = 0; i < packagesSize; i++) {
+ String packageName = packageStates.keyAt(i);
+ PackageStateInternal packageState = packageStates.valueAt(i);
+ if (packageState.getSharedUser() == null) {
+ List<RuntimePermissionsState.PermissionState> permissions =
+ getPermissionsFromPermissionsState(
+ packageState.getLegacyPermissionState(), userId);
+ if (permissions.isEmpty() && !packageState.isInstallPermissionsFixed()) {
+ // Storing an empty state means the package is known to the system and
+ // its install permissions have been granted and fixed. If this is not
+ // the case, we should not store anything.
+ continue;
+ }
+ packagePermissions.put(packageName, permissions);
+ }
+ }
+
+ Map<String, List<RuntimePermissionsState.PermissionState>> sharedUserPermissions =
+ new ArrayMap<>();
+ final int sharedUsersSize = sharedUsers.size();
+ for (int i = 0; i < sharedUsersSize; i++) {
+ String sharedUserName = sharedUsers.keyAt(i);
+ SharedUserSetting sharedUserSetting = sharedUsers.valueAt(i);
List<RuntimePermissionsState.PermissionState> permissions =
getPermissionsFromPermissionsState(
- packageSetting.getLegacyPermissionState(), userId);
- if (permissions.isEmpty() && !packageSetting.isInstallPermissionsFixed()) {
- // Storing an empty state means the package is known to the system and its
- // install permissions have been granted and fixed. If this is not the case,
- // we should not store anything.
- continue;
- }
- packagePermissions.put(packageName, permissions);
+ sharedUserSetting.getLegacyPermissionState(), userId);
+ sharedUserPermissions.put(sharedUserName, permissions);
}
+
+ RuntimePermissionsState runtimePermissions = new RuntimePermissionsState(version,
+ fingerprint, packagePermissions, sharedUserPermissions);
+
+ mPersistence.writeForUser(runtimePermissions, UserHandle.of(userId));
}
-
- Map<String, List<RuntimePermissionsState.PermissionState>> sharedUserPermissions =
- new ArrayMap<>();
- final int sharedUsersSize = mSharedUsers.size();
- for (int i = 0; i < sharedUsersSize; i++) {
- String sharedUserName = mSharedUsers.keyAt(i);
- SharedUserSetting sharedUserSetting = mSharedUsers.valueAt(i);
- List<RuntimePermissionsState.PermissionState> permissions =
- getPermissionsFromPermissionsState(
- sharedUserSetting.getLegacyPermissionState(), userId);
- sharedUserPermissions.put(sharedUserName, permissions);
- }
-
- RuntimePermissionsState runtimePermissions = new RuntimePermissionsState(version,
- fingerprint, packagePermissions, sharedUserPermissions);
-
- mPersistence.writeForUser(runtimePermissions, UserHandle.of(userId));
}
@NonNull
@@ -5541,82 +5575,91 @@
return permissions;
}
- @GuardedBy("Settings.this.mLock")
- private void onUserRemovedLPw(int userId) {
- // Make sure we do not
- mHandler.removeMessages(userId);
+ private void onUserRemoved(int userId) {
+ synchronized (mLock) {
+ // Make sure we do not
+ mHandler.removeMessages(userId);
- mPermissionUpgradeNeeded.delete(userId);
- mVersions.delete(userId);
- mFingerprints.remove(userId);
+ mPermissionUpgradeNeeded.delete(userId);
+ mVersions.delete(userId);
+ mFingerprints.remove(userId);
+ }
}
public void deleteUserRuntimePermissionsFile(int userId) {
- mPersistence.deleteForUser(UserHandle.of(userId));
+ synchronized (mLock) {
+ mPersistence.deleteForUser(UserHandle.of(userId));
+ }
}
- @GuardedBy("Settings.this.mLock")
- public void readStateForUserSyncLPr(int userId) {
- RuntimePermissionsState runtimePermissions = mPersistence.readForUser(UserHandle.of(
- userId));
- if (runtimePermissions == null) {
- readLegacyStateForUserSyncLPr(userId);
- writeStateForUserAsyncLPr(userId);
- return;
- }
-
- // If the runtime permissions file exists but the version is not set this is
- // an upgrade from P->Q. Hence mark it with the special UPGRADE_VERSION.
- int version = runtimePermissions.getVersion();
- if (version == RuntimePermissionsState.NO_VERSION) {
- version = UPGRADE_VERSION;
- }
- mVersions.put(userId, version);
-
- String fingerprint = runtimePermissions.getFingerprint();
- mFingerprints.put(userId, fingerprint);
-
- boolean isUpgradeToR = getInternalVersion().sdkVersion < Build.VERSION_CODES.R;
-
- Map<String, List<RuntimePermissionsState.PermissionState>> packagePermissions =
- runtimePermissions.getPackagePermissions();
- int packagesSize = mPackages.size();
- for (int i = 0; i < packagesSize; i++) {
- String packageName = mPackages.keyAt(i);
- PackageSetting packageSetting = mPackages.valueAt(i);
-
- List<RuntimePermissionsState.PermissionState> permissions =
- packagePermissions.get(packageName);
- if (permissions != null) {
- readPermissionsStateLpr(permissions, packageSetting.getLegacyPermissionState(),
- userId);
- packageSetting.setInstallPermissionsFixed(true);
- } else if (packageSetting.getSharedUser() == null && !isUpgradeToR) {
- Slog.w(TAG, "Missing permission state for package: " + packageName);
- packageSetting.getLegacyPermissionState().setMissing(true, userId);
+ public void readStateForUserSync(int userId, @NonNull VersionInfo internalVersion,
+ @NonNull WatchedArrayMap<String, PackageSetting> packageSettings,
+ @NonNull WatchedArrayMap<String, SharedUserSetting> sharedUsers,
+ @NonNull File userRuntimePermissionsFile) {
+ synchronized (mLock) {
+ RuntimePermissionsState runtimePermissions = mPersistence.readForUser(UserHandle.of(
+ userId));
+ if (runtimePermissions == null) {
+ readLegacyStateForUserSync(userId, userRuntimePermissionsFile, packageSettings,
+ sharedUsers);
+ writeStateForUserAsync(userId);
+ return;
}
- }
- Map<String, List<RuntimePermissionsState.PermissionState>> sharedUserPermissions =
- runtimePermissions.getSharedUserPermissions();
- int sharedUsersSize = mSharedUsers.size();
- for (int i = 0; i < sharedUsersSize; i++) {
- String sharedUserName = mSharedUsers.keyAt(i);
- SharedUserSetting sharedUserSetting = mSharedUsers.valueAt(i);
+ // If the runtime permissions file exists but the version is not set this is
+ // an upgrade from P->Q. Hence mark it with the special UPGRADE_VERSION.
+ int version = runtimePermissions.getVersion();
+ if (version == RuntimePermissionsState.NO_VERSION) {
+ version = UPGRADE_VERSION;
+ }
+ mVersions.put(userId, version);
- List<RuntimePermissionsState.PermissionState> permissions =
- sharedUserPermissions.get(sharedUserName);
- if (permissions != null) {
- readPermissionsStateLpr(permissions,
- sharedUserSetting.getLegacyPermissionState(), userId);
- } else if (!isUpgradeToR) {
- Slog.w(TAG, "Missing permission state for shared user: " + sharedUserName);
- sharedUserSetting.getLegacyPermissionState().setMissing(true, userId);
+ String fingerprint = runtimePermissions.getFingerprint();
+ mFingerprints.put(userId, fingerprint);
+
+ boolean isUpgradeToR = internalVersion.sdkVersion < Build.VERSION_CODES.R;
+
+ Map<String, List<RuntimePermissionsState.PermissionState>> packagePermissions =
+ runtimePermissions.getPackagePermissions();
+ int packagesSize = packageSettings.size();
+ for (int i = 0; i < packagesSize; i++) {
+ String packageName = packageSettings.keyAt(i);
+ PackageSetting packageSetting = packageSettings.valueAt(i);
+
+ List<RuntimePermissionsState.PermissionState> permissions =
+ packagePermissions.get(packageName);
+ if (permissions != null) {
+ readPermissionsState(permissions,
+ packageSetting.getLegacyPermissionState(),
+ userId);
+ packageSetting.setInstallPermissionsFixed(true);
+ } else if (packageSetting.getSharedUser() == null && !isUpgradeToR) {
+ Slog.w(TAG, "Missing permission state for package: " + packageName);
+ packageSetting.getLegacyPermissionState().setMissing(true, userId);
+ }
+ }
+
+ Map<String, List<RuntimePermissionsState.PermissionState>> sharedUserPermissions =
+ runtimePermissions.getSharedUserPermissions();
+ int sharedUsersSize = sharedUsers.size();
+ for (int i = 0; i < sharedUsersSize; i++) {
+ String sharedUserName = sharedUsers.keyAt(i);
+ SharedUserSetting sharedUserSetting = sharedUsers.valueAt(i);
+
+ List<RuntimePermissionsState.PermissionState> permissions =
+ sharedUserPermissions.get(sharedUserName);
+ if (permissions != null) {
+ readPermissionsState(permissions,
+ sharedUserSetting.getLegacyPermissionState(), userId);
+ } else if (!isUpgradeToR) {
+ Slog.w(TAG, "Missing permission state for shared user: " + sharedUserName);
+ sharedUserSetting.getLegacyPermissionState().setMissing(true, userId);
+ }
}
}
}
- private void readPermissionsStateLpr(
+ private void readPermissionsState(
@NonNull List<RuntimePermissionsState.PermissionState> permissions,
@NonNull LegacyPermissionState permissionsState, @UserIdInt int userId) {
int permissionsSize = permissions.size();
@@ -5630,77 +5673,86 @@
}
}
- @GuardedBy("Settings.this.mLock")
- private void readLegacyStateForUserSyncLPr(int userId) {
- File permissionsFile = getUserRuntimePermissionsFile(userId);
- if (!permissionsFile.exists()) {
- return;
- }
+ private void readLegacyStateForUserSync(int userId, @NonNull File permissionsFile,
+ @NonNull WatchedArrayMap<String, ? extends PackageStateInternal> packageStates,
+ @NonNull WatchedArrayMap<String, SharedUserSetting> sharedUsers) {
+ synchronized (mLock) {
+ if (!permissionsFile.exists()) {
+ return;
+ }
- FileInputStream in;
- try {
- in = new AtomicFile(permissionsFile).openRead();
- } catch (FileNotFoundException fnfe) {
- Slog.i(PackageManagerService.TAG, "No permissions state");
- return;
- }
+ FileInputStream in;
+ try {
+ in = new AtomicFile(permissionsFile).openRead();
+ } catch (FileNotFoundException fnfe) {
+ Slog.i(PackageManagerService.TAG, "No permissions state");
+ return;
+ }
- try {
- final TypedXmlPullParser parser = Xml.resolvePullParser(in);
- parseLegacyRuntimePermissionsLPr(parser, userId);
+ try {
+ final TypedXmlPullParser parser = Xml.resolvePullParser(in);
+ parseLegacyRuntimePermissions(parser, userId, packageStates, sharedUsers);
- } catch (XmlPullParserException | IOException e) {
- throw new IllegalStateException("Failed parsing permissions file: "
- + permissionsFile, e);
- } finally {
- IoUtils.closeQuietly(in);
+ } catch (XmlPullParserException | IOException e) {
+ throw new IllegalStateException("Failed parsing permissions file: "
+ + permissionsFile, e);
+ } finally {
+ IoUtils.closeQuietly(in);
+ }
}
}
- // Private internals
-
- @GuardedBy("Settings.this.mLock")
- private void parseLegacyRuntimePermissionsLPr(TypedXmlPullParser parser, int userId)
+ private void parseLegacyRuntimePermissions(TypedXmlPullParser parser, int userId,
+ @NonNull WatchedArrayMap<String, ? extends PackageStateInternal> packageStates,
+ @NonNull WatchedArrayMap<String, SharedUserSetting> sharedUsers)
throws IOException, XmlPullParserException {
- final int outerDepth = parser.getDepth();
- int type;
- while ((type = parser.next()) != XmlPullParser.END_DOCUMENT
- && (type != XmlPullParser.END_TAG || parser.getDepth() > outerDepth)) {
- if (type == XmlPullParser.END_TAG || type == XmlPullParser.TEXT) {
- continue;
- }
+ synchronized (mLock) {
+ final int outerDepth = parser.getDepth();
+ int type;
+ while ((type = parser.next()) != XmlPullParser.END_DOCUMENT
+ && (type != XmlPullParser.END_TAG || parser.getDepth() > outerDepth)) {
+ if (type == XmlPullParser.END_TAG || type == XmlPullParser.TEXT) {
+ continue;
+ }
- switch (parser.getName()) {
- case TAG_RUNTIME_PERMISSIONS: {
- // If the permisions settings file exists but the version is not set this is
- // an upgrade from P->Q. Hence mark it with the special UPGRADE_VERSION
- int version = parser.getAttributeInt(null, ATTR_VERSION, UPGRADE_VERSION);
- mVersions.put(userId, version);
- String fingerprint = parser.getAttributeValue(null, ATTR_FINGERPRINT);
- mFingerprints.put(userId, fingerprint);
- } break;
-
- case TAG_PACKAGE: {
- String name = parser.getAttributeValue(null, ATTR_NAME);
- PackageSetting ps = mPackages.get(name);
- if (ps == null) {
- Slog.w(PackageManagerService.TAG, "Unknown package:" + name);
- XmlUtils.skipCurrentTag(parser);
- continue;
+ switch (parser.getName()) {
+ case TAG_RUNTIME_PERMISSIONS: {
+ // If the permisions settings file exists but the version is not set this is
+ // an upgrade from P->Q. Hence mark it with the special UPGRADE_VERSION
+ int version = parser.getAttributeInt(null, ATTR_VERSION,
+ UPGRADE_VERSION);
+ mVersions.put(userId, version);
+ String fingerprint = parser.getAttributeValue(null, ATTR_FINGERPRINT);
+ mFingerprints.put(userId, fingerprint);
}
- parseLegacyPermissionsLPr(parser, ps.getLegacyPermissionState(), userId);
- } break;
+ break;
- case TAG_SHARED_USER: {
- String name = parser.getAttributeValue(null, ATTR_NAME);
- SharedUserSetting sus = mSharedUsers.get(name);
- if (sus == null) {
- Slog.w(PackageManagerService.TAG, "Unknown shared user:" + name);
- XmlUtils.skipCurrentTag(parser);
- continue;
+ case TAG_PACKAGE: {
+ String name = parser.getAttributeValue(null, ATTR_NAME);
+ PackageStateInternal ps = packageStates.get(name);
+ if (ps == null) {
+ Slog.w(PackageManagerService.TAG, "Unknown package:" + name);
+ XmlUtils.skipCurrentTag(parser);
+ continue;
+ }
+ parseLegacyPermissionsLPr(parser, ps.getLegacyPermissionState(),
+ userId);
}
- parseLegacyPermissionsLPr(parser, sus.getLegacyPermissionState(), userId);
- } break;
+ break;
+
+ case TAG_SHARED_USER: {
+ String name = parser.getAttributeValue(null, ATTR_NAME);
+ SharedUserSetting sus = sharedUsers.get(name);
+ if (sus == null) {
+ Slog.w(PackageManagerService.TAG, "Unknown shared user:" + name);
+ XmlUtils.skipCurrentTag(parser);
+ continue;
+ }
+ parseLegacyPermissionsLPr(parser, sus.getLegacyPermissionState(),
+ userId);
+ }
+ break;
+ }
}
}
}
@@ -5708,25 +5760,27 @@
private void parseLegacyPermissionsLPr(TypedXmlPullParser parser,
LegacyPermissionState permissionsState, int userId)
throws IOException, XmlPullParserException {
- final int outerDepth = parser.getDepth();
- int type;
- while ((type = parser.next()) != XmlPullParser.END_DOCUMENT
- && (type != XmlPullParser.END_TAG || parser.getDepth() > outerDepth)) {
- if (type == XmlPullParser.END_TAG || type == XmlPullParser.TEXT) {
- continue;
- }
-
- switch (parser.getName()) {
- case TAG_ITEM: {
- String name = parser.getAttributeValue(null, ATTR_NAME);
- final boolean granted =
- parser.getAttributeBoolean(null, ATTR_GRANTED, true);
- final int flags =
- parser.getAttributeIntHex(null, ATTR_FLAGS, 0);
- permissionsState.putPermissionState(new PermissionState(name, true,
- granted, flags), userId);
+ synchronized (mLock) {
+ final int outerDepth = parser.getDepth();
+ int type;
+ while ((type = parser.next()) != XmlPullParser.END_DOCUMENT
+ && (type != XmlPullParser.END_TAG || parser.getDepth() > outerDepth)) {
+ if (type == XmlPullParser.END_TAG || type == XmlPullParser.TEXT) {
+ continue;
}
- break;
+
+ switch (parser.getName()) {
+ case TAG_ITEM: {
+ String name = parser.getAttributeValue(null, ATTR_NAME);
+ final boolean granted =
+ parser.getAttributeBoolean(null, ATTR_GRANTED, true);
+ final int flags =
+ parser.getAttributeIntHex(null, ATTR_FLAGS, 0);
+ permissionsState.putPermissionState(new PermissionState(name, true,
+ granted, flags), userId);
+ }
+ break;
+ }
}
}
}
@@ -5740,9 +5794,7 @@
public void handleMessage(Message message) {
final int userId = message.what;
Runnable callback = (Runnable) message.obj;
- synchronized (mLock) {
- writeStateForUserSyncLPr(userId);
- }
+ mInvokeWriteUserStateAsyncCallback.accept(userId);
if (callback != null) {
callback.run();
}
diff --git a/services/core/java/com/android/server/pm/SharedLibrariesImpl.java b/services/core/java/com/android/server/pm/SharedLibrariesImpl.java
new file mode 100644
index 0000000..f38ae77
--- /dev/null
+++ b/services/core/java/com/android/server/pm/SharedLibrariesImpl.java
@@ -0,0 +1,824 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.pm;
+
+import static com.android.server.pm.PackageManagerService.PLATFORM_PACKAGE_NAME;
+import static com.android.server.pm.PackageManagerService.TAG;
+
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.content.pm.PackageManager;
+import android.content.pm.SharedLibraryInfo;
+import android.content.pm.VersionedPackage;
+import android.os.Process;
+import android.os.UserHandle;
+import android.os.storage.StorageManager;
+import android.service.pm.PackageServiceDumpProto;
+import android.util.ArraySet;
+import android.util.Pair;
+import android.util.Slog;
+import android.util.proto.ProtoOutputStream;
+
+import com.android.internal.annotations.GuardedBy;
+import com.android.internal.util.ArrayUtils;
+import com.android.server.SystemConfig;
+import com.android.server.pm.parsing.pkg.AndroidPackage;
+import com.android.server.pm.parsing.pkg.AndroidPackageUtils;
+import com.android.server.utils.Snappable;
+import com.android.server.utils.SnapshotCache;
+import com.android.server.utils.Watchable;
+import com.android.server.utils.WatchableImpl;
+import com.android.server.utils.Watched;
+import com.android.server.utils.WatchedArrayMap;
+import com.android.server.utils.WatchedLongSparseArray;
+import com.android.server.utils.Watcher;
+
+import java.io.File;
+import java.io.IOException;
+import java.io.PrintWriter;
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.LinkedHashSet;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+import java.util.function.BiConsumer;
+
+/**
+ * Current known shared libraries on the device.
+ */
+public final class SharedLibrariesImpl implements SharedLibrariesRead, Watchable, Snappable {
+
+ // TODO(b/200588896): remove PMS dependency
+ private final PackageManagerService mPm;
+ private final PackageManagerServiceInjector mInjector;
+ private DeletePackageHelper mDeletePackageHelper; // late init
+
+ // A map of library name to a list of {@link SharedLibraryInfo}s with different versions.
+ @Watched
+ private final WatchedArrayMap<String, WatchedLongSparseArray<SharedLibraryInfo>>
+ mSharedLibraries;
+ private final SnapshotCache<WatchedArrayMap<String, WatchedLongSparseArray<SharedLibraryInfo>>>
+ mSharedLibrariesSnapshot;
+
+ // A map of declaring package name to a list of {@link SharedLibraryInfo}s with different
+ // versions.
+ @Watched
+ private final WatchedArrayMap<String, WatchedLongSparseArray<SharedLibraryInfo>>
+ mStaticLibsByDeclaringPackage;
+ private final SnapshotCache<WatchedArrayMap<String, WatchedLongSparseArray<SharedLibraryInfo>>>
+ mStaticLibsByDeclaringPackageSnapshot;
+
+ /**
+ * Watchable machinery
+ */
+ private final WatchableImpl mWatchable = new WatchableImpl();
+
+ /**
+ * The observer that watches for changes from array members
+ */
+ private final Watcher mObserver = new Watcher() {
+ @Override
+ public void onChange(@Nullable Watchable what) {
+ SharedLibrariesImpl.this.dispatchChange(what);
+ }
+ };
+
+ private final SnapshotCache<SharedLibrariesImpl> mSnapshot;
+
+ // Create a snapshot cache
+ private SnapshotCache<SharedLibrariesImpl> makeCache() {
+ return new SnapshotCache<SharedLibrariesImpl>(this /* source */, this /* watchable */) {
+ @Override
+ public SharedLibrariesImpl createSnapshot() {
+ final SharedLibrariesImpl sharedLibrariesImpl = new SharedLibrariesImpl(mSource);
+ sharedLibrariesImpl.mWatchable.seal();
+ return sharedLibrariesImpl;
+ }};
+ }
+
+ /**
+ * Default constructor used in PackageManagerService.
+ */
+ SharedLibrariesImpl(PackageManagerService pm, PackageManagerServiceInjector injector) {
+ mPm = pm;
+ mInjector = injector;
+
+ mSharedLibraries = new WatchedArrayMap<>();
+ mSharedLibrariesSnapshot = new SnapshotCache.Auto<>(mSharedLibraries, mSharedLibraries,
+ "SharedLibrariesImpl.mSharedLibraries");
+ mStaticLibsByDeclaringPackage = new WatchedArrayMap<>();
+ mStaticLibsByDeclaringPackageSnapshot = new SnapshotCache.Auto<>(
+ mStaticLibsByDeclaringPackage, mStaticLibsByDeclaringPackage,
+ "SharedLibrariesImpl.mStaticLibsByDeclaringPackage");
+
+ registerObservers();
+ Watchable.verifyWatchedAttributes(this, mObserver);
+ mSnapshot = makeCache();
+ }
+
+ /**
+ * Invoked by PMS constructor after the instance of {@link DeletePackageHelper} is ready.
+ */
+ void setDeletePackageHelper(DeletePackageHelper deletePackageHelper) {
+ mDeletePackageHelper = deletePackageHelper;
+ }
+
+ private void registerObservers() {
+ mSharedLibraries.registerObserver(mObserver);
+ mStaticLibsByDeclaringPackage.registerObserver(mObserver);
+ }
+
+ /**
+ * A copy constructor used in snapshot().
+ */
+ private SharedLibrariesImpl(SharedLibrariesImpl source) {
+ mPm = source.mPm;
+ mInjector = source.mInjector;
+
+ mSharedLibraries = source.mSharedLibrariesSnapshot.snapshot();
+ mSharedLibrariesSnapshot = new SnapshotCache.Sealed<>();
+ mStaticLibsByDeclaringPackage = source.mStaticLibsByDeclaringPackageSnapshot.snapshot();
+ mStaticLibsByDeclaringPackageSnapshot = new SnapshotCache.Sealed<>();
+
+ // Do not register any Watchables and do not create a snapshot cache.
+ mSnapshot = new SnapshotCache.Sealed();
+ }
+
+ /**
+ * Ensures an observer is in the list, exactly once. The observer cannot be null. The
+ * function quietly returns if the observer is already in the list.
+ *
+ * @param observer The {@link Watcher} to be notified when the {@link Watchable} changes.
+ */
+ @Override
+ public void registerObserver(@NonNull Watcher observer) {
+ mWatchable.registerObserver(observer);
+ }
+
+ /**
+ * Ensures an observer is not in the list. The observer must not be null. The function
+ * quietly returns if the objserver is not in the list.
+ *
+ * @param observer The {@link Watcher} that should not be in the notification list.
+ */
+ @Override
+ public void unregisterObserver(@NonNull Watcher observer) {
+ mWatchable.unregisterObserver(observer);
+ }
+
+ /**
+ * Return true if the {@link Watcher} is a registered observer.
+ * @param observer A {@link Watcher} that might be registered
+ * @return true if the observer is registered with this {@link Watchable}.
+ */
+ @Override
+ public boolean isRegisteredObserver(@NonNull Watcher observer) {
+ return mWatchable.isRegisteredObserver(observer);
+ }
+
+ /**
+ * Invokes {@link Watcher#onChange} on each registered observer. The method can be called
+ * with the {@link Watchable} that generated the event. In a tree of {@link Watchable}s, this
+ * is generally the first (deepest) {@link Watchable} to detect a change.
+ *
+ * @param what The {@link Watchable} that generated the event.
+ */
+ @Override
+ public void dispatchChange(@Nullable Watchable what) {
+ mWatchable.dispatchChange(what);
+ }
+
+ /**
+ * Create an immutable copy of the object, suitable for read-only methods. A snapshot
+ * is free to omit state that is only needed for mutating methods.
+ */
+ @Override
+ public @NonNull SharedLibrariesRead snapshot() {
+ return mSnapshot.snapshot();
+ }
+
+ /**
+ * Returns all shared libraries on the device.
+ */
+ @GuardedBy("mPm.mLock")
+ @Override
+ public @NonNull WatchedArrayMap<String, WatchedLongSparseArray<SharedLibraryInfo>> getAll() {
+ return mSharedLibraries;
+ }
+
+ /**
+ * Given the library name, returns a list of shared libraries on all versions.
+ */
+ @GuardedBy("mPm.mLock")
+ @Override
+ public @NonNull WatchedLongSparseArray<SharedLibraryInfo> getSharedLibraryInfos(
+ @NonNull String libName) {
+ return mSharedLibraries.get(libName);
+ }
+
+ /**
+ * Returns the shared library with given library name and version number.
+ */
+ @GuardedBy("mPm.mLock")
+ @Override
+ public @Nullable SharedLibraryInfo getSharedLibraryInfo(@NonNull String libName, long version) {
+ final WatchedLongSparseArray<SharedLibraryInfo> versionedLib =
+ mSharedLibraries.get(libName);
+ if (versionedLib == null) {
+ return null;
+ }
+ return versionedLib.get(version);
+ }
+
+ /**
+ * Given the declaring package name, returns a list of static shared libraries on all versions.
+ */
+ @GuardedBy("mPm.mLock")
+ @Override
+ public @NonNull WatchedLongSparseArray<SharedLibraryInfo> getStaticLibraryInfos(
+ @NonNull String declaringPackageName) {
+ return mStaticLibsByDeclaringPackage.get(declaringPackageName);
+ }
+
+ @GuardedBy("mPm.mLock")
+ private @Nullable PackageSetting getLibraryPackageLPr(@NonNull SharedLibraryInfo libInfo) {
+ final VersionedPackage declaringPackage = libInfo.getDeclaringPackage();
+ if (libInfo.isStatic()) {
+ // Resolve the package name - we use synthetic package names internally
+ final String internalPackageName = mPm.resolveInternalPackageNameLPr(
+ declaringPackage.getPackageName(),
+ declaringPackage.getLongVersionCode());
+ return mPm.mSettings.getPackageLPr(internalPackageName);
+ }
+ if (libInfo.isSdk()) {
+ return mPm.mSettings.getPackageLPr(declaringPackage.getPackageName());
+ }
+ return null;
+ }
+
+ /**
+ * Finds all unused shared libraries which have cached more than the given
+ * {@code maxCachePeriod}. Deletes them one by one until the available storage space on the
+ * device is larger than {@code neededSpace}.
+ *
+ * @param neededSpace A minimum available storage space the device needs to reach
+ * @param maxCachePeriod A maximum period of time an unused shared library can be cached
+ * on the device.
+ * @return {@code true} if the available storage space is reached.
+ */
+ boolean pruneUnusedStaticSharedLibraries(long neededSpace, long maxCachePeriod)
+ throws IOException {
+ final StorageManager storage = mInjector.getSystemService(StorageManager.class);
+ final File volume = storage.findPathForUuid(StorageManager.UUID_PRIVATE_INTERNAL);
+
+ List<VersionedPackage> packagesToDelete = null;
+ final long now = System.currentTimeMillis();
+
+ // Important: We skip shared libs used for some user since
+ // in such a case we need to keep the APK on the device. The check for
+ // a lib being used for any user is performed by the uninstall call.
+ synchronized (mPm.mLock) {
+ final int libCount = mSharedLibraries.size();
+ for (int i = 0; i < libCount; i++) {
+ final WatchedLongSparseArray<SharedLibraryInfo> versionedLib =
+ mSharedLibraries.valueAt(i);
+ if (versionedLib == null) {
+ continue;
+ }
+ final int versionCount = versionedLib.size();
+ for (int j = 0; j < versionCount; j++) {
+ SharedLibraryInfo libInfo = versionedLib.valueAt(j);
+ final PackageSetting ps = getLibraryPackageLPr(libInfo);
+ if (ps == null) {
+ continue;
+ }
+ // Skip unused libs cached less than the min period to prevent pruning a lib
+ // needed by a subsequently installed package.
+ if (now - ps.getLastUpdateTime() < maxCachePeriod) {
+ continue;
+ }
+
+ if (ps.getPkg().isSystem()) {
+ continue;
+ }
+
+ if (packagesToDelete == null) {
+ packagesToDelete = new ArrayList<>();
+ }
+ packagesToDelete.add(new VersionedPackage(ps.getPkg().getPackageName(),
+ libInfo.getDeclaringPackage().getLongVersionCode()));
+ }
+ }
+ }
+
+ if (packagesToDelete != null) {
+ final int packageCount = packagesToDelete.size();
+ for (int i = 0; i < packageCount; i++) {
+ final VersionedPackage pkgToDelete = packagesToDelete.get(i);
+ // Delete the package synchronously (will fail of the lib used for any user).
+ if (mDeletePackageHelper.deletePackageX(pkgToDelete.getPackageName(),
+ pkgToDelete.getLongVersionCode(), UserHandle.USER_SYSTEM,
+ PackageManager.DELETE_ALL_USERS,
+ true /*removedBySystem*/) == PackageManager.DELETE_SUCCEEDED) {
+ if (volume.getUsableSpace() >= neededSpace) {
+ return true;
+ }
+ }
+ }
+ }
+
+ return false;
+ }
+
+ /**
+ * Given a package of static shared library, returns its shared library info of
+ * the latest version.
+ *
+ * @param pkg A package of static shared library.
+ * @return The latest version of shared library info.
+ */
+ @GuardedBy("mPm.mLock")
+ @Nullable SharedLibraryInfo getLatestSharedLibraVersionLPr(@NonNull AndroidPackage pkg) {
+ WatchedLongSparseArray<SharedLibraryInfo> versionedLib = mSharedLibraries.get(
+ pkg.getStaticSharedLibName());
+ if (versionedLib == null) {
+ return null;
+ }
+ long previousLibVersion = -1;
+ final int versionCount = versionedLib.size();
+ for (int i = 0; i < versionCount; i++) {
+ final long libVersion = versionedLib.keyAt(i);
+ if (libVersion < pkg.getStaticSharedLibVersion()) {
+ previousLibVersion = Math.max(previousLibVersion, libVersion);
+ }
+ }
+ if (previousLibVersion >= 0) {
+ return versionedLib.get(previousLibVersion);
+ }
+ return null;
+ }
+
+ /**
+ * Given a package scanned result of a static shared library, returns its package setting of
+ * the latest version
+ *
+ * @param scanResult The scanned result of a static shared library package.
+ * @return The package setting that represents the latest version of shared library info.
+ */
+ @Nullable
+ PackageSetting getSharedLibLatestVersionSetting(@NonNull ScanResult scanResult) {
+ PackageSetting sharedLibPackage = null;
+ synchronized (mPm.mLock) {
+ final SharedLibraryInfo latestSharedLibraVersionLPr =
+ getLatestSharedLibraVersionLPr(scanResult.mRequest.mParsedPackage);
+ if (latestSharedLibraVersionLPr != null) {
+ sharedLibPackage = mPm.mSettings.getPackageLPr(
+ latestSharedLibraVersionLPr.getPackageName());
+ }
+ }
+ return sharedLibPackage;
+ }
+
+ /**
+ * Apply a given {@code action} to all the libraries defining in the package.
+ *
+ * @param pkg A package defining libraries.
+ * @param libInfo An extra shared library info passing to the action.
+ * @param action The action to apply.
+ */
+ @GuardedBy("mPm.mLock")
+ private void applyDefiningSharedLibraryUpdateLPr(
+ @NonNull AndroidPackage pkg, @Nullable SharedLibraryInfo libInfo,
+ @NonNull BiConsumer<SharedLibraryInfo, SharedLibraryInfo> action) {
+ // Note that libraries defined by this package may be null if:
+ // - Package manager was unable to create the shared library. The package still
+ // gets installed, but the shared library does not get created.
+ // Or:
+ // - Package manager is in a state where package isn't scanned yet. This will
+ // get called again after scanning to fix the dependencies.
+ if (AndroidPackageUtils.isLibrary(pkg)) {
+ if (pkg.getSdkLibName() != null) {
+ SharedLibraryInfo definedLibrary = getSharedLibraryInfo(
+ pkg.getSdkLibName(), pkg.getSdkLibVersionMajor());
+ if (definedLibrary != null) {
+ action.accept(definedLibrary, libInfo);
+ }
+ } else if (pkg.getStaticSharedLibName() != null) {
+ SharedLibraryInfo definedLibrary = getSharedLibraryInfo(
+ pkg.getStaticSharedLibName(), pkg.getStaticSharedLibVersion());
+ if (definedLibrary != null) {
+ action.accept(definedLibrary, libInfo);
+ }
+ } else {
+ for (String libraryName : pkg.getLibraryNames()) {
+ SharedLibraryInfo definedLibrary = getSharedLibraryInfo(
+ libraryName, SharedLibraryInfo.VERSION_UNDEFINED);
+ if (definedLibrary != null) {
+ action.accept(definedLibrary, libInfo);
+ }
+ }
+ }
+ }
+ }
+
+ /**
+ * Adds shared library {@code libInfo}'s self code paths and using library files to the list
+ * {@code usesLibraryFiles}. Also, adds the dependencies to the shared libraries that are
+ * defining in the {@code pkg}.
+ *
+ * @param pkg A package that is using the {@code libInfo}.
+ * @param usesLibraryFiles A list to add code paths to.
+ * @param libInfo A shared library info that is used by the {@code pkg}.
+ * @param changingLib The updating library package.
+ * @param changingLibSetting The updating library package setting.
+ */
+ @GuardedBy("mPm.mLock")
+ private void addSharedLibraryLPr(@NonNull AndroidPackage pkg,
+ @NonNull Set<String> usesLibraryFiles, @NonNull SharedLibraryInfo libInfo,
+ @Nullable AndroidPackage changingLib, @Nullable PackageSetting changingLibSetting) {
+ if (libInfo.getPath() != null) {
+ usesLibraryFiles.add(libInfo.getPath());
+ return;
+ }
+ AndroidPackage pkgForCodePaths = mPm.mPackages.get(libInfo.getPackageName());
+ PackageSetting pkgSetting = mPm.mSettings.getPackageLPr(libInfo.getPackageName());
+ if (changingLib != null && changingLib.getPackageName().equals(libInfo.getPackageName())) {
+ // If we are doing this while in the middle of updating a library apk,
+ // then we need to make sure to use that new apk for determining the
+ // dependencies here. (We haven't yet finished committing the new apk
+ // to the package manager state.)
+ if (pkgForCodePaths == null
+ || pkgForCodePaths.getPackageName().equals(changingLib.getPackageName())) {
+ pkgForCodePaths = changingLib;
+ pkgSetting = changingLibSetting;
+ }
+ }
+ if (pkgForCodePaths != null) {
+ usesLibraryFiles.addAll(AndroidPackageUtils.getAllCodePaths(pkgForCodePaths));
+ // If the package provides libraries, add the dependency to them.
+ applyDefiningSharedLibraryUpdateLPr(pkg, libInfo, SharedLibraryInfo::addDependency);
+ if (pkgSetting != null) {
+ usesLibraryFiles.addAll(pkgSetting.getPkgState().getUsesLibraryFiles());
+ }
+ }
+ }
+
+ /**
+ * Collects all shared libraries being used by the target package. Rebuilds the dependencies
+ * of shared libraries and update the correct shared library code paths for it.
+ *
+ * @param pkg The target package to update shared library dependency.
+ * @param pkgSetting The target's package setting.
+ * @param changingLib The updating library package.
+ * @param changingLibSetting The updating library package setting.
+ * @param availablePackages All installed packages and current being installed packages.
+ */
+ @GuardedBy("mPm.mLock")
+ void updateSharedLibrariesLPw(@NonNull AndroidPackage pkg, @NonNull PackageSetting pkgSetting,
+ @Nullable AndroidPackage changingLib, @Nullable PackageSetting changingLibSetting,
+ @NonNull Map<String, AndroidPackage> availablePackages)
+ throws PackageManagerException {
+ final ArrayList<SharedLibraryInfo> sharedLibraryInfos =
+ SharedLibraryHelper.collectSharedLibraryInfos(
+ pkgSetting.getPkg(), availablePackages, mSharedLibraries,
+ null /* newLibraries */, mInjector.getCompatibility());
+ executeSharedLibrariesUpdateLPw(pkg, pkgSetting, changingLib, changingLibSetting,
+ sharedLibraryInfos, mPm.mUserManager.getUserIds());
+ }
+
+ /**
+ * Rebuilds the dependencies of shared libraries for the target package, and update the
+ * shared library code paths to its package setting.
+ *
+ * @param pkg The target package to update shared library dependency.
+ * @param pkgSetting The target's package setting.
+ * @param changingLib The updating library package.
+ * @param changingLibSetting The updating library package setting.
+ * @param usesLibraryInfos The shared libraries used by the target package.
+ * @param allUsers All user ids on the device.
+ */
+ @GuardedBy("mPm.mLock")
+ void executeSharedLibrariesUpdateLPw(AndroidPackage pkg,
+ @NonNull PackageSetting pkgSetting, @Nullable AndroidPackage changingLib,
+ @Nullable PackageSetting changingLibSetting,
+ ArrayList<SharedLibraryInfo> usesLibraryInfos, int[] allUsers) {
+ // If the package provides libraries, clear their old dependencies.
+ // This method will set them up again.
+ applyDefiningSharedLibraryUpdateLPr(pkg, null, (definingLibrary, dependency) -> {
+ definingLibrary.clearDependencies();
+ });
+ if (usesLibraryInfos != null) {
+ pkgSetting.getPkgState().setUsesLibraryInfos(usesLibraryInfos);
+ // Use LinkedHashSet to preserve the order of files added to
+ // usesLibraryFiles while eliminating duplicates.
+ Set<String> usesLibraryFiles = new LinkedHashSet<>();
+ for (SharedLibraryInfo libInfo : usesLibraryInfos) {
+ addSharedLibraryLPr(pkg, usesLibraryFiles, libInfo, changingLib,
+ changingLibSetting);
+ }
+ pkgSetting.setPkgStateLibraryFiles(usesLibraryFiles);
+
+ // let's make sure we mark all static shared libraries as installed for the same users
+ // that its dependent packages are installed for.
+ int[] installedUsers = new int[allUsers.length];
+ int installedUserCount = 0;
+ for (int u = 0; u < allUsers.length; u++) {
+ if (pkgSetting.getInstalled(allUsers[u])) {
+ installedUsers[installedUserCount++] = allUsers[u];
+ }
+ }
+ for (SharedLibraryInfo sharedLibraryInfo : usesLibraryInfos) {
+ if (!sharedLibraryInfo.isStatic()) {
+ continue;
+ }
+ final PackageSetting staticLibPkgSetting =
+ mPm.getPackageSettingForMutation(sharedLibraryInfo.getPackageName());
+ if (staticLibPkgSetting == null) {
+ Slog.wtf(TAG, "Shared lib without setting: " + sharedLibraryInfo);
+ continue;
+ }
+ for (int u = 0; u < installedUserCount; u++) {
+ staticLibPkgSetting.setInstalled(true, installedUsers[u]);
+ }
+ }
+ } else {
+ pkgSetting.getPkgState().setUsesLibraryInfos(Collections.emptyList())
+ .setUsesLibraryFiles(Collections.emptyList());
+ }
+ }
+
+ private static boolean hasString(List<String> list, List<String> which) {
+ if (list == null || which == null) {
+ return false;
+ }
+ for (int i = list.size() - 1; i >= 0; i--) {
+ for (int j = which.size() - 1; j >= 0; j--) {
+ if (which.get(j).equals(list.get(i))) {
+ return true;
+ }
+ }
+ }
+ return false;
+ }
+
+ /**
+ * Update shared library dependencies and code paths for applications that are using the
+ * library {@code updatedPkg}. Update all applications if the {@code updatedPkg} is null.
+ *
+ * @param updatedPkg The updating shared library package.
+ * @param updatedPkgSetting The updating shared library package setting.
+ * @param availablePackages All available packages on the device.
+ * @return Packages that has been updated.
+ */
+ @GuardedBy("mPm.mLock")
+ @Nullable ArrayList<AndroidPackage> updateAllSharedLibrariesLPw(
+ @Nullable AndroidPackage updatedPkg, @Nullable PackageSetting updatedPkgSetting,
+ @NonNull Map<String, AndroidPackage> availablePackages) {
+ ArrayList<AndroidPackage> resultList = null;
+ // Set of all descendants of a library; used to eliminate cycles
+ ArraySet<String> descendants = null;
+ // The current list of packages that need updating
+ List<Pair<AndroidPackage, PackageSetting>> needsUpdating = null;
+ if (updatedPkg != null && updatedPkgSetting != null) {
+ needsUpdating = new ArrayList<>(1);
+ needsUpdating.add(Pair.create(updatedPkg, updatedPkgSetting));
+ }
+ do {
+ final Pair<AndroidPackage, PackageSetting> changingPkgPair =
+ (needsUpdating == null) ? null : needsUpdating.remove(0);
+ final AndroidPackage changingPkg = changingPkgPair != null
+ ? changingPkgPair.first : null;
+ final PackageSetting changingPkgSetting = changingPkgPair != null
+ ? changingPkgPair.second : null;
+ for (int i = mPm.mPackages.size() - 1; i >= 0; --i) {
+ final AndroidPackage pkg = mPm.mPackages.valueAt(i);
+ final PackageSetting pkgSetting = mPm.mSettings.getPackageLPr(pkg.getPackageName());
+ if (changingPkg != null
+ && !hasString(pkg.getUsesLibraries(), changingPkg.getLibraryNames())
+ && !hasString(pkg.getUsesOptionalLibraries(), changingPkg.getLibraryNames())
+ && !ArrayUtils.contains(pkg.getUsesStaticLibraries(),
+ changingPkg.getStaticSharedLibName())
+ && !ArrayUtils.contains(pkg.getUsesSdkLibraries(),
+ changingPkg.getSdkLibName())) {
+ continue;
+ }
+ if (resultList == null) {
+ resultList = new ArrayList<>();
+ }
+ resultList.add(pkg);
+ // if we're updating a shared library, all of its descendants must be updated
+ if (changingPkg != null) {
+ if (descendants == null) {
+ descendants = new ArraySet<>();
+ }
+ if (!descendants.contains(pkg.getPackageName())) {
+ descendants.add(pkg.getPackageName());
+ needsUpdating.add(Pair.create(pkg, pkgSetting));
+ }
+ }
+ try {
+ updateSharedLibrariesLPw(pkg, pkgSetting, changingPkg,
+ changingPkgSetting, availablePackages);
+ } catch (PackageManagerException e) {
+ // If a system app update or an app and a required lib missing we
+ // delete the package and for updated system apps keep the data as
+ // it is better for the user to reinstall than to be in an limbo
+ // state. Also libs disappearing under an app should never happen
+ // - just in case.
+ if (!pkg.isSystem() || pkgSetting.getPkgState().isUpdatedSystemApp()) {
+ final int flags = pkgSetting.getPkgState().isUpdatedSystemApp()
+ ? PackageManager.DELETE_KEEP_DATA : 0;
+ mDeletePackageHelper.deletePackageLIF(pkg.getPackageName(), null, true,
+ mPm.mUserManager.getUserIds(), flags, null,
+ true);
+ }
+ Slog.e(TAG, "updateAllSharedLibrariesLPw failed: " + e.getMessage());
+ }
+ }
+ } while (needsUpdating != null && needsUpdating.size() > 0);
+ return resultList;
+ }
+
+ /**
+ * Add a build-in shared library info by given system configuration.
+ */
+ @GuardedBy("mPm.mLock")
+ void addBuiltInSharedLibraryLPw(@NonNull SystemConfig.SharedLibraryEntry entry) {
+ // check if built-in or dynamic library exists
+ if (getSharedLibraryInfo(entry.name, SharedLibraryInfo.VERSION_UNDEFINED) != null) {
+ return;
+ }
+
+ SharedLibraryInfo libraryInfo = new SharedLibraryInfo(entry.filename, null, null,
+ entry.name, SharedLibraryInfo.VERSION_UNDEFINED,
+ SharedLibraryInfo.TYPE_BUILTIN,
+ new VersionedPackage(PLATFORM_PACKAGE_NAME, 0L), null, null,
+ entry.isNative);
+
+ commitSharedLibraryInfoLPw(libraryInfo);
+ }
+
+ /**
+ * Add a shared library info to the system. This is invoked when the package is being added or
+ * scanned.
+ */
+ @GuardedBy("mPm.mLock")
+ void commitSharedLibraryInfoLPw(@NonNull SharedLibraryInfo libraryInfo) {
+ final String name = libraryInfo.getName();
+ WatchedLongSparseArray<SharedLibraryInfo> versionedLib = mSharedLibraries.get(name);
+ if (versionedLib == null) {
+ versionedLib = new WatchedLongSparseArray<>();
+ mSharedLibraries.put(name, versionedLib);
+ }
+ final String declaringPackageName = libraryInfo.getDeclaringPackage().getPackageName();
+ if (libraryInfo.getType() == SharedLibraryInfo.TYPE_STATIC) {
+ mStaticLibsByDeclaringPackage.put(declaringPackageName, versionedLib);
+ }
+ versionedLib.put(libraryInfo.getLongVersion(), libraryInfo);
+ }
+
+ /**
+ * Remove a shared library from the system.
+ */
+ @GuardedBy("mPm.mLock")
+ boolean removeSharedLibraryLPw(@NonNull String libName, long version) {
+ WatchedLongSparseArray<SharedLibraryInfo> versionedLib = mSharedLibraries.get(libName);
+ if (versionedLib == null) {
+ return false;
+ }
+ final int libIdx = versionedLib.indexOfKey(version);
+ if (libIdx < 0) {
+ return false;
+ }
+ SharedLibraryInfo libraryInfo = versionedLib.valueAt(libIdx);
+
+ // Remove the shared library overlays from its dependent packages.
+ for (int currentUserId : mPm.mUserManager.getUserIds()) {
+ final List<VersionedPackage> dependents = mPm.getPackagesUsingSharedLibrary(
+ libraryInfo, 0, Process.SYSTEM_UID, currentUserId);
+ if (dependents == null) {
+ continue;
+ }
+ for (VersionedPackage dependentPackage : dependents) {
+ final PackageSetting ps = mPm.mSettings.getPackageLPr(
+ dependentPackage.getPackageName());
+ if (ps != null) {
+ ps.setOverlayPathsForLibrary(libraryInfo.getName(), null, currentUserId);
+ }
+ }
+ }
+
+ versionedLib.remove(version);
+ if (versionedLib.size() <= 0) {
+ mSharedLibraries.remove(libName);
+ if (libraryInfo.getType() == SharedLibraryInfo.TYPE_STATIC) {
+ mStaticLibsByDeclaringPackage.remove(libraryInfo.getDeclaringPackage()
+ .getPackageName());
+ }
+ }
+ return true;
+ }
+
+ /**
+ * Dump all shared libraries.
+ */
+ @GuardedBy("mPm.mLock")
+ @Override
+ public void dump(@NonNull PrintWriter pw, @NonNull DumpState dumpState) {
+ final boolean checkin = dumpState.isCheckIn();
+ boolean printedHeader = false;
+ final int numSharedLibraries = mSharedLibraries.size();
+ for (int index = 0; index < numSharedLibraries; index++) {
+ final String libName = mSharedLibraries.keyAt(index);
+ final WatchedLongSparseArray<SharedLibraryInfo> versionedLib =
+ mSharedLibraries.get(libName);
+ if (versionedLib == null) {
+ continue;
+ }
+ final int versionCount = versionedLib.size();
+ for (int i = 0; i < versionCount; i++) {
+ SharedLibraryInfo libraryInfo = versionedLib.valueAt(i);
+ if (!checkin) {
+ if (!printedHeader) {
+ if (dumpState.onTitlePrinted()) {
+ pw.println();
+ }
+ pw.println("Libraries:");
+ printedHeader = true;
+ }
+ pw.print(" ");
+ } else {
+ pw.print("lib,");
+ }
+ pw.print(libraryInfo.getName());
+ if (libraryInfo.isStatic()) {
+ pw.print(" version=" + libraryInfo.getLongVersion());
+ }
+ if (!checkin) {
+ pw.print(" -> ");
+ }
+ if (libraryInfo.getPath() != null) {
+ if (libraryInfo.isNative()) {
+ pw.print(" (so) ");
+ } else {
+ pw.print(" (jar) ");
+ }
+ pw.print(libraryInfo.getPath());
+ } else {
+ pw.print(" (apk) ");
+ pw.print(libraryInfo.getPackageName());
+ }
+ pw.println();
+ }
+ }
+ }
+
+ /**
+ * Dump all shared libraries to given proto output stream.
+ */
+ @GuardedBy("mPm.mLock")
+ @Override
+ public void dumpProto(@NonNull ProtoOutputStream proto) {
+ final int count = mSharedLibraries.size();
+ for (int i = 0; i < count; i++) {
+ final String libName = mSharedLibraries.keyAt(i);
+ WatchedLongSparseArray<SharedLibraryInfo> versionedLib =
+ mSharedLibraries.get(libName);
+ if (versionedLib == null) {
+ continue;
+ }
+ final int versionCount = versionedLib.size();
+ for (int j = 0; j < versionCount; j++) {
+ final SharedLibraryInfo libraryInfo = versionedLib.valueAt(j);
+ final long sharedLibraryToken =
+ proto.start(PackageServiceDumpProto.SHARED_LIBRARIES);
+ proto.write(PackageServiceDumpProto.SharedLibraryProto.NAME, libraryInfo.getName());
+ final boolean isJar = (libraryInfo.getPath() != null);
+ proto.write(PackageServiceDumpProto.SharedLibraryProto.IS_JAR, isJar);
+ if (isJar) {
+ proto.write(PackageServiceDumpProto.SharedLibraryProto.PATH,
+ libraryInfo.getPath());
+ } else {
+ proto.write(PackageServiceDumpProto.SharedLibraryProto.APK,
+ libraryInfo.getPackageName());
+ }
+ proto.end(sharedLibraryToken);
+ }
+ }
+ }
+}
diff --git a/services/core/java/com/android/server/pm/SharedLibrariesRead.java b/services/core/java/com/android/server/pm/SharedLibrariesRead.java
new file mode 100644
index 0000000..e6f2311
--- /dev/null
+++ b/services/core/java/com/android/server/pm/SharedLibrariesRead.java
@@ -0,0 +1,86 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.pm;
+
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.content.pm.SharedLibraryInfo;
+import android.util.proto.ProtoOutputStream;
+
+import com.android.server.utils.WatchedArrayMap;
+import com.android.server.utils.WatchedLongSparseArray;
+
+import java.io.PrintWriter;
+
+/**
+ * An interface implemented by {@link SharedLibrariesImpl} for {@link Computer} to get current
+ * shared libraries on the device.
+ */
+interface SharedLibrariesRead {
+
+ /**
+ * Returns all shared libraries on the device.
+ *
+ * @return A map of library name to a list of {@link SharedLibraryInfo}s with
+ * different versions.
+ */
+ @NonNull
+ WatchedArrayMap<String, WatchedLongSparseArray<SharedLibraryInfo>> getAll();
+
+ /**
+ * Given the library name, returns a list of shared libraries on all versions.
+ *
+ * @param libName The library name.
+ * @return A list of shared library info.
+ */
+ @Nullable
+ WatchedLongSparseArray<SharedLibraryInfo> getSharedLibraryInfos(@NonNull String libName);
+
+ /**
+ * Returns the shared library with given library name and version number.
+ *
+ * @param libName The library name.
+ * @param version The library version number.
+ * @return The shared library info.
+ */
+ @Nullable
+ SharedLibraryInfo getSharedLibraryInfo(@NonNull String libName, long version);
+
+ /**
+ * Given the declaring package name, returns a list of static shared libraries on all versions.
+ *
+ * @param declaringPackageName The declaring name of the package.
+ * @return A list of shared library info.
+ */
+ @Nullable
+ WatchedLongSparseArray<SharedLibraryInfo> getStaticLibraryInfos(
+ @NonNull String declaringPackageName);
+
+ /**
+ * Dump all shared libraries.
+ *
+ * @param pw A PrintWriter to dump to.
+ * @param dumpState Including options and states for writing.
+ */
+ void dump(@NonNull PrintWriter pw, @NonNull DumpState dumpState);
+
+ /**
+ * Dump all shared libraries to given proto output stream.
+ * @param proto A proto output stream to dump to.
+ */
+ void dumpProto(@NonNull ProtoOutputStream proto);
+}
diff --git a/services/core/java/com/android/server/pm/StagingManager.java b/services/core/java/com/android/server/pm/StagingManager.java
index 9cb8863..c1058bb 100644
--- a/services/core/java/com/android/server/pm/StagingManager.java
+++ b/services/core/java/com/android/server/pm/StagingManager.java
@@ -32,7 +32,7 @@
import android.content.pm.IStagedApexObserver;
import android.content.pm.PackageInstaller;
import android.content.pm.PackageInstaller.SessionInfo;
-import android.content.pm.PackageInstaller.SessionInfo.StagedSessionErrorCode;
+import android.content.pm.PackageInstaller.SessionInfo.SessionErrorCode;
import android.content.pm.PackageManager;
import android.content.pm.PackageManagerInternal;
import android.content.pm.StagedApexInfo;
@@ -129,7 +129,7 @@
boolean containsApkSession();
boolean containsApexSession();
void setSessionReady();
- void setSessionFailed(@StagedSessionErrorCode int errorCode, String errorMessage);
+ void setSessionFailed(@SessionErrorCode int errorCode, String errorMessage);
void setSessionApplied();
void installSession(IntentSender statusReceiver);
boolean hasParentSessionId();
diff --git a/services/core/java/com/android/server/pm/pkg/PackageStateInternal.java b/services/core/java/com/android/server/pm/pkg/PackageStateInternal.java
index 5460afa..56f62ab 100644
--- a/services/core/java/com/android/server/pm/pkg/PackageStateInternal.java
+++ b/services/core/java/com/android/server/pm/pkg/PackageStateInternal.java
@@ -23,6 +23,7 @@
import android.util.SparseArray;
import com.android.server.pm.InstallSource;
+import com.android.server.pm.PackageKeySetData;
import com.android.server.pm.SharedUserSetting;
import com.android.server.pm.parsing.pkg.AndroidPackage;
import com.android.server.pm.permission.LegacyPermissionState;
@@ -82,4 +83,7 @@
String getPathString();
float getLoadingProgress();
+
+ @NonNull
+ PackageKeySetData getKeySetData();
}
diff --git a/services/core/java/com/android/server/pm/pkg/PackageUserStateImpl.java b/services/core/java/com/android/server/pm/pkg/PackageUserStateImpl.java
index 32a9cf1..6fafb24 100644
--- a/services/core/java/com/android/server/pm/pkg/PackageUserStateImpl.java
+++ b/services/core/java/com/android/server/pm/pkg/PackageUserStateImpl.java
@@ -28,6 +28,7 @@
import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.util.ArrayUtils;
+import com.android.internal.util.CollectionUtils;
import com.android.internal.util.DataClass;
import java.util.Objects;
@@ -49,7 +50,6 @@
private boolean mNotLaunched;
private boolean mHidden; // Is the app restricted by owner / admin
private int mDistractionFlags;
- private boolean mSuspended;
private boolean mInstantApp;
private boolean mVirtualPreload;
private int mEnabledState = PackageManager.COMPONENT_ENABLED_STATE_DEFAULT;
@@ -101,7 +101,6 @@
mNotLaunched = other.mNotLaunched;
mHidden = other.mHidden;
mDistractionFlags = other.mDistractionFlags;
- mSuspended = other.mSuspended;
mInstantApp = other.mInstantApp;
mVirtualPreload = other.mVirtualPreload;
mEnabledState = other.mEnabledState;
@@ -256,6 +255,28 @@
return mComponentLabelIconOverrideMap.get(componentName);
}
+ @Override
+ public boolean isSuspended() {
+ return !CollectionUtils.isEmpty(mSuspendParams);
+ }
+
+ public PackageUserStateImpl putSuspendParams(@NonNull String suspendingPackage,
+ @NonNull SuspendParams suspendParams) {
+ if (mSuspendParams == null) {
+ mSuspendParams = new ArrayMap<>();
+ }
+ mSuspendParams.put(suspendingPackage, suspendParams);
+ return this;
+ }
+
+ public PackageUserStateImpl removeSuspension(@NonNull String suspendingPackage) {
+ if (mSuspendParams != null) {
+ mSuspendParams.remove(suspendingPackage);
+ }
+ return this;
+ }
+
+
// Code below generated by codegen v1.0.23.
@@ -312,11 +333,6 @@
}
@DataClass.Generated.Member
- public boolean isSuspended() {
- return mSuspended;
- }
-
- @DataClass.Generated.Member
public boolean isInstantApp() {
return mInstantApp;
}
@@ -433,12 +449,6 @@
}
@DataClass.Generated.Member
- public @NonNull PackageUserStateImpl setSuspended( boolean value) {
- mSuspended = value;
- return this;
- }
-
- @DataClass.Generated.Member
public @NonNull PackageUserStateImpl setInstantApp( boolean value) {
mInstantApp = value;
return this;
@@ -532,7 +542,6 @@
&& mNotLaunched == that.mNotLaunched
&& mHidden == that.mHidden
&& mDistractionFlags == that.mDistractionFlags
- && mSuspended == that.mSuspended
&& mInstantApp == that.mInstantApp
&& mVirtualPreload == that.mVirtualPreload
&& mEnabledState == that.mEnabledState
@@ -563,7 +572,6 @@
_hash = 31 * _hash + Boolean.hashCode(mNotLaunched);
_hash = 31 * _hash + Boolean.hashCode(mHidden);
_hash = 31 * _hash + mDistractionFlags;
- _hash = 31 * _hash + Boolean.hashCode(mSuspended);
_hash = 31 * _hash + Boolean.hashCode(mInstantApp);
_hash = 31 * _hash + Boolean.hashCode(mVirtualPreload);
_hash = 31 * _hash + mEnabledState;
@@ -581,10 +589,10 @@
}
@DataClass.Generated(
- time = 1633983318771L,
+ time = 1636145886996L,
codegenVersion = "1.0.23",
sourceFile = "frameworks/base/services/core/java/com/android/server/pm/pkg/PackageUserStateImpl.java",
- inputSignatures = "protected @android.annotation.Nullable android.util.ArraySet<java.lang.String> mDisabledComponents\nprotected @android.annotation.Nullable android.util.ArraySet<java.lang.String> mEnabledComponents\nprivate long mCeDataInode\nprivate boolean mInstalled\nprivate boolean mStopped\nprivate boolean mNotLaunched\nprivate boolean mHidden\nprivate int mDistractionFlags\nprivate boolean mSuspended\nprivate boolean mInstantApp\nprivate boolean mVirtualPreload\nprivate int mEnabledState\nprivate @android.content.pm.PackageManager.InstallReason int mInstallReason\nprivate @android.content.pm.PackageManager.UninstallReason int mUninstallReason\nprivate @android.annotation.Nullable java.lang.String mHarmfulAppWarning\nprivate @android.annotation.Nullable java.lang.String mLastDisableAppCaller\nprotected @android.annotation.Nullable android.content.pm.overlay.OverlayPaths mOverlayPaths\nprotected @android.annotation.Nullable android.util.ArrayMap<java.lang.String,android.content.pm.overlay.OverlayPaths> mSharedLibraryOverlayPaths\nprivate @android.annotation.Nullable java.lang.String mSplashScreenTheme\nprivate @android.annotation.Nullable android.util.ArrayMap<java.lang.String,com.android.server.pm.pkg.SuspendParams> mSuspendParams\nprivate @android.annotation.Nullable android.content.pm.overlay.OverlayPaths mCachedOverlayPaths\nprivate @android.annotation.Nullable android.util.ArrayMap<android.content.ComponentName,android.util.Pair<java.lang.String,java.lang.Integer>> mComponentLabelIconOverrideMap\npublic @android.annotation.Nullable boolean setOverlayPaths(android.content.pm.overlay.OverlayPaths)\npublic boolean setSharedLibraryOverlayPaths(java.lang.String,android.content.pm.overlay.OverlayPaths)\npublic @android.annotation.Nullable @java.lang.Override android.util.ArraySet<java.lang.String> getDisabledComponentsNoCopy()\npublic @android.annotation.Nullable @java.lang.Override android.util.ArraySet<java.lang.String> getEnabledComponentsNoCopy()\npublic @java.lang.Override boolean isComponentEnabled(java.lang.String)\npublic @java.lang.Override boolean isComponentDisabled(java.lang.String)\npublic @java.lang.Override android.content.pm.overlay.OverlayPaths getAllOverlayPaths()\npublic @com.android.internal.annotations.VisibleForTesting boolean overrideLabelAndIcon(android.content.ComponentName,java.lang.String,java.lang.Integer)\npublic void resetOverrideComponentLabelIcon()\npublic @android.annotation.Nullable android.util.Pair<java.lang.String,java.lang.Integer> getOverrideLabelIconForComponent(android.content.ComponentName)\nclass PackageUserStateImpl extends java.lang.Object implements [com.android.server.pm.pkg.PackageUserStateInternal]\n@com.android.internal.util.DataClass(genConstructor=false, genBuilder=false, genEqualsHashCode=true)")
+ inputSignatures = "protected @android.annotation.Nullable android.util.ArraySet<java.lang.String> mDisabledComponents\nprotected @android.annotation.Nullable android.util.ArraySet<java.lang.String> mEnabledComponents\nprivate long mCeDataInode\nprivate boolean mInstalled\nprivate boolean mStopped\nprivate boolean mNotLaunched\nprivate boolean mHidden\nprivate int mDistractionFlags\nprivate boolean mInstantApp\nprivate boolean mVirtualPreload\nprivate int mEnabledState\nprivate @android.content.pm.PackageManager.InstallReason int mInstallReason\nprivate @android.content.pm.PackageManager.UninstallReason int mUninstallReason\nprivate @android.annotation.Nullable java.lang.String mHarmfulAppWarning\nprivate @android.annotation.Nullable java.lang.String mLastDisableAppCaller\nprotected @android.annotation.Nullable android.content.pm.overlay.OverlayPaths mOverlayPaths\nprotected @android.annotation.Nullable android.util.ArrayMap<java.lang.String,android.content.pm.overlay.OverlayPaths> mSharedLibraryOverlayPaths\nprivate @android.annotation.Nullable java.lang.String mSplashScreenTheme\nprivate @android.annotation.Nullable android.util.ArrayMap<java.lang.String,com.android.server.pm.pkg.SuspendParams> mSuspendParams\nprivate @android.annotation.Nullable android.content.pm.overlay.OverlayPaths mCachedOverlayPaths\nprivate @android.annotation.Nullable android.util.ArrayMap<android.content.ComponentName,android.util.Pair<java.lang.String,java.lang.Integer>> mComponentLabelIconOverrideMap\npublic @android.annotation.Nullable boolean setOverlayPaths(android.content.pm.overlay.OverlayPaths)\npublic boolean setSharedLibraryOverlayPaths(java.lang.String,android.content.pm.overlay.OverlayPaths)\npublic @android.annotation.Nullable @java.lang.Override android.util.ArraySet<java.lang.String> getDisabledComponentsNoCopy()\npublic @android.annotation.Nullable @java.lang.Override android.util.ArraySet<java.lang.String> getEnabledComponentsNoCopy()\npublic @java.lang.Override boolean isComponentEnabled(java.lang.String)\npublic @java.lang.Override boolean isComponentDisabled(java.lang.String)\npublic @java.lang.Override android.content.pm.overlay.OverlayPaths getAllOverlayPaths()\npublic @com.android.internal.annotations.VisibleForTesting boolean overrideLabelAndIcon(android.content.ComponentName,java.lang.String,java.lang.Integer)\npublic void resetOverrideComponentLabelIcon()\npublic @android.annotation.Nullable android.util.Pair<java.lang.String,java.lang.Integer> getOverrideLabelIconForComponent(android.content.ComponentName)\npublic @java.lang.Override boolean isSuspended()\npublic com.android.server.pm.pkg.PackageUserStateImpl putSuspendParams(java.lang.String,com.android.server.pm.pkg.SuspendParams)\npublic com.android.server.pm.pkg.PackageUserStateImpl removeSuspension(java.lang.String)\nclass PackageUserStateImpl extends java.lang.Object implements [com.android.server.pm.pkg.PackageUserStateInternal]\n@com.android.internal.util.DataClass(genConstructor=false, genBuilder=false, genEqualsHashCode=true)")
@Deprecated
private void __metadata() {}
diff --git a/services/core/java/com/android/server/pm/pkg/PackageUserStateInternal.java b/services/core/java/com/android/server/pm/pkg/PackageUserStateInternal.java
index 6f33312..bd8b3ab 100644
--- a/services/core/java/com/android/server/pm/pkg/PackageUserStateInternal.java
+++ b/services/core/java/com/android/server/pm/pkg/PackageUserStateInternal.java
@@ -32,6 +32,7 @@
PackageUserStateInternal DEFAULT = new PackageUserStateDefault();
+ // TODO: Make non-null with emptyMap()
@Nullable
ArrayMap<String, SuspendParams> getSuspendParams();
diff --git a/services/core/java/com/android/server/pm/pkg/SuspendParams.java b/services/core/java/com/android/server/pm/pkg/SuspendParams.java
index 71512dc..d24ce96 100644
--- a/services/core/java/com/android/server/pm/pkg/SuspendParams.java
+++ b/services/core/java/com/android/server/pm/pkg/SuspendParams.java
@@ -42,11 +42,15 @@
private static final String TAG_APP_EXTRAS = "app-extras";
private static final String TAG_LAUNCHER_EXTRAS = "launcher-extras";
- public SuspendDialogInfo dialogInfo;
- public PersistableBundle appExtras;
- public PersistableBundle launcherExtras;
+ private final SuspendDialogInfo dialogInfo;
+ private final PersistableBundle appExtras;
+ private final PersistableBundle launcherExtras;
- private SuspendParams() {
+ private SuspendParams(SuspendDialogInfo dialogInfo, PersistableBundle appExtras,
+ PersistableBundle launcherExtras) {
+ this.dialogInfo = dialogInfo;
+ this.appExtras = appExtras;
+ this.launcherExtras = launcherExtras;
}
/**
@@ -60,11 +64,7 @@
if (dialogInfo == null && appExtras == null && launcherExtras == null) {
return null;
}
- final SuspendParams instance = new SuspendParams();
- instance.dialogInfo = dialogInfo;
- instance.appExtras = appExtras;
- instance.launcherExtras = launcherExtras;
- return instance;
+ return new SuspendParams(dialogInfo, appExtras, launcherExtras);
}
@Override
@@ -172,4 +172,16 @@
}
return getInstanceOrNull(readDialogInfo, readAppExtras, readLauncherExtras);
}
+
+ public SuspendDialogInfo getDialogInfo() {
+ return dialogInfo;
+ }
+
+ public PersistableBundle getAppExtras() {
+ return appExtras;
+ }
+
+ public PersistableBundle getLauncherExtras() {
+ return launcherExtras;
+ }
}
diff --git a/services/core/java/com/android/server/pm/pkg/mutate/PackageStateMutator.java b/services/core/java/com/android/server/pm/pkg/mutate/PackageStateMutator.java
new file mode 100644
index 0000000..35d4d9e
--- /dev/null
+++ b/services/core/java/com/android/server/pm/pkg/mutate/PackageStateMutator.java
@@ -0,0 +1,333 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.pm.pkg.mutate;
+
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.content.pm.ApplicationInfo;
+import android.content.pm.PackageManager;
+import android.content.pm.overlay.OverlayPaths;
+import android.util.ArraySet;
+
+import com.android.server.pm.PackageSetting;
+import com.android.server.pm.pkg.PackageUserStateImpl;
+import com.android.server.pm.pkg.SuspendParams;
+
+import java.util.concurrent.atomic.AtomicLong;
+import java.util.function.Function;
+
+public class PackageStateMutator {
+
+ private static final AtomicLong sStateChangeSequence = new AtomicLong();
+
+ private final StateWriteWrapper mStateWrite = new StateWriteWrapper();
+
+ private final Function<String, PackageSetting> mActiveStateFunction;
+ private final Function<String, PackageSetting> mDisabledStateFunction;
+
+ public PackageStateMutator(@NonNull Function<String, PackageSetting> activeStateFunction,
+ @NonNull Function<String, PackageSetting> disabledStateFunction) {
+ mActiveStateFunction = activeStateFunction;
+ mDisabledStateFunction = disabledStateFunction;
+ }
+
+ public static void onPackageStateChanged() {
+ sStateChangeSequence.incrementAndGet();
+ }
+
+ @NonNull
+ public PackageStateWrite forPackage(@NonNull String packageName) {
+ return mStateWrite.setState(mActiveStateFunction.apply(packageName));
+ }
+
+ @Nullable
+ public PackageStateWrite forPackageNullable(@NonNull String packageName) {
+ final PackageSetting packageState = mActiveStateFunction.apply(packageName);
+ mStateWrite.setState(packageState);
+ if (packageState == null) {
+ return null;
+ }
+
+ return mStateWrite.setState(packageState);
+ }
+
+ @NonNull
+ public PackageStateWrite forDisabledSystemPackage(@NonNull String packageName) {
+ return mStateWrite.setState(mDisabledStateFunction.apply(packageName));
+ }
+
+ @Nullable
+ public PackageStateWrite forDisabledSystemPackageNullable(@NonNull String packageName) {
+ final PackageSetting packageState = mDisabledStateFunction.apply(packageName);
+ if (packageState == null) {
+ return null;
+ }
+
+ return mStateWrite.setState(packageState);
+ }
+
+ @NonNull
+ public InitialState initialState(int changedPackagesSequenceNumber) {
+ return new InitialState(changedPackagesSequenceNumber, sStateChangeSequence.get());
+ }
+
+ /**
+ * @return null if initial state is null or if nothing has changed, otherwise return result
+ * with what changed
+ */
+ @Nullable
+ public Result generateResult(@Nullable InitialState state, int changedPackagesSequenceNumber) {
+ if (state == null) {
+ return Result.SUCCESS;
+ }
+
+ boolean packagesChanged = changedPackagesSequenceNumber != state.mPackageSequence;
+ boolean stateChanged = sStateChangeSequence.get() != state.mStateSequence;
+ if (packagesChanged && stateChanged) {
+ return Result.PACKAGES_AND_STATE_CHANGED;
+ } else if (packagesChanged) {
+ return Result.PACKAGES_CHANGED;
+ } else if (stateChanged) {
+ return Result.STATE_CHANGED;
+ } else {
+ return Result.SUCCESS;
+ }
+ }
+
+ public static class InitialState {
+
+ private final int mPackageSequence;
+ private final long mStateSequence;
+
+ public InitialState(int packageSequence, long stateSequence) {
+ mPackageSequence = packageSequence;
+ mStateSequence = stateSequence;
+ }
+ }
+
+ public static class Result {
+
+ public static final Result SUCCESS = new Result(true, false, false, false);
+ public static final Result PACKAGES_CHANGED = new Result(false, true, false, false);
+ public static final Result STATE_CHANGED = new Result(false, false, true, false);
+ public static final Result PACKAGES_AND_STATE_CHANGED = new Result(false, true, true, false);
+ public static final Result SPECIFIC_PACKAGE_NULL = new Result(false, false, true, true);
+
+ private final boolean mCommitted;
+ private final boolean mPackagesChanged;
+ private final boolean mStateChanged;
+ private final boolean mSpecificPackageNull;
+
+ public Result(boolean committed, boolean packagesChanged, boolean stateChanged,
+ boolean specificPackageNull) {
+ mCommitted = committed;
+ mPackagesChanged = packagesChanged;
+ mStateChanged = stateChanged;
+ mSpecificPackageNull = specificPackageNull;
+ }
+
+ public boolean isCommitted() {
+ return mCommitted;
+ }
+
+ public boolean isPackagesChanged() {
+ return mPackagesChanged;
+ }
+
+ public boolean isStateChanged() {
+ return mStateChanged;
+ }
+
+ public boolean isSpecificPackageNull() {
+ return mSpecificPackageNull;
+ }
+ }
+
+ private static class StateWriteWrapper implements PackageStateWrite {
+
+ private final UserStateWriteWrapper mUserStateWrite = new UserStateWriteWrapper();
+
+ @NonNull
+ private PackageSetting mState;
+
+ public StateWriteWrapper setState(PackageSetting state) {
+ this.mState = state;
+ return this;
+ }
+
+ @NonNull
+ @Override
+ public PackageUserStateWrite userState(int userId) {
+ return mUserStateWrite.setStates(
+ mState == null ? null : mState.getOrCreateUserState(userId));
+ }
+
+ @Override
+ public PackageStateWrite setLastPackageUsageTime(int reason, long timeInMillis) {
+ if (mState != null) {
+ mState.getTransientState().setLastPackageUsageTimeInMills(reason, timeInMillis);
+ }
+ return this;
+ }
+
+ @Override
+ public PackageStateWrite setHiddenUntilInstalled(boolean value) {
+ if (mState != null) {
+ mState.getTransientState().setHiddenUntilInstalled(value);
+ }
+ return this;
+ }
+
+ @NonNull
+ @Override
+ public PackageStateWrite setRequiredForSystemUser(boolean requiredForSystemUser) {
+ if (mState != null) {
+ if (requiredForSystemUser) {
+ mState.setPrivateFlags(mState.getPrivateFlags()
+ | ApplicationInfo.PRIVATE_FLAG_REQUIRED_FOR_SYSTEM_USER);
+ } else {
+ mState.setPrivateFlags(mState.getPrivateFlags()
+ & ~ApplicationInfo.PRIVATE_FLAG_REQUIRED_FOR_SYSTEM_USER);
+ }
+ }
+ return this;
+ }
+
+ @NonNull
+ @Override
+ public PackageStateWrite setMimeGroup(@NonNull String mimeGroup,
+ @NonNull ArraySet<String> mimeTypes) {
+ if (mState != null) {
+ mState.setMimeGroup(mimeGroup, mimeTypes);
+ }
+ return this;
+ }
+
+ private static class UserStateWriteWrapper implements PackageUserStateWrite {
+
+ @Nullable
+ private PackageUserStateImpl mUserState;
+
+ public UserStateWriteWrapper setStates(@Nullable PackageUserStateImpl userState) {
+ mUserState = userState;
+ return this;
+ }
+
+ @NonNull
+ @Override
+ public PackageUserStateWrite setInstalled(boolean installed) {
+ if (mUserState != null) {
+ mUserState.setInstalled(installed);
+ }
+ return this;
+ }
+
+ @NonNull
+ @Override
+ public PackageUserStateWrite setUninstallReason(int reason) {
+ if (mUserState != null) {
+ mUserState.setUninstallReason(reason);
+ }
+ return this;
+ }
+
+ @NonNull
+ @Override
+ public PackageUserStateWrite setDistractionFlags(
+ @PackageManager.DistractionRestriction int restrictionFlags) {
+ if (mUserState != null) {
+ mUserState.setDistractionFlags(restrictionFlags);
+ }
+ return this;
+ }
+
+ @NonNull
+ @Override
+ public PackageUserStateWrite putSuspendParams(@NonNull String suspendingPackage,
+ @Nullable SuspendParams suspendParams) {
+ if (mUserState != null) {
+ mUserState.putSuspendParams(suspendingPackage, suspendParams);
+ }
+ return this;
+ }
+
+ @NonNull
+ @Override
+ public PackageUserStateWrite removeSuspension(@NonNull String suspendingPackage) {
+ if (mUserState != null) {
+ mUserState.removeSuspension(suspendingPackage);
+ }
+ return this;
+ }
+
+ @NonNull
+ @Override
+ public PackageUserStateWrite setHidden(boolean hidden) {
+ if (mUserState != null) {
+ mUserState.setHidden(hidden);
+ }
+ return this;
+ }
+
+ @NonNull
+ @Override
+ public PackageUserStateWrite setStopped(boolean stopped) {
+ if (mUserState != null) {
+ mUserState.setStopped(stopped);
+ }
+ return this;
+ }
+
+ @NonNull
+ @Override
+ public PackageUserStateWrite setNotLaunched(boolean notLaunched) {
+ if (mUserState != null) {
+ mUserState.setNotLaunched(notLaunched);
+ }
+ return this;
+ }
+
+ @NonNull
+ @Override
+ public PackageUserStateWrite setOverlayPaths(@NonNull OverlayPaths overlayPaths) {
+ if (mUserState != null) {
+ mUserState.setOverlayPaths(overlayPaths);
+ }
+ return this;
+ }
+
+ @NonNull
+ @Override
+ public PackageUserStateWrite setOverlayPathsForLibrary(@NonNull String libraryName,
+ @Nullable OverlayPaths overlayPaths) {
+ if (mUserState != null) {
+ mUserState.setSharedLibraryOverlayPaths(libraryName, overlayPaths);
+ }
+ return this;
+ }
+
+ @NonNull
+ @Override
+ public PackageUserStateWrite setHarmfulAppWarning(@Nullable String warning) {
+ if (mUserState != null) {
+ mUserState.setHarmfulAppWarning(warning);
+ }
+ return this;
+ }
+ }
+ }
+}
diff --git a/services/core/java/com/android/server/pm/pkg/mutate/PackageStateWrite.java b/services/core/java/com/android/server/pm/pkg/mutate/PackageStateWrite.java
new file mode 100644
index 0000000..585bece
--- /dev/null
+++ b/services/core/java/com/android/server/pm/pkg/mutate/PackageStateWrite.java
@@ -0,0 +1,41 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.pm.pkg.mutate;
+
+import android.annotation.NonNull;
+import android.annotation.UserIdInt;
+import android.content.pm.PackageManager;
+import android.util.ArraySet;
+
+public interface PackageStateWrite {
+
+ @NonNull
+ PackageUserStateWrite userState(@UserIdInt int userId);
+
+ @NonNull
+ PackageStateWrite setLastPackageUsageTime(@PackageManager.NotifyReason int reason,
+ long timeInMillis);
+
+ @NonNull
+ PackageStateWrite setHiddenUntilInstalled(boolean hiddenUntilInstalled);
+
+ @NonNull
+ PackageStateWrite setRequiredForSystemUser(boolean requiredForSystemUser);
+
+ @NonNull
+ PackageStateWrite setMimeGroup(@NonNull String mimeGroup, @NonNull ArraySet<String> mimeTypes);
+}
diff --git a/services/core/java/com/android/server/pm/pkg/mutate/PackageUserStateWrite.java b/services/core/java/com/android/server/pm/pkg/mutate/PackageUserStateWrite.java
new file mode 100644
index 0000000..e23a1b6
--- /dev/null
+++ b/services/core/java/com/android/server/pm/pkg/mutate/PackageUserStateWrite.java
@@ -0,0 +1,63 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.pm.pkg.mutate;
+
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.content.pm.PackageManager;
+import android.content.pm.overlay.OverlayPaths;
+
+import com.android.server.pm.pkg.SuspendParams;
+
+public interface PackageUserStateWrite {
+
+ @NonNull
+ PackageUserStateWrite setInstalled(boolean installed);
+
+ @NonNull
+ PackageUserStateWrite setUninstallReason(@PackageManager.UninstallReason int reason);
+
+ @NonNull
+ PackageUserStateWrite setDistractionFlags(
+ @PackageManager.DistractionRestriction int restrictionFlags);
+
+ @NonNull
+ PackageUserStateWrite putSuspendParams(@NonNull String suspendingPackage,
+ @Nullable SuspendParams suspendParams);
+
+ @NonNull
+ PackageUserStateWrite removeSuspension(@NonNull String suspendingPackage);
+
+ @NonNull
+ PackageUserStateWrite setHidden(boolean hidden);
+
+ @NonNull
+ PackageUserStateWrite setStopped(boolean stopped);
+
+ @NonNull
+ PackageUserStateWrite setNotLaunched(boolean notLaunched);
+
+ @NonNull
+ PackageUserStateWrite setOverlayPaths(@Nullable OverlayPaths overlayPaths);
+
+ @NonNull
+ PackageUserStateWrite setOverlayPathsForLibrary(@NonNull String libraryName,
+ @Nullable OverlayPaths overlayPaths);
+
+ @NonNull
+ PackageUserStateWrite setHarmfulAppWarning(@Nullable String warning);
+}
diff --git a/services/core/java/com/android/server/policy/KeyCombinationManager.java b/services/core/java/com/android/server/policy/KeyCombinationManager.java
index 268de3e..68e078c 100644
--- a/services/core/java/com/android/server/policy/KeyCombinationManager.java
+++ b/services/core/java/com/android/server/policy/KeyCombinationManager.java
@@ -17,11 +17,13 @@
import static android.view.KeyEvent.KEYCODE_POWER;
+import android.os.Handler;
import android.os.SystemClock;
import android.util.Log;
import android.util.SparseLongArray;
import android.view.KeyEvent;
+import com.android.internal.annotations.GuardedBy;
import com.android.internal.util.ToBooleanFunction;
import java.io.PrintWriter;
@@ -35,13 +37,18 @@
private static final String TAG = "KeyCombinationManager";
// Store the received down time of keycode.
+ @GuardedBy("mLock")
private final SparseLongArray mDownTimes = new SparseLongArray(2);
private final ArrayList<TwoKeysCombinationRule> mRules = new ArrayList();
// Selected rules according to current key down.
+ private final Object mLock = new Object();
+ @GuardedBy("mLock")
private final ArrayList<TwoKeysCombinationRule> mActiveRules = new ArrayList();
// The rule has been triggered by current keys.
+ @GuardedBy("mLock")
private TwoKeysCombinationRule mTriggeredRule;
+ private final Handler mHandler = new Handler();
// Keys in a key combination must be pressed within this interval of each other.
private static final long COMBINE_KEY_DELAY_MILLIS = 150;
@@ -109,6 +116,12 @@
* Return true if any active rule could be triggered by the key event, otherwise false.
*/
boolean interceptKey(KeyEvent event, boolean interactive) {
+ synchronized (mLock) {
+ return interceptKeyLocked(event, interactive);
+ }
+ }
+
+ private boolean interceptKeyLocked(KeyEvent event, boolean interactive) {
final boolean down = event.getAction() == KeyEvent.ACTION_DOWN;
final int keyCode = event.getKeyCode();
final int count = mActiveRules.size();
@@ -154,7 +167,7 @@
return false;
}
Log.v(TAG, "Performing combination rule : " + rule);
- rule.execute();
+ mHandler.post(rule::execute);
mTriggeredRule = rule;
return true;
});
@@ -169,7 +182,7 @@
for (int index = count - 1; index >= 0; index--) {
final TwoKeysCombinationRule rule = mActiveRules.get(index);
if (rule.shouldInterceptKey(keyCode)) {
- rule.cancel();
+ mHandler.post(rule::cancel);
mActiveRules.remove(index);
}
}
@@ -181,31 +194,37 @@
* Return the interceptTimeout to tell InputDispatcher when is ready to deliver to window.
*/
long getKeyInterceptTimeout(int keyCode) {
- if (forAllActiveRules((rule) -> rule.shouldInterceptKey(keyCode))) {
- return mDownTimes.get(keyCode) + COMBINE_KEY_DELAY_MILLIS;
+ synchronized (mLock) {
+ if (forAllActiveRules((rule) -> rule.shouldInterceptKey(keyCode))) {
+ return mDownTimes.get(keyCode) + COMBINE_KEY_DELAY_MILLIS;
+ }
+ return 0;
}
- return 0;
}
/**
* True if the key event had been handled.
*/
boolean isKeyConsumed(KeyEvent event) {
- if ((event.getFlags() & KeyEvent.FLAG_FALLBACK) != 0) {
- return false;
+ synchronized (mLock) {
+ if ((event.getFlags() & KeyEvent.FLAG_FALLBACK) != 0) {
+ return false;
+ }
+ return mTriggeredRule != null && mTriggeredRule.shouldInterceptKey(event.getKeyCode());
}
- return mTriggeredRule != null && mTriggeredRule.shouldInterceptKey(event.getKeyCode());
}
/**
* True if power key is the candidate.
*/
boolean isPowerKeyIntercepted() {
- if (forAllActiveRules((rule) -> rule.shouldInterceptKey(KEYCODE_POWER))) {
- // return false if only if power key pressed.
- return mDownTimes.size() > 1 || mDownTimes.get(KEYCODE_POWER) == 0;
+ synchronized (mLock) {
+ if (forAllActiveRules((rule) -> rule.shouldInterceptKey(KEYCODE_POWER))) {
+ // return false if only if power key pressed.
+ return mDownTimes.size() > 1 || mDownTimes.get(KEYCODE_POWER) == 0;
+ }
+ return false;
}
- return false;
}
/**
diff --git a/services/core/java/com/android/server/policy/ModifierShortcutManager.java b/services/core/java/com/android/server/policy/ModifierShortcutManager.java
index e857d32..784e177 100644
--- a/services/core/java/com/android/server/policy/ModifierShortcutManager.java
+++ b/services/core/java/com/android/server/policy/ModifierShortcutManager.java
@@ -112,10 +112,15 @@
* @return The intent that matches the shortcut, or null if not found.
*/
private Intent getIntent(KeyCharacterMap kcm, int keyCode, int metaState) {
+ // If a modifier key other than shift is also pressed, skip it.
+ final boolean isShiftOn = KeyEvent.metaStateHasModifiers(metaState, KeyEvent.META_SHIFT_ON);
+ if (!isShiftOn && !KeyEvent.metaStateHasNoModifiers(metaState)) {
+ return null;
+ }
+
ShortcutInfo shortcut = null;
// If the Shift key is pressed, then search for the shift shortcuts.
- boolean isShiftOn = (metaState & KeyEvent.META_SHIFT_ON) == KeyEvent.META_SHIFT_ON;
SparseArray<ShortcutInfo> shortcutMap = isShiftOn ? mShiftShortcuts : mIntentShortcuts;
// First try the exact keycode (with modifiers).
diff --git a/services/core/java/com/android/server/policy/PhoneWindowManager.java b/services/core/java/com/android/server/policy/PhoneWindowManager.java
index 298f102..28f65cf 100644
--- a/services/core/java/com/android/server/policy/PhoneWindowManager.java
+++ b/services/core/java/com/android/server/policy/PhoneWindowManager.java
@@ -363,6 +363,12 @@
public static final int TOAST_WINDOW_TIMEOUT = 3500 + TOAST_WINDOW_ANIM_BUFFER;
/**
+ * Action for launching assistant in retail mode
+ */
+ private static final String ACTION_VOICE_ASSIST_RETAIL =
+ "android.intent.action.VOICE_ASSIST_RETAIL";
+
+ /**
* Lock protecting internal state. Must not call out into window
* manager with lock held. (This lock will be acquired in places
* where the window manager is calling in with its own lock held.)
@@ -1090,29 +1096,35 @@
mPowerManager.boostScreenBrightness(eventTime);
break;
case MULTI_PRESS_POWER_LAUNCH_TARGET_ACTIVITY:
- if (DEBUG_INPUT) {
- Slog.d(TAG, "Executing the double press power action.");
- }
+ launchTargetActivityOnMultiPressPower();
+ break;
+ }
+ }
+
+ private void launchTargetActivityOnMultiPressPower() {
+ if (DEBUG_INPUT) {
+ Slog.d(TAG, "Executing the double press power action.");
+ }
+ if (mPowerDoublePressTargetActivity != null) {
+ Intent intent = new Intent();
+ intent.setComponent(mPowerDoublePressTargetActivity);
+ ResolveInfo resolveInfo = mContext.getPackageManager().resolveActivity(
+ intent, /* flags= */0);
+ if (resolveInfo != null) {
final boolean keyguardActive =
mKeyguardDelegate != null && mKeyguardDelegate.isShowing();
+ intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK
+ | Intent.FLAG_ACTIVITY_RESET_TASK_IF_NEEDED);
if (!keyguardActive) {
- Intent intent = new Intent();
- if (mPowerDoublePressTargetActivity != null) {
- intent.setComponent(mPowerDoublePressTargetActivity);
- ResolveInfo resolveInfo = mContext.getPackageManager().resolveActivity(
- intent, /* flags= */0);
- if (resolveInfo != null) {
- intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK
- | Intent.FLAG_ACTIVITY_RESET_TASK_IF_NEEDED);
- startActivityAsUser(intent, UserHandle.CURRENT_OR_SELF);
- } else {
- Slog.e(TAG, "Could not resolve activity with : "
- + mPowerDoublePressTargetActivity.flattenToString()
- + " name.");
- }
- }
+ startActivityAsUser(intent, UserHandle.CURRENT_OR_SELF);
+ } else {
+ mKeyguardDelegate.dismissKeyguardToLaunch(intent);
}
- break;
+ } else {
+ Slog.e(TAG, "Could not resolve activity with : "
+ + mPowerDoublePressTargetActivity.flattenToString()
+ + " name.");
+ }
}
}
@@ -1242,6 +1254,12 @@
return LONG_PRESS_POWER_GLOBAL_ACTIONS;
}
+ // If long press to launch assistant is disabled in settings, do nothing.
+ if (mLongPressOnPowerBehavior == LONG_PRESS_POWER_GO_TO_VOICE_ASSIST
+ && !isLongPressToAssistantEnabled(mContext)) {
+ return LONG_PRESS_POWER_NOTHING;
+ }
+
return mLongPressOnPowerBehavior;
}
@@ -1273,6 +1291,9 @@
intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK
| Intent.FLAG_ACTIVITY_RESET_TASK_IF_NEEDED);
startActivityAsUser(intent, UserHandle.CURRENT_OR_SELF);
+ } else {
+ // If keyguarded then notify the keyguard.
+ mKeyguardDelegate.onSystemKeyPressed(KeyEvent.KEYCODE_STEM_PRIMARY);
}
break;
}
@@ -2675,6 +2696,7 @@
final boolean canceled = event.isCanceled();
final int displayId = event.getDisplayId();
final long key_consumed = -1;
+ final long key_not_consumed = 0;
if (DEBUG_INPUT) {
Log.d(TAG, "interceptKeyTi keyCode=" + keyCode + " down=" + down + " repeatCount="
@@ -2864,7 +2886,7 @@
case KeyEvent.KEYCODE_TAB:
if (event.isMetaPressed()) {
// Pass through keyboard navigation keys.
- return 0;
+ return key_not_consumed;
}
// Display task switcher for ALT-TAB.
if (down && repeatCount == 0) {
@@ -2895,9 +2917,9 @@
return key_consumed;
case KeyEvent.KEYCODE_SPACE:
- // Handle keyboard layout switching.
- if ((metaState & (KeyEvent.META_CTRL_MASK | KeyEvent.META_META_MASK)) == 0) {
- return 0;
+ // Handle keyboard layout switching. (META + SPACE)
+ if ((metaState & KeyEvent.META_META_MASK) == 0) {
+ return key_not_consumed;
}
// Share the same behavior with KEYCODE_LANGUAGE_SWITCH.
case KeyEvent.KEYCODE_LANGUAGE_SWITCH:
@@ -2971,7 +2993,7 @@
}
// Let the application handle the key.
- return 0;
+ return key_not_consumed;
}
/**
@@ -3037,6 +3059,10 @@
+ ", policyFlags=" + policyFlags);
}
+ if (interceptUnhandledKey(event)) {
+ return null;
+ }
+
KeyEvent fallbackEvent = null;
if ((event.getFlags() & KeyEvent.FLAG_FALLBACK) == 0) {
final KeyCharacterMap kcm = event.getKeyCharacterMap();
@@ -3091,13 +3117,46 @@
return fallbackEvent;
}
+ private boolean interceptUnhandledKey(KeyEvent event) {
+ final int keyCode = event.getKeyCode();
+ final int repeatCount = event.getRepeatCount();
+ final boolean down = event.getAction() == KeyEvent.ACTION_DOWN;
+ final int metaState = event.getModifiers();
+
+ switch(keyCode) {
+ case KeyEvent.KEYCODE_SPACE:
+ if (down && repeatCount == 0) {
+ // Handle keyboard layout switching. (CTRL + SPACE)
+ if (KeyEvent.metaStateHasModifiers(metaState, KeyEvent.META_CTRL_ON)) {
+ int direction = (metaState & KeyEvent.META_SHIFT_MASK) != 0 ? -1 : 1;
+ mWindowManagerFuncs.switchKeyboardLayout(event.getDeviceId(), direction);
+ return true;
+ }
+ }
+ break;
+ case KeyEvent.KEYCODE_Z:
+ if (down && KeyEvent.metaStateHasModifiers(metaState,
+ KeyEvent.META_CTRL_ON | KeyEvent.META_ALT_ON)) {
+ // Intercept the Accessibility keychord (CTRL + ALT + Z) for keyboard users.
+ if (mAccessibilityShortcutController
+ .isAccessibilityShortcutAvailable(isKeyguardLocked())) {
+ mHandler.sendMessage(mHandler.obtainMessage(MSG_ACCESSIBILITY_SHORTCUT));
+ return true;
+ }
+ }
+ break;
+ }
+
+ return false;
+ }
+
private boolean interceptFallback(IBinder focusedToken, KeyEvent fallbackEvent,
int policyFlags) {
int actions = interceptKeyBeforeQueueing(fallbackEvent, policyFlags);
if ((actions & ACTION_PASS_TO_USER) != 0) {
long delayMillis = interceptKeyBeforeDispatching(
focusedToken, fallbackEvent, policyFlags);
- if (delayMillis == 0) {
+ if (delayMillis == 0 && !interceptUnhandledKey(fallbackEvent)) {
return true;
}
}
@@ -3218,17 +3277,50 @@
.getSystemService(Context.SEARCH_SERVICE)).launchAssist(args);
}
- /** Launches ACTION_VOICE_ASSIST. Does nothing on keyguard. */
+ /**
+ * Launches ACTION_VOICE_ASSIST_RETAIL if in retail mode, or ACTION_VOICE_ASSIST otherwise
+ * Does nothing on keyguard except for watches. Delegates it to keyguard if present on watch.
+ */
private void launchVoiceAssist(boolean allowDuringSetup) {
- final boolean keyguardActive = mKeyguardDelegate == null
- ? false
- : mKeyguardDelegate.isShowing();
+ final boolean keyguardActive =
+ mKeyguardDelegate != null && mKeyguardDelegate.isShowing();
if (!keyguardActive) {
- Intent intent = new Intent(Intent.ACTION_VOICE_ASSIST);
- startActivityAsUser(intent, null, UserHandle.CURRENT_OR_SELF,
- allowDuringSetup);
+ if (mHasFeatureWatch && isInRetailMode()) {
+ launchRetailVoiceAssist(allowDuringSetup);
+ } else {
+ startVoiceAssistIntent(allowDuringSetup);
+ }
+ } else {
+ mKeyguardDelegate.dismissKeyguardToLaunch(new Intent(Intent.ACTION_VOICE_ASSIST));
}
+ }
+ private void launchRetailVoiceAssist(boolean allowDuringSetup) {
+ Intent retailIntent = new Intent(ACTION_VOICE_ASSIST_RETAIL);
+ ResolveInfo resolveInfo = mContext.getPackageManager().resolveActivity(
+ retailIntent, /* flags= */0);
+ if (resolveInfo != null) {
+ retailIntent.setComponent(
+ new ComponentName(resolveInfo.activityInfo.packageName,
+ resolveInfo.activityInfo.name));
+ startActivityAsUser(retailIntent, null, UserHandle.CURRENT_OR_SELF,
+ allowDuringSetup);
+ } else {
+ Slog.w(TAG, "Couldn't find an app to process " + ACTION_VOICE_ASSIST_RETAIL
+ + ". Fall back to start " + Intent.ACTION_VOICE_ASSIST);
+ startVoiceAssistIntent(allowDuringSetup);
+ }
+ }
+
+ private void startVoiceAssistIntent(boolean allowDuringSetup) {
+ Intent intent = new Intent(Intent.ACTION_VOICE_ASSIST);
+ startActivityAsUser(intent, null, UserHandle.CURRENT_OR_SELF,
+ allowDuringSetup);
+ }
+
+ private boolean isInRetailMode() {
+ return Settings.Global.getInt(mContext.getContentResolver(),
+ Settings.Global.DEVICE_DEMO_MODE, 0) == 1;
}
private void startActivityAsUser(Intent intent, UserHandle handle) {
@@ -3935,19 +4027,6 @@
}
}
- // Intercept the Accessibility keychord (CTRL + ALT + Z) for keyboard users.
- if (mAccessibilityShortcutController.isAccessibilityShortcutAvailable(isKeyguardLocked())) {
- switch (keyCode) {
- case KeyEvent.KEYCODE_Z: {
- if (down && event.isCtrlPressed() && event.isAltPressed()) {
- mHandler.sendMessage(mHandler.obtainMessage(MSG_ACCESSIBILITY_SHORTCUT));
- result &= ~ACTION_PASS_TO_USER;
- }
- break;
- }
- }
- }
-
if (useHapticFeedback) {
performHapticFeedback(HapticFeedbackConstants.VIRTUAL_KEY, false,
"Virtual Key - Press");
@@ -5824,6 +5903,18 @@
}
}
+ public static boolean isLongPressToAssistantEnabled(Context context) {
+ ContentResolver resolver = context.getContentResolver();
+ int longPressToAssistant = Settings.System.getIntForUser(resolver,
+ Settings.Global.Wearable.CLOCKWORK_LONG_PRESS_TO_ASSISTANT_ENABLED,
+ /* def= */ 1,
+ UserHandle.USER_CURRENT);
+ if(Log.isLoggable(TAG, Log.DEBUG)) {
+ Log.d(TAG, "longPressToAssistant = " + longPressToAssistant);
+ }
+ return (longPressToAssistant == 1);
+ }
+
private class HdmiVideoExtconUEventObserver extends ExtconStateObserver<Boolean> {
private static final String HDMI_EXIST = "HDMI=1";
private static final String NAME = "hdmi";
diff --git a/services/core/java/com/android/server/policy/keyguard/KeyguardServiceDelegate.java b/services/core/java/com/android/server/policy/keyguard/KeyguardServiceDelegate.java
index 0080ec6..97a57e0 100644
--- a/services/core/java/com/android/server/policy/keyguard/KeyguardServiceDelegate.java
+++ b/services/core/java/com/android/server/policy/keyguard/KeyguardServiceDelegate.java
@@ -415,6 +415,17 @@
}
}
+ public void dismissKeyguardToLaunch(Intent intentToLaunch) {
+ if (mKeyguardService != null) {
+ mKeyguardService.dismissKeyguardToLaunch(intentToLaunch);
+ }
+ }
+ public void onSystemKeyPressed(int keycode) {
+ if (mKeyguardService != null) {
+ mKeyguardService.onSystemKeyPressed(keycode);
+ }
+ }
+
public void dumpDebug(ProtoOutputStream proto, long fieldId) {
final long token = proto.start(fieldId);
proto.write(SHOWING, mKeyguardState.showing);
diff --git a/services/core/java/com/android/server/policy/keyguard/KeyguardServiceWrapper.java b/services/core/java/com/android/server/policy/keyguard/KeyguardServiceWrapper.java
index 2029f86..774e261 100644
--- a/services/core/java/com/android/server/policy/keyguard/KeyguardServiceWrapper.java
+++ b/services/core/java/com/android/server/policy/keyguard/KeyguardServiceWrapper.java
@@ -17,6 +17,7 @@
package com.android.server.policy.keyguard;
import android.content.Context;
+import android.content.Intent;
import android.os.Bundle;
import android.os.IBinder;
import android.os.PowerManager;
@@ -254,6 +255,24 @@
}
}
+ @Override
+ public void dismissKeyguardToLaunch(Intent intentToLaunch) {
+ try {
+ mService.dismissKeyguardToLaunch(intentToLaunch);
+ } catch (RemoteException e) {
+ Slog.w(TAG , "Remote Exception", e);
+ }
+ }
+
+ @Override
+ public void onSystemKeyPressed(int keycode) {
+ try {
+ mService.onSystemKeyPressed(keycode);
+ } catch (RemoteException e) {
+ Slog.w(TAG , "Remote Exception", e);
+ }
+ }
+
@Override // Binder interface
public IBinder asBinder() {
return mService.asBinder();
diff --git a/services/core/java/com/android/server/soundtrigger_middleware/ObjectPrinter.java b/services/core/java/com/android/server/soundtrigger_middleware/ObjectPrinter.java
index 7f047f8..fba0fb4 100644
--- a/services/core/java/com/android/server/soundtrigger_middleware/ObjectPrinter.java
+++ b/services/core/java/com/android/server/soundtrigger_middleware/ObjectPrinter.java
@@ -20,8 +20,6 @@
import android.annotation.Nullable;
import java.lang.reflect.Array;
-import java.lang.reflect.Field;
-import java.lang.reflect.Modifier;
import java.util.Collection;
import java.util.Map;
@@ -33,67 +31,28 @@
static public final int kDefaultMaxCollectionLength = 16;
/**
- * Simple version of {@link #print(Object, boolean, int)} that prints an object, without
- * recursing into sub-objects.
- *
- * @param obj The object to print.
- * @return A string representing the object.
- */
- static String print(@Nullable Object obj) {
- return print(obj, false, kDefaultMaxCollectionLength);
- }
-
- /**
* Pretty-prints an object.
*
* @param obj The object to print.
- * @param deep Whether to pretty-print sub-objects (if false, just prints them
- * with {@link Object#toString()}).
* @param maxCollectionLength Whenever encountering collections, maximum number of elements to
* print.
* @return A string representing the object.
*/
- static String print(@Nullable Object obj, boolean deep, int maxCollectionLength) {
+ static String print(@Nullable Object obj, int maxCollectionLength) {
StringBuilder builder = new StringBuilder();
- print(builder, obj, deep, maxCollectionLength);
+ print(builder, obj, maxCollectionLength);
return builder.toString();
}
/**
- * This version is suitable for use inside a toString() override of an object, e.g.:
- * <pre><code>
- * class MyObject {
- * ...
- * @Override
- * String toString() {
- * return ObjectPrinter.printPublicFields(this, ...);
- * }
- * }
- * </code></pre>
- *
- * @param obj The object to print.
- * @param deep Whether to pretty-print sub-objects (if false, just prints them
- * with {@link Object#toString()}).
- * @param maxCollectionLength Whenever encountering collections, maximum number of elements to
- * print.
- */
- static String printPublicFields(@Nullable Object obj, boolean deep, int maxCollectionLength) {
- StringBuilder builder = new StringBuilder();
- printPublicFields(builder, obj, deep, maxCollectionLength);
- return builder.toString();
- }
-
- /**
- * A version of {@link #print(Object, boolean, int)} that uses a {@link StringBuilder}.
+ * A version of {@link #print(Object, int)} that uses a {@link StringBuilder}.
*
* @param builder StringBuilder to print into.
* @param obj The object to print.
- * @param deep Whether to pretty-print sub-objects (if false, just prints them
- * with {@link Object#toString()}).
* @param maxCollectionLength Whenever encountering collections, maximum number of elements to
* print.
*/
- static void print(@NonNull StringBuilder builder, @Nullable Object obj, boolean deep,
+ static void print(@NonNull StringBuilder builder, @Nullable Object obj,
int maxCollectionLength) {
try {
if (obj == null) {
@@ -101,16 +60,16 @@
return;
}
if (obj instanceof Boolean) {
- builder.append(obj.toString());
+ builder.append(obj);
return;
}
if (obj instanceof Number) {
- builder.append(obj.toString());
+ builder.append(obj);
return;
}
if (obj instanceof Character) {
builder.append('\'');
- builder.append(obj.toString());
+ builder.append(obj);
builder.append('\'');
return;
}
@@ -137,7 +96,7 @@
isLong = true;
break;
}
- print(builder, child, deep, maxCollectionLength);
+ print(builder, child, maxCollectionLength);
++i;
}
if (isLong) {
@@ -163,9 +122,9 @@
isLong = true;
break;
}
- print(builder, child.getKey(), deep, maxCollectionLength);
+ print(builder, child.getKey(), maxCollectionLength);
builder.append(": ");
- print(builder, child.getValue(), deep, maxCollectionLength);
+ print(builder, child.getValue(), maxCollectionLength);
++i;
}
if (isLong) {
@@ -189,7 +148,7 @@
isLong = true;
break;
}
- print(builder, Array.get(obj, i), deep, maxCollectionLength);
+ print(builder, Array.get(obj, i), maxCollectionLength);
}
if (isLong) {
builder.append("... (+");
@@ -200,48 +159,7 @@
return;
}
- if (!deep) {
- builder.append(obj.toString());
- return;
- }
- printPublicFields(builder, obj, deep, maxCollectionLength);
- } catch (Exception e) {
- throw new RuntimeException(e);
- }
- }
-
- /**
- * A version of {@link #printPublicFields(Object, boolean, int)} that uses a {@link
- * StringBuilder}.
- *
- * @param obj The object to print.
- * @param deep Whether to pretty-print sub-objects (if false, just prints them
- * with {@link Object#toString()}).
- * @param maxCollectionLength Whenever encountering collections, maximum number of elements to
- * print.
- */
- static void printPublicFields(@NonNull StringBuilder builder, @Nullable Object obj,
- boolean deep,
- int maxCollectionLength) {
- try {
- Class cls = obj.getClass();
- builder.append("{ ");
-
- boolean first = true;
- for (Field fld : cls.getDeclaredFields()) {
- int mod = fld.getModifiers();
- if ((mod & Modifier.PUBLIC) != 0 && (mod & Modifier.STATIC) == 0) {
- if (first) {
- first = false;
- } else {
- builder.append(", ");
- }
- builder.append(fld.getName());
- builder.append(": ");
- print(builder, fld.get(obj), deep, maxCollectionLength);
- }
- }
- builder.append(" }");
+ builder.append(obj);
} catch (Exception e) {
throw new RuntimeException(e);
}
diff --git a/services/core/java/com/android/server/soundtrigger_middleware/SoundTriggerMiddlewareLogging.java b/services/core/java/com/android/server/soundtrigger_middleware/SoundTriggerMiddlewareLogging.java
index 559e777..dc4bdaa 100644
--- a/services/core/java/com/android/server/soundtrigger_middleware/SoundTriggerMiddlewareLogging.java
+++ b/services/core/java/com/android/server/soundtrigger_middleware/SoundTriggerMiddlewareLogging.java
@@ -18,14 +18,14 @@
import android.annotation.NonNull;
import android.annotation.Nullable;
+import android.media.permission.Identity;
+import android.media.permission.IdentityContext;
import android.media.soundtrigger.ModelParameterRange;
import android.media.soundtrigger.PhraseRecognitionEvent;
import android.media.soundtrigger.PhraseSoundModel;
import android.media.soundtrigger.RecognitionConfig;
import android.media.soundtrigger.RecognitionEvent;
import android.media.soundtrigger.SoundModel;
-import android.media.permission.Identity;
-import android.media.permission.IdentityContext;
import android.media.soundtrigger_middleware.ISoundTriggerCallback;
import android.media.soundtrigger_middleware.ISoundTriggerModule;
import android.media.soundtrigger_middleware.SoundTriggerModuleDescriptor;
@@ -387,7 +387,7 @@
}
private static void printObject(@NonNull StringBuilder builder, @Nullable Object obj) {
- ObjectPrinter.print(builder, obj, true, 16);
+ ObjectPrinter.print(builder, obj, 16);
}
private static String printObject(@Nullable Object obj) {
diff --git a/services/core/java/com/android/server/soundtrigger_middleware/SoundTriggerMiddlewarePermission.java b/services/core/java/com/android/server/soundtrigger_middleware/SoundTriggerMiddlewarePermission.java
index 76927e1..f3d151f 100644
--- a/services/core/java/com/android/server/soundtrigger_middleware/SoundTriggerMiddlewarePermission.java
+++ b/services/core/java/com/android/server/soundtrigger_middleware/SoundTriggerMiddlewarePermission.java
@@ -21,9 +21,11 @@
import android.annotation.NonNull;
import android.annotation.Nullable;
-import android.app.AppOpsManager;
import android.content.Context;
import android.content.PermissionChecker;
+import android.media.permission.Identity;
+import android.media.permission.IdentityContext;
+import android.media.permission.PermissionUtil;
import android.media.soundtrigger.ModelParameterRange;
import android.media.soundtrigger.PhraseRecognitionEvent;
import android.media.soundtrigger.PhraseSoundModel;
@@ -31,9 +33,6 @@
import android.media.soundtrigger.RecognitionEvent;
import android.media.soundtrigger.SoundModel;
import android.media.soundtrigger.Status;
-import android.media.permission.Identity;
-import android.media.permission.IdentityContext;
-import android.media.permission.PermissionUtil;
import android.media.soundtrigger_middleware.ISoundTriggerCallback;
import android.media.soundtrigger_middleware.ISoundTriggerMiddlewareService;
import android.media.soundtrigger_middleware.ISoundTriggerModule;
@@ -144,7 +143,7 @@
if (status != PermissionChecker.PERMISSION_GRANTED) {
throw new SecurityException(
String.format("Failed to obtain permission %s for identity %s", permission,
- ObjectPrinter.print(identity, true, 16)));
+ ObjectPrinter.print(identity, 16)));
}
}
@@ -168,7 +167,7 @@
case PermissionChecker.PERMISSION_HARD_DENIED:
throw new SecurityException(
String.format("Failed to obtain permission %s for identity %s", permission,
- ObjectPrinter.print(identity, true, 16)));
+ ObjectPrinter.print(identity, 16)));
default:
throw new RuntimeException("Unexpected perimission check result.");
}
diff --git a/services/core/java/com/android/server/soundtrigger_middleware/SoundTriggerMiddlewareValidation.java b/services/core/java/com/android/server/soundtrigger_middleware/SoundTriggerMiddlewareValidation.java
index 4243fc7..09035cd 100644
--- a/services/core/java/com/android/server/soundtrigger_middleware/SoundTriggerMiddlewareValidation.java
+++ b/services/core/java/com/android/server/soundtrigger_middleware/SoundTriggerMiddlewareValidation.java
@@ -18,6 +18,8 @@
import android.annotation.NonNull;
import android.annotation.Nullable;
+import android.media.permission.Identity;
+import android.media.permission.IdentityContext;
import android.media.soundtrigger.ModelParameterRange;
import android.media.soundtrigger.PhraseRecognitionEvent;
import android.media.soundtrigger.PhraseSoundModel;
@@ -27,8 +29,6 @@
import android.media.soundtrigger.RecognitionStatus;
import android.media.soundtrigger.SoundModel;
import android.media.soundtrigger.Status;
-import android.media.permission.Identity;
-import android.media.permission.IdentityContext;
import android.media.soundtrigger_middleware.ISoundTriggerCallback;
import android.media.soundtrigger_middleware.ISoundTriggerMiddlewareService;
import android.media.soundtrigger_middleware.ISoundTriggerModule;
@@ -230,7 +230,7 @@
final ModuleState module = mModules.get(handle);
pw.println("=========================================");
pw.printf("Module %d\n%s\n", handle,
- ObjectPrinter.print(module.properties, true, 16));
+ ObjectPrinter.print(module.properties, 16));
pw.println("=========================================");
for (Session session : module.sessions) {
session.dump(pw);
@@ -250,11 +250,11 @@
/** State of a sound model. */
static class ModelState {
ModelState(SoundModel model) {
- this.description = ObjectPrinter.print(model, true, 16);
+ this.description = ObjectPrinter.print(model, 16);
}
ModelState(PhraseSoundModel model) {
- this.description = ObjectPrinter.print(model, true, 16);
+ this.description = ObjectPrinter.print(model, 16);
}
/** Activity state of a sound model. */
@@ -690,8 +690,8 @@
if (mState == ModuleStatus.ALIVE) {
pw.println("-------------------------------");
pw.printf("Session %s, client: %s\n", toString(),
- ObjectPrinter.print(mOriginatorIdentity, true, 16));
- pw.printf("Loaded models (handle, active, description):", toString());
+ ObjectPrinter.print(mOriginatorIdentity, 16));
+ pw.println("Loaded models (handle, active, description):");
pw.println();
pw.println("-------------------------------");
for (Map.Entry<Integer, ModelState> entry : mLoadedModels.entrySet()) {
diff --git a/services/core/java/com/android/server/tv/TvInputManagerService.java b/services/core/java/com/android/server/tv/TvInputManagerService.java
index 332fed7..568e4b8 100755
--- a/services/core/java/com/android/server/tv/TvInputManagerService.java
+++ b/services/core/java/com/android/server/tv/TvInputManagerService.java
@@ -44,6 +44,8 @@
import android.hardware.hdmi.HdmiControlManager;
import android.hardware.hdmi.HdmiDeviceInfo;
import android.media.PlaybackParams;
+import android.media.tv.AdRequest;
+import android.media.tv.AdResponse;
import android.media.tv.AitInfo;
import android.media.tv.BroadcastInfoRequest;
import android.media.tv.BroadcastInfoResponse;
@@ -2494,6 +2496,28 @@
}
@Override
+ public void requestAd(IBinder sessionToken, AdRequest request, int userId) {
+ final int callingUid = Binder.getCallingUid();
+ final int callingPid = Binder.getCallingPid();
+ final int resolvedUserId = resolveCallingUserId(callingPid, callingUid,
+ userId, "requestAd");
+ final long identity = Binder.clearCallingIdentity();
+ try {
+ synchronized (mLock) {
+ try {
+ SessionState sessionState = getSessionStateLocked(sessionToken, callingUid,
+ resolvedUserId);
+ getSessionLocked(sessionState).requestAd(request);
+ } catch (RemoteException | SessionNotFoundException e) {
+ Slog.e(TAG, "error in requestAd", e);
+ }
+ }
+ } finally {
+ Binder.restoreCallingIdentity(identity);
+ };
+ }
+
+ @Override
public int getClientPid(String sessionId) {
ensureTunerResourceAccessPermission();
final long identity = Binder.clearCallingIdentity();
@@ -3557,6 +3581,23 @@
}
}
}
+
+ @Override
+ public void onAdResponse (AdResponse response) {
+ synchronized (mLock) {
+ if (DEBUG) {
+ Slog.d(TAG, "onAdResponse()");
+ }
+ if (mSessionState.session == null || mSessionState.client == null) {
+ return;
+ }
+ try {
+ mSessionState.client.onAdResponse(response, mSessionState.seq);
+ } catch (RemoteException e) {
+ Slog.e(TAG, "error in onAdResponse", e);
+ }
+ }
+ }
}
@VisibleForTesting
diff --git a/services/core/java/com/android/server/tv/interactive/TvIAppManagerService.java b/services/core/java/com/android/server/tv/interactive/TvIAppManagerService.java
index 02a56ec..a2bf2fe 100644
--- a/services/core/java/com/android/server/tv/interactive/TvIAppManagerService.java
+++ b/services/core/java/com/android/server/tv/interactive/TvIAppManagerService.java
@@ -29,6 +29,8 @@
import android.content.pm.ServiceInfo;
import android.content.pm.UserInfo;
import android.graphics.Rect;
+import android.media.tv.AdRequest;
+import android.media.tv.AdResponse;
import android.media.tv.BroadcastInfoRequest;
import android.media.tv.BroadcastInfoResponse;
import android.media.tv.TvTrackInfo;
@@ -158,6 +160,7 @@
}
iAppState.mInfo = info;
iAppState.mUid = getIAppUid(info);
+ iAppState.mComponentName = info.getComponent();
iAppMap.put(iAppServiceId, iAppState);
iAppState.mIAppNumber = count;
}
@@ -1177,6 +1180,28 @@
}
@Override
+ public void notifyAdResponse(IBinder sessionToken, AdResponse response, int userId) {
+ final int callingUid = Binder.getCallingUid();
+ final int callingPid = Binder.getCallingPid();
+ final int resolvedUserId = resolveCallingUserId(callingPid, callingUid, userId,
+ "notifyAdResponse");
+ final long identity = Binder.clearCallingIdentity();
+ try {
+ synchronized (mLock) {
+ try {
+ SessionState sessionState = getSessionStateLocked(sessionToken, callingUid,
+ resolvedUserId);
+ getSessionLocked(sessionState).notifyAdResponse(response);
+ } catch (RemoteException | SessionNotFoundException e) {
+ Slogf.e(TAG, "error in notifyAdResponse", e);
+ }
+ }
+ } finally {
+ Binder.restoreCallingIdentity(identity);
+ }
+ }
+
+ @Override
public void registerCallback(final ITvIAppManagerCallback callback, int userId) {
int callingPid = Binder.getCallingPid();
int callingUid = Binder.getCallingUid();
@@ -1907,6 +1932,23 @@
}
@Override
+ public void onAdRequest(AdRequest request) {
+ synchronized (mLock) {
+ if (DEBUG) {
+ Slogf.d(TAG, "onAdRequest (id=" + request.getId() + ")");
+ }
+ if (mSessionState.mSession == null || mSessionState.mClient == null) {
+ return;
+ }
+ try {
+ mSessionState.mClient.onAdRequest(request, mSessionState.mSeq);
+ } catch (RemoteException e) {
+ Slogf.e(TAG, "error in onAdRequest", e);
+ }
+ }
+ }
+
+ @Override
public void onSessionStateChanged(int state) {
synchronized (mLock) {
if (DEBUG) {
diff --git a/services/core/java/com/android/server/wm/ActivityRecord.java b/services/core/java/com/android/server/wm/ActivityRecord.java
index fcc2bd6..0b0b704 100644
--- a/services/core/java/com/android/server/wm/ActivityRecord.java
+++ b/services/core/java/com/android/server/wm/ActivityRecord.java
@@ -6337,7 +6337,6 @@
}
} else if (w.isDrawn()) {
// The starting window for this container is drawn.
- mTaskSupervisor.getActivityMetricsLogger().notifyStartingWindowDrawn(this);
startingDisplayed = true;
}
}
@@ -6906,8 +6905,7 @@
getSyncTransaction().hide(mSurfaceControl);
}
if (show) {
- mActivityRecordInputSink.applyChangesToSurfaceIfChanged(
- getSyncTransaction(), mSurfaceControl);
+ mActivityRecordInputSink.applyChangesToSurfaceIfChanged(getSyncTransaction());
}
}
if (mThumbnail != null) {
diff --git a/services/core/java/com/android/server/wm/ActivityRecordInputSink.java b/services/core/java/com/android/server/wm/ActivityRecordInputSink.java
index bce2883..9353f6d 100644
--- a/services/core/java/com/android/server/wm/ActivityRecordInputSink.java
+++ b/services/core/java/com/android/server/wm/ActivityRecordInputSink.java
@@ -60,7 +60,7 @@
// Hold on to InputEventReceiver to prevent it from getting GCd.
private InputEventReceiver mInputEventReceiver;
private InputWindowHandleWrapper mInputWindowHandleWrapper;
-
+ private SurfaceControl mSurfaceControl;
private int mRapidTouchCount = 0;
private IBinder mToken;
private boolean mDisabled = false;
@@ -73,12 +73,25 @@
+ mActivityRecord.mActivityComponent.getShortClassName();
}
- public void applyChangesToSurfaceIfChanged(
- SurfaceControl.Transaction transaction, SurfaceControl surfaceControl) {
+ public void applyChangesToSurfaceIfChanged(SurfaceControl.Transaction transaction) {
InputWindowHandleWrapper inputWindowHandleWrapper = getInputWindowHandleWrapper();
- if (inputWindowHandleWrapper.isChanged()) {
- inputWindowHandleWrapper.applyChangesToSurface(transaction, surfaceControl);
+ if (mSurfaceControl == null) {
+ mSurfaceControl = createSurface(transaction);
}
+ if (inputWindowHandleWrapper.isChanged()) {
+ inputWindowHandleWrapper.applyChangesToSurface(transaction, mSurfaceControl);
+ }
+ }
+
+ private SurfaceControl createSurface(SurfaceControl.Transaction t) {
+ SurfaceControl surfaceControl = mActivityRecord.makeChildSurface(null)
+ .setName(mName)
+ .setHidden(false)
+ .setCallsite("ActivityRecordInputSink.createSurface")
+ .build();
+ // Put layer below all siblings (and the parent surface too)
+ t.setLayer(surfaceControl, Integer.MIN_VALUE);
+ return surfaceControl;
}
private InputWindowHandleWrapper getInputWindowHandleWrapper() {
diff --git a/services/core/java/com/android/server/wm/ActivityTaskSupervisor.java b/services/core/java/com/android/server/wm/ActivityTaskSupervisor.java
index 51c8daf..3cecce2 100644
--- a/services/core/java/com/android/server/wm/ActivityTaskSupervisor.java
+++ b/services/core/java/com/android/server/wm/ActivityTaskSupervisor.java
@@ -641,22 +641,35 @@
intent.setComponent(new ComponentName(
aInfo.applicationInfo.packageName, aInfo.name));
- // Don't debug things in the system process
- if (!aInfo.processName.equals("system")) {
- if ((startFlags & (START_FLAG_DEBUG | START_FLAG_NATIVE_DEBUGGING
- | START_FLAG_TRACK_ALLOCATION)) != 0 || profilerInfo != null) {
-
+ final boolean requestDebug = (startFlags & (START_FLAG_DEBUG
+ | START_FLAG_NATIVE_DEBUGGING | START_FLAG_TRACK_ALLOCATION)) != 0;
+ final boolean requestProfile = profilerInfo != null;
+ if (requestDebug || requestProfile) {
+ final boolean debuggable = (Build.IS_DEBUGGABLE
+ || (aInfo.applicationInfo.flags & ApplicationInfo.FLAG_DEBUGGABLE) != 0)
+ && !aInfo.processName.equals("system");
+ if ((requestDebug && !debuggable) || (requestProfile
+ && (!debuggable && !aInfo.applicationInfo.isProfileableByShell()))) {
+ Slog.w(TAG, "Ignore debugging for non-debuggable app: " + aInfo.packageName);
+ } else {
// Mimic an AMS synchronous call by passing a message to AMS and wait for AMS
// to notify us that the task has completed.
// TODO(b/80414790) look into further untangling for the situation where the
// caller is on the same thread as the handler we are posting to.
synchronized (mService.mGlobalLock) {
// Post message to AMS.
- final Message msg = PooledLambda.obtainMessage(
- ActivityManagerInternal::setDebugFlagsForStartingActivity,
- mService.mAmInternal, aInfo, startFlags, profilerInfo,
- mService.mGlobalLock);
- mService.mH.sendMessage(msg);
+ mService.mH.post(() -> {
+ try {
+ mService.mAmInternal.setDebugFlagsForStartingActivity(aInfo,
+ startFlags, profilerInfo, mService.mGlobalLock);
+ } catch (Throwable e) {
+ // Simply ignore it because the debugging doesn't take effect.
+ Slog.w(TAG, e);
+ synchronized (mService.mGlobalLockWithoutBoost) {
+ mService.mGlobalLockWithoutBoost.notifyAll();
+ }
+ }
+ });
try {
mService.mGlobalLock.wait();
} catch (InterruptedException ignore) {
diff --git a/services/core/java/com/android/server/wm/BLASTSyncEngine.java b/services/core/java/com/android/server/wm/BLASTSyncEngine.java
index 3ce6b51..bb4519c 100644
--- a/services/core/java/com/android/server/wm/BLASTSyncEngine.java
+++ b/services/core/java/com/android/server/wm/BLASTSyncEngine.java
@@ -59,7 +59,6 @@
*/
class BLASTSyncEngine {
private static final String TAG = "BLASTSyncEngine";
- private static final String TRACE_NAME_SYNC_GROUP_READY = "SyncGroupReady";
interface TransactionReadyListener {
void onTransactionReady(int mSyncId, SurfaceControl.Transaction transaction);
@@ -75,6 +74,7 @@
boolean mReady = false;
final ArraySet<WindowContainer> mRootMembers = new ArraySet<>();
private SurfaceControl.Transaction mOrphanTransaction = null;
+ private String mTraceName;
private SyncGroup(TransactionReadyListener listener, int id, String name) {
mSyncId = id;
@@ -86,8 +86,8 @@
}
};
if (Trace.isTagEnabled(TRACE_TAG_WINDOW_MANAGER)) {
- Trace.asyncTraceBegin(TRACE_TAG_WINDOW_MANAGER,
- name + TRACE_NAME_SYNC_GROUP_READY, id);
+ mTraceName = name + "SyncGroupReady";
+ Trace.asyncTraceBegin(TRACE_TAG_WINDOW_MANAGER, mTraceName, id);
}
}
@@ -121,7 +121,9 @@
}
private void finishNow() {
- Trace.asyncTraceEnd(TRACE_TAG_WINDOW_MANAGER, TRACE_NAME_SYNC_GROUP_READY, mSyncId);
+ if (mTraceName != null) {
+ Trace.asyncTraceEnd(TRACE_TAG_WINDOW_MANAGER, mTraceName, mSyncId);
+ }
ProtoLog.v(WM_DEBUG_SYNC_ENGINE, "SyncGroup %d: Finished!", mSyncId);
SurfaceControl.Transaction merged = mWm.mTransactionFactory.get();
if (mOrphanTransaction != null) {
diff --git a/services/core/java/com/android/server/wm/DisplayContent.java b/services/core/java/com/android/server/wm/DisplayContent.java
index 8ede016..c740f7b 100644
--- a/services/core/java/com/android/server/wm/DisplayContent.java
+++ b/services/core/java/com/android/server/wm/DisplayContent.java
@@ -1467,7 +1467,7 @@
@Override
boolean onDescendantOrientationChanged(WindowContainer requestingContainer) {
final Configuration config = updateOrientation(
- getRequestedOverrideConfiguration(), requestingContainer, false /* forceUpdate */);
+ requestingContainer, false /* forceUpdate */);
// If display rotation class tells us that it doesn't consider app requested orientation,
// this display won't rotate just because of an app changes its requested orientation. Thus
// it indicates that this display chooses not to handle this request.
@@ -1516,14 +1516,10 @@
* DisplayContent)} to tell the window manager it can unfreeze the screen. This will typically
* be done by calling {@link #sendNewConfiguration}.
*
- * @param currentConfig The current requested override configuration (it is usually set from
- * the last {@link #sendNewConfiguration}) of the display. It is used to
- * check if the configuration container has the latest state.
* @param freezeDisplayWindow Freeze the app window if the orientation is changed.
* @param forceUpdate See {@link DisplayRotation#updateRotationUnchecked(boolean)}
*/
- Configuration updateOrientation(Configuration currentConfig,
- WindowContainer freezeDisplayWindow, boolean forceUpdate) {
+ Configuration updateOrientation(WindowContainer<?> freezeDisplayWindow, boolean forceUpdate) {
if (!mDisplayReady) {
return null;
}
@@ -1540,15 +1536,15 @@
}
config = new Configuration();
computeScreenConfiguration(config);
- } else if (currentConfig != null
+ } else if (!(mTransitionController.isCollecting(this)
// If waiting for a remote rotation, don't prematurely update configuration.
- && !(mDisplayRotation.isWaitingForRemoteRotation()
- || mTransitionController.isCollecting(this))) {
+ || mDisplayRotation.isWaitingForRemoteRotation())) {
// No obvious action we need to take, but if our current state mismatches the
// activity manager's, update it, disregarding font scale, which should remain set
// to the value of the previous configuration.
// Here we're calling Configuration#unset() instead of setToDefaults() because we
// need to keep override configs clear of non-empty values (e.g. fontSize).
+ final Configuration currentConfig = getRequestedOverrideConfiguration();
mTmpConfiguration.unset();
mTmpConfiguration.updateFrom(currentConfig);
computeScreenConfiguration(mTmpConfiguration);
@@ -1598,6 +1594,9 @@
*/
@Rotation
int rotationForActivityInDifferentOrientation(@NonNull ActivityRecord r) {
+ if (mTransitionController.isShellTransitionsEnabled()) {
+ return ROTATION_UNDEFINED;
+ }
if (!WindowManagerService.ENABLE_FIXED_ROTATION_TRANSFORM) {
return ROTATION_UNDEFINED;
}
diff --git a/services/core/java/com/android/server/wm/FadeRotationAnimationController.java b/services/core/java/com/android/server/wm/FadeRotationAnimationController.java
index bbda577..c85e04d 100644
--- a/services/core/java/com/android/server/wm/FadeRotationAnimationController.java
+++ b/services/core/java/com/android/server/wm/FadeRotationAnimationController.java
@@ -59,8 +59,13 @@
/** The list to store the drawn tokens before the rotation animation starts. */
private ArrayList<WindowToken> mPendingShowTokens;
- /** It is used when the display has rotated, but some windows fade out in old rotation. */
- private SeamlessRotator mRotator;
+ /**
+ * The sync transactions of the target windows. It is used when the display has rotated but
+ * the windows need to fade out in previous rotation. These transactions will be applied with
+ * fade-in animation, so there won't be a flickering such as the windows have redrawn during
+ * fading out.
+ */
+ private ArrayMap<WindowState, SurfaceControl.Transaction> mCapturedDrawTransactions;
private final int mOriginalRotation;
private final boolean mHasScreenRotationAnimation;
@@ -110,16 +115,36 @@
mTargetWindowTokens.put(w.mToken, null);
}
}, true /* traverseTopToBottom */);
+
+ // The transition sync group may be finished earlier because it doesn't wait for these
+ // target windows. But the windows still need to use sync transaction to keep the appearance
+ // in previous rotation, so request a no-op sync to keep the state.
+ if (!mIsChangeTransition && transitionType != WindowManager.TRANSIT_NONE) {
+ for (int i = mTargetWindowTokens.size() - 1; i >= 0; i--) {
+ final WindowToken token = mTargetWindowTokens.keyAt(i);
+ for (int j = token.getChildCount() - 1; j >= 0; j--) {
+ token.getChildAt(j).applyWithNextDraw(t -> {});
+ }
+ }
+ }
}
@Override
public void fadeWindowToken(boolean show, WindowToken windowToken, int animationType) {
if (show) {
- final SurfaceControl leash = mTargetWindowTokens.remove(windowToken);
- if (leash != null && mRotator != null) {
- // The leash was unrotated by start transaction of transition. Clear the transform
- // to reshow the window in current rotation.
- mRotator.setIdentityMatrix(mDisplayContent.getPendingTransaction(), leash);
+ // The previous animation leash will be dropped when preparing fade-in animation, so
+ // simply remove it without restoring the transformation.
+ mTargetWindowTokens.remove(windowToken);
+ if (mCapturedDrawTransactions != null) {
+ // Unblock the window to draw its latest content with fade-in animation.
+ final SurfaceControl.Transaction t = mDisplayContent.getPendingTransaction();
+ for (int i = windowToken.getChildCount() - 1; i >= 0; i--) {
+ final SurfaceControl.Transaction drawT =
+ mCapturedDrawTransactions.remove(windowToken.getChildAt(i));
+ if (drawT != null) {
+ t.merge(drawT);
+ }
+ }
}
}
super.fadeWindowToken(show, windowToken, animationType);
@@ -225,14 +250,14 @@
// Take OPEN/CLOSE transition type as the example, the non-activity windows need to
// fade out in previous rotation while display has rotated to the new rotation, so
// their leashes are unrotated with the start transaction.
- mRotator = new SeamlessRotator(mOriginalRotation,
+ final SeamlessRotator rotator = new SeamlessRotator(mOriginalRotation,
mDisplayContent.getWindowConfiguration().getRotation(),
mDisplayContent.getDisplayInfo(),
false /* applyFixedTransformationHint */);
for (int i = mTargetWindowTokens.size() - 1; i >= 0; i--) {
final SurfaceControl leash = mTargetWindowTokens.valueAt(i);
if (leash != null) {
- mRotator.applyTransform(t, leash);
+ rotator.applyTransform(t, leash);
}
}
return;
@@ -280,6 +305,25 @@
}
}
+ /** Captures the post draw transaction if the window should update with fade-in animation. */
+ boolean handleFinishDrawing(WindowState w, SurfaceControl.Transaction postDrawTransaction) {
+ if (mIsChangeTransition || !isTargetToken(w.mToken)) return false;
+ if (postDrawTransaction != null && w.mTransitionController.inTransition()) {
+ if (mCapturedDrawTransactions == null) {
+ mCapturedDrawTransactions = new ArrayMap<>();
+ }
+ final SurfaceControl.Transaction t = mCapturedDrawTransactions.get(w);
+ if (t == null) {
+ mCapturedDrawTransactions.put(w, postDrawTransaction);
+ } else {
+ t.merge(postDrawTransaction);
+ }
+ return true;
+ }
+ mDisplayContent.finishFadeRotationAnimation(w.mToken);
+ return false;
+ }
+
@Override
public Animation getFadeInAnimation() {
if (mHasScreenRotationAnimation) {
diff --git a/services/core/java/com/android/server/wm/RootWindowContainer.java b/services/core/java/com/android/server/wm/RootWindowContainer.java
index dcb28d2..535bbb7 100644
--- a/services/core/java/com/android/server/wm/RootWindowContainer.java
+++ b/services/core/java/com/android/server/wm/RootWindowContainer.java
@@ -1037,13 +1037,7 @@
}
}
- final boolean curDisplayInTransitNotAnimate =
- // legacy transition
- (curDisplay.mAppTransition.isRunning() && !curDisplay.isAppTransitioning())
- // shell transition
- || (curDisplay.mTransitionController.isShellTransitionsEnabled()
- && !curDisplay.mTransitionController.isPlaying());
- if (curDisplayInTransitNotAnimate) {
+ if (curDisplay.mAppTransition.isRunning() && !curDisplay.isAppTransitioning()) {
// We have finished the animation of an app transition. To do this, we have
// delayed a lot of operations like showing and hiding apps, moving apps in
// Z-order, etc.
@@ -1823,8 +1817,7 @@
final DisplayContent displayContent = getDisplayContent(displayId);
Configuration config = null;
if (displayContent != null) {
- config = displayContent.updateOrientation(
- getDisplayOverrideConfiguration(displayId), starting, true /* forceUpdate */);
+ config = displayContent.updateOrientation(starting, true /* forceUpdate */);
}
// Visibilities may change so let the starting activity have a chance to report. Can't do it
// when visibility is changed in each AppWindowToken because it may trigger wrong
@@ -2561,24 +2554,6 @@
mDisplayManagerInternal.setDisplayAccessUIDs(mDisplayAccessUIDs);
}
- Configuration getDisplayOverrideConfiguration(int displayId) {
- final DisplayContent displayContent = getDisplayContentOrCreate(displayId);
- if (displayContent == null) {
- throw new IllegalArgumentException("No display found with id: " + displayId);
- }
-
- return displayContent.getRequestedOverrideConfiguration();
- }
-
- void setDisplayOverrideConfiguration(Configuration overrideConfiguration, int displayId) {
- final DisplayContent displayContent = getDisplayContentOrCreate(displayId);
- if (displayContent == null) {
- throw new IllegalArgumentException("No display found with id: " + displayId);
- }
-
- displayContent.onRequestedOverrideConfigurationChanged(overrideConfiguration);
- }
-
void prepareForShutdown() {
for (int i = 0; i < getChildCount(); i++) {
createSleepToken("shutdown", getChildAt(i).mDisplayId);
diff --git a/services/core/java/com/android/server/wm/RunningTasks.java b/services/core/java/com/android/server/wm/RunningTasks.java
index 1533245..d31b007 100644
--- a/services/core/java/com/android/server/wm/RunningTasks.java
+++ b/services/core/java/com/android/server/wm/RunningTasks.java
@@ -58,6 +58,7 @@
private boolean mAllowed;
private boolean mFilterOnlyVisibleRecents;
private Task mTopDisplayFocusRootTask;
+ private Task mTopDisplayAdjacentTask;
private RecentTasks mRecentTasks;
private boolean mKeepIntentExtra;
@@ -81,6 +82,12 @@
mRecentTasks = root.mService.getRecentTasks();
mKeepIntentExtra = (flags & FLAG_KEEP_INTENT_EXTRA) == FLAG_KEEP_INTENT_EXTRA;
+ if (mTopDisplayFocusRootTask.getAdjacentTaskFragment() != null) {
+ mTopDisplayAdjacentTask = mTopDisplayFocusRootTask.getAdjacentTaskFragment().asTask();
+ } else {
+ mTopDisplayAdjacentTask = null;
+ }
+
final PooledConsumer c = PooledLambda.obtainConsumer(RunningTasks::processTask, this,
PooledLambda.__(Task.class));
root.forAllLeafTasks(c, false);
@@ -130,6 +137,12 @@
// can be used to determine the order of the tasks (it may not be set for newly
// created tasks)
task.touchActiveTime();
+ } else if (rootTask == mTopDisplayAdjacentTask && rootTask.getTopMostTask() == task) {
+ // The short-term workaround for launcher could get suitable running task info in
+ // split screen.
+ task.touchActiveTime();
+ // TreeSet doesn't allow same value and make sure this task is lower than focus one.
+ task.lastActiveTime--;
}
mTmpSortedSet.add(task);
diff --git a/services/core/java/com/android/server/wm/SplashScreenExceptionList.java b/services/core/java/com/android/server/wm/SplashScreenExceptionList.java
index e815a0e..9ca49fe 100644
--- a/services/core/java/com/android/server/wm/SplashScreenExceptionList.java
+++ b/services/core/java/com/android/server/wm/SplashScreenExceptionList.java
@@ -64,7 +64,8 @@
mOnPropertiesChangedListener);
}
- private void updateDeviceConfig(String values) {
+ @VisibleForTesting
+ void updateDeviceConfig(String values) {
parseDeviceConfigPackageList(values);
}
diff --git a/services/core/java/com/android/server/wm/Transition.java b/services/core/java/com/android/server/wm/Transition.java
index 1c40044..a477108 100644
--- a/services/core/java/com/android/server/wm/Transition.java
+++ b/services/core/java/com/android/server/wm/Transition.java
@@ -497,6 +497,11 @@
if (fadeRotationController != null) {
fadeRotationController.onTransitionFinished();
}
+ // Transient-launch activities cannot be IME target (WindowState#canBeImeTarget),
+ // so re-compute in case the IME target is changed after transition.
+ if (mTransientLaunches != null) {
+ mTargetDisplay.computeImeTarget(true /* updateImeTarget */);
+ }
}
void abort() {
diff --git a/services/core/java/com/android/server/wm/WindowContainer.java b/services/core/java/com/android/server/wm/WindowContainer.java
index bef7810..61acb97 100644
--- a/services/core/java/com/android/server/wm/WindowContainer.java
+++ b/services/core/java/com/android/server/wm/WindowContainer.java
@@ -3354,8 +3354,12 @@
for (int i = mChildren.size() - 1; i >= 0; --i) {
mChildren.get(i).finishSync(outMergedTransaction, cancel);
}
- mSyncState = SYNC_STATE_NONE;
if (cancel && mSyncGroup != null) mSyncGroup.onCancelSync(this);
+ clearSyncState();
+ }
+
+ void clearSyncState() {
+ mSyncState = SYNC_STATE_NONE;
mSyncGroup = null;
}
diff --git a/services/core/java/com/android/server/wm/WindowManagerService.java b/services/core/java/com/android/server/wm/WindowManagerService.java
index fc1fd92..7be128b 100644
--- a/services/core/java/com/android/server/wm/WindowManagerService.java
+++ b/services/core/java/com/android/server/wm/WindowManagerService.java
@@ -2548,10 +2548,8 @@
if (win.mAttrs.type == TYPE_APPLICATION_STARTING) {
transit = WindowManagerPolicy.TRANSIT_PREVIEW_DONE;
}
- if (win.inTransition()) {
- focusMayChange = true;
- win.mAnimatingExit = true;
- } else if (win.isWinVisibleLw() && winAnimator.applyAnimationLocked(transit, false)) {
+
+ if (win.isWinVisibleLw() && winAnimator.applyAnimationLocked(transit, false)) {
focusMayChange = true;
win.mAnimatingExit = true;
} else if (win.mDisplayContent.okToAnimate() && win.isAnimating(TRANSITION | PARENTS,
diff --git a/services/core/java/com/android/server/wm/WindowState.java b/services/core/java/com/android/server/wm/WindowState.java
index f6729c5..863e3ca 100644
--- a/services/core/java/com/android/server/wm/WindowState.java
+++ b/services/core/java/com/android/server/wm/WindowState.java
@@ -5427,7 +5427,7 @@
@Override
void assignLayer(Transaction t, int layer) {
- if (isStartingWindowAssociatedToTask()) {
+ if (mStartingData != null) {
// The starting window should cover the task.
t.setLayer(mSurfaceControl, Integer.MAX_VALUE);
return;
@@ -5747,29 +5747,36 @@
Slog.i(TAG, "finishDrawing of relaunch: " + this + " " + duration + "ms");
mActivityRecord.mRelaunchStartTime = 0;
}
-
- executeDrawHandlers(postDrawTransaction);
-
- final boolean applyPostDrawNow = mClientWasDrawingForSync && postDrawTransaction != null;
- mClientWasDrawingForSync = false;
- if (!onSyncFinishedDrawing()) {
- return mWinAnimator.finishDrawingLocked(postDrawTransaction, applyPostDrawNow);
- }
-
- if (mActivityRecord != null
- && mTransitionController.isShellTransitionsEnabled()
- && mAttrs.type == TYPE_APPLICATION_STARTING) {
+ if (mActivityRecord != null && mAttrs.type == TYPE_APPLICATION_STARTING) {
mWmService.mAtmService.mTaskSupervisor.getActivityMetricsLogger()
.notifyStartingWindowDrawn(mActivityRecord);
}
- if (postDrawTransaction != null) {
+ final boolean hasSyncHandlers = executeDrawHandlers(postDrawTransaction);
+
+ boolean skipLayout = false;
+ // Control the timing to switch the appearance of window with different rotations.
+ final FadeRotationAnimationController fadeRotationController =
+ mDisplayContent.getFadeRotationAnimationController();
+ if (fadeRotationController != null
+ && fadeRotationController.handleFinishDrawing(this, postDrawTransaction)) {
+ // Consume the transaction because the controller will apply it with fade animation.
+ // Layout is not needed because the window will be hidden by the fade leash. Clear
+ // sync state because its sync transaction doesn't need to be merged to sync group.
+ postDrawTransaction = null;
+ skipLayout = true;
+ clearSyncState();
+ } else if (onSyncFinishedDrawing() && postDrawTransaction != null) {
mSyncTransaction.merge(postDrawTransaction);
+ // Consume the transaction because the sync group will merge it.
+ postDrawTransaction = null;
}
- mWinAnimator.finishDrawingLocked(null, false /* forceApplyNow */);
+ final boolean layoutNeeded =
+ mWinAnimator.finishDrawingLocked(postDrawTransaction, mClientWasDrawingForSync);
+ mClientWasDrawingForSync = false;
// We always want to force a traversal after a finish draw for blast sync.
- return true;
+ return !skipLayout && (hasSyncHandlers || layoutNeeded);
}
void immediatelyNotifyBlastSync() {
diff --git a/services/core/jni/com_android_server_location_GnssLocationProvider.cpp b/services/core/jni/com_android_server_location_GnssLocationProvider.cpp
index 826171a..f66f119 100644
--- a/services/core/jni/com_android_server_location_GnssLocationProvider.cpp
+++ b/services/core/jni/com_android_server_location_GnssLocationProvider.cpp
@@ -1806,15 +1806,20 @@
}
static jboolean android_location_gnss_hal_GnssNative_start_measurement_collection(
- JNIEnv* /* env */, jclass, jboolean enableFullTracking, jboolean enableCorrVecOutputs) {
+ JNIEnv* /* env */, jclass, jboolean enableFullTracking, jboolean enableCorrVecOutputs,
+ jint intervalMs) {
if (gnssMeasurementIface == nullptr) {
ALOGE("%s: IGnssMeasurement interface not available.", __func__);
return JNI_FALSE;
}
+ hardware::gnss::IGnssMeasurementInterface::Options options;
+ options.enableFullTracking = enableFullTracking;
+ options.enableCorrVecOutputs = enableCorrVecOutputs;
+ options.intervalMs = intervalMs;
return gnssMeasurementIface->setCallback(std::make_unique<gnss::GnssMeasurementCallback>(
mCallbacksObj),
- enableFullTracking, enableCorrVecOutputs);
+ options);
}
static jboolean android_location_gnss_hal_GnssNative_stop_measurement_collection(JNIEnv* env,
@@ -2269,7 +2274,7 @@
/* name, signature, funcPtr */
{"native_is_measurement_supported", "()Z",
reinterpret_cast<void*>(android_location_gnss_hal_GnssNative_is_measurement_supported)},
- {"native_start_measurement_collection", "(ZZ)Z",
+ {"native_start_measurement_collection", "(ZZI)Z",
reinterpret_cast<void*>(
android_location_gnss_hal_GnssNative_start_measurement_collection)},
{"native_stop_measurement_collection", "()Z",
diff --git a/services/core/jni/gnss/GnssMeasurement.cpp b/services/core/jni/gnss/GnssMeasurement.cpp
index 663d839..9fbf259 100644
--- a/services/core/jni/gnss/GnssMeasurement.cpp
+++ b/services/core/jni/gnss/GnssMeasurement.cpp
@@ -50,9 +50,15 @@
: mIGnssMeasurement(iGnssMeasurement) {}
jboolean GnssMeasurement::setCallback(const std::unique_ptr<GnssMeasurementCallback>& callback,
- bool enableFullTracking, bool enableCorrVecOutputs) {
- auto status = mIGnssMeasurement->setCallback(callback->getAidl(), enableFullTracking,
- enableCorrVecOutputs);
+ const IGnssMeasurementInterface::Options& options) {
+ if (mIGnssMeasurement->getInterfaceVersion() >= 2) {
+ auto status = mIGnssMeasurement->setCallbackWithOptions(callback->getAidl(), options);
+ if (checkAidlStatus(status, "IGnssMeasurement setCallbackWithOptions() failed.")) {
+ return true;
+ }
+ }
+ auto status = mIGnssMeasurement->setCallback(callback->getAidl(), options.enableFullTracking,
+ options.enableCorrVecOutputs);
return checkAidlStatus(status, "IGnssMeasurement setCallback() failed.");
}
@@ -67,13 +73,16 @@
: mIGnssMeasurement_V1_0(iGnssMeasurement) {}
jboolean GnssMeasurement_V1_0::setCallback(const std::unique_ptr<GnssMeasurementCallback>& callback,
- bool enableFullTracking, bool enableCorrVecOutputs) {
- if (enableFullTracking == true) {
+ const IGnssMeasurementInterface::Options& options) {
+ if (options.enableFullTracking == true) {
ALOGW("Full tracking mode is not supported in 1.0 GNSS HAL.");
}
- if (enableCorrVecOutputs == true) {
+ if (options.enableCorrVecOutputs == true) {
ALOGW("Correlation vector output is not supported in 1.0 GNSS HAL.");
}
+ if (options.intervalMs > 1000) {
+ ALOGW("Measurement interval is not supported in 1.0 GNSS HAL.");
+ }
auto status = mIGnssMeasurement_V1_0->setCallback(callback->getHidl());
if (!checkHidlReturn(status, "IGnssMeasurement setCallback() failed.")) {
return JNI_FALSE;
@@ -93,11 +102,15 @@
: GnssMeasurement_V1_0{iGnssMeasurement}, mIGnssMeasurement_V1_1(iGnssMeasurement) {}
jboolean GnssMeasurement_V1_1::setCallback(const std::unique_ptr<GnssMeasurementCallback>& callback,
- bool enableFullTracking, bool enableCorrVecOutputs) {
- if (enableCorrVecOutputs == true) {
+ const IGnssMeasurementInterface::Options& options) {
+ if (options.enableCorrVecOutputs == true) {
ALOGW("Correlation vector output is not supported in 1.1 GNSS HAL.");
}
- auto status = mIGnssMeasurement_V1_1->setCallback_1_1(callback->getHidl(), enableFullTracking);
+ if (options.intervalMs > 1000) {
+ ALOGW("Measurement interval is not supported in 1.0 GNSS HAL.");
+ }
+ auto status = mIGnssMeasurement_V1_1->setCallback_1_1(callback->getHidl(),
+ options.enableFullTracking);
if (!checkHidlReturn(status, "IGnssMeasurement setCallback_V1_1() failed.")) {
return JNI_FALSE;
}
@@ -111,11 +124,15 @@
: GnssMeasurement_V1_1{iGnssMeasurement}, mIGnssMeasurement_V2_0(iGnssMeasurement) {}
jboolean GnssMeasurement_V2_0::setCallback(const std::unique_ptr<GnssMeasurementCallback>& callback,
- bool enableFullTracking, bool enableCorrVecOutputs) {
- if (enableCorrVecOutputs == true) {
+ const IGnssMeasurementInterface::Options& options) {
+ if (options.enableCorrVecOutputs == true) {
ALOGW("Correlation vector output is not supported in 2.0 GNSS HAL.");
}
- auto status = mIGnssMeasurement_V2_0->setCallback_2_0(callback->getHidl(), enableFullTracking);
+ if (options.intervalMs > 1000) {
+ ALOGW("Measurement interval is not supported in 1.0 GNSS HAL.");
+ }
+ auto status = mIGnssMeasurement_V2_0->setCallback_2_0(callback->getHidl(),
+ options.enableFullTracking);
if (!checkHidlReturn(status, "IGnssMeasurement setCallback_2_0() failed.")) {
return JNI_FALSE;
}
@@ -129,11 +146,15 @@
: GnssMeasurement_V2_0{iGnssMeasurement}, mIGnssMeasurement_V2_1(iGnssMeasurement) {}
jboolean GnssMeasurement_V2_1::setCallback(const std::unique_ptr<GnssMeasurementCallback>& callback,
- bool enableFullTracking, bool enableCorrVecOutputs) {
- if (enableCorrVecOutputs == true) {
+ const IGnssMeasurementInterface::Options& options) {
+ if (options.enableCorrVecOutputs == true) {
ALOGW("Correlation vector output is not supported in 2.1 GNSS HAL.");
}
- auto status = mIGnssMeasurement_V2_1->setCallback_2_1(callback->getHidl(), enableFullTracking);
+ if (options.intervalMs > 1000) {
+ ALOGW("Measurement interval is not supported in 1.0 GNSS HAL.");
+ }
+ auto status = mIGnssMeasurement_V2_1->setCallback_2_1(callback->getHidl(),
+ options.enableFullTracking);
if (!checkHidlReturn(status, "IGnssMeasurement setCallback_2_1() failed.")) {
return JNI_FALSE;
}
diff --git a/services/core/jni/gnss/GnssMeasurement.h b/services/core/jni/gnss/GnssMeasurement.h
index f0752cd..7a95db8 100644
--- a/services/core/jni/gnss/GnssMeasurement.h
+++ b/services/core/jni/gnss/GnssMeasurement.h
@@ -37,16 +37,18 @@
class GnssMeasurementInterface {
public:
virtual ~GnssMeasurementInterface() {}
- virtual jboolean setCallback(const std::unique_ptr<GnssMeasurementCallback>& callback,
- bool enableFullTracking, bool enableCorrVecOutputs) = 0;
+ virtual jboolean setCallback(
+ const std::unique_ptr<GnssMeasurementCallback>& callback,
+ const android::hardware::gnss::IGnssMeasurementInterface::Options& options) = 0;
virtual jboolean close() = 0;
};
class GnssMeasurement : public GnssMeasurementInterface {
public:
GnssMeasurement(const sp<android::hardware::gnss::IGnssMeasurementInterface>& iGnssMeasurement);
- jboolean setCallback(const std::unique_ptr<GnssMeasurementCallback>& callback,
- bool enableFullTracking, bool enableCorrVecOutputs) override;
+ jboolean setCallback(
+ const std::unique_ptr<GnssMeasurementCallback>& callback,
+ const android::hardware::gnss::IGnssMeasurementInterface::Options& options) override;
jboolean close() override;
private:
@@ -57,8 +59,9 @@
public:
GnssMeasurement_V1_0(
const sp<android::hardware::gnss::V1_0::IGnssMeasurement>& iGnssMeasurement);
- jboolean setCallback(const std::unique_ptr<GnssMeasurementCallback>& callback,
- bool enableFullTracking, bool enableCorrVecOutputs) override;
+ jboolean setCallback(
+ const std::unique_ptr<GnssMeasurementCallback>& callback,
+ const android::hardware::gnss::IGnssMeasurementInterface::Options& options) override;
jboolean close() override;
private:
@@ -69,8 +72,9 @@
public:
GnssMeasurement_V1_1(
const sp<android::hardware::gnss::V1_1::IGnssMeasurement>& iGnssMeasurement);
- jboolean setCallback(const std::unique_ptr<GnssMeasurementCallback>& callback,
- bool enableFullTracking, bool enableCorrVecOutputs) override;
+ jboolean setCallback(
+ const std::unique_ptr<GnssMeasurementCallback>& callback,
+ const android::hardware::gnss::IGnssMeasurementInterface::Options& options) override;
private:
const sp<android::hardware::gnss::V1_1::IGnssMeasurement> mIGnssMeasurement_V1_1;
@@ -80,8 +84,9 @@
public:
GnssMeasurement_V2_0(
const sp<android::hardware::gnss::V2_0::IGnssMeasurement>& iGnssMeasurement);
- jboolean setCallback(const std::unique_ptr<GnssMeasurementCallback>& callback,
- bool enableFullTracking, bool enableCorrVecOutputs) override;
+ jboolean setCallback(
+ const std::unique_ptr<GnssMeasurementCallback>& callback,
+ const android::hardware::gnss::IGnssMeasurementInterface::Options& options) override;
private:
const sp<android::hardware::gnss::V2_0::IGnssMeasurement> mIGnssMeasurement_V2_0;
@@ -91,8 +96,9 @@
public:
GnssMeasurement_V2_1(
const sp<android::hardware::gnss::V2_1::IGnssMeasurement>& iGnssMeasurement);
- jboolean setCallback(const std::unique_ptr<GnssMeasurementCallback>& callback,
- bool enableFullTracking, bool enableCorrVecOutputs) override;
+ jboolean setCallback(
+ const std::unique_ptr<GnssMeasurementCallback>& callback,
+ const android::hardware::gnss::IGnssMeasurementInterface::Options& options) override;
private:
const sp<android::hardware::gnss::V2_1::IGnssMeasurement> mIGnssMeasurement_V2_1;
diff --git a/services/tests/PackageManagerComponentOverrideTests/src/com/android/server/pm/test/override/PackageManagerComponentLabelIconOverrideTest.kt b/services/tests/PackageManagerComponentOverrideTests/src/com/android/server/pm/test/override/PackageManagerComponentLabelIconOverrideTest.kt
index 4b12fd4..8203c1b 100644
--- a/services/tests/PackageManagerComponentOverrideTests/src/com/android/server/pm/test/override/PackageManagerComponentLabelIconOverrideTest.kt
+++ b/services/tests/PackageManagerComponentOverrideTests/src/com/android/server/pm/test/override/PackageManagerComponentLabelIconOverrideTest.kt
@@ -354,6 +354,7 @@
PackageManager.PERMISSION_GRANTED
}
}
+ val mockSharedLibrariesImpl: SharedLibrariesImpl = mock()
val mockInjector: PackageManagerServiceInjector = mock {
whenever(this.lock) { PackageManagerTracedLock() }
whenever(this.componentResolver) { mockComponentResolver }
@@ -366,6 +367,7 @@
whenever(this.appsFilter) { mockAppsFilter }
whenever(this.context) { mockContext }
whenever(this.getHandler()) { testHandler }
+ whenever(this.sharedLibrariesImpl) { mockSharedLibrariesImpl }
}
val testParams = PackageManagerServiceTestParams().apply {
this.pendingPackageBroadcasts = mockPendingBroadcasts
diff --git a/services/tests/mockingservicestests/src/com/android/server/communal/CommunalManagerServiceTest.java b/services/tests/mockingservicestests/src/com/android/server/communal/CommunalManagerServiceTest.java
index d6db1b2..7ebf014 100644
--- a/services/tests/mockingservicestests/src/com/android/server/communal/CommunalManagerServiceTest.java
+++ b/services/tests/mockingservicestests/src/com/android/server/communal/CommunalManagerServiceTest.java
@@ -29,6 +29,7 @@
import static com.google.common.truth.Truth.assertThat;
import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.ArgumentMatchers.anyInt;
import static org.mockito.ArgumentMatchers.anyString;
import static org.mockito.ArgumentMatchers.eq;
import static org.mockito.Mockito.spy;
@@ -138,7 +139,7 @@
ArgumentCaptor<BroadcastReceiver> packageReceiverCaptor =
ArgumentCaptor.forClass(BroadcastReceiver.class);
verify(mContextSpy).registerReceiverAsUser(packageReceiverCaptor.capture(),
- eq(UserHandle.SYSTEM), any(), any(), any());
+ eq(UserHandle.SYSTEM), any(), any(), any(), anyInt());
mPackageReceiver = packageReceiverCaptor.getValue();
mBinder = mService.getBinderServiceInstance();
diff --git a/services/tests/mockingservicestests/src/com/android/server/location/gnss/hal/FakeGnssHal.java b/services/tests/mockingservicestests/src/com/android/server/location/gnss/hal/FakeGnssHal.java
index e0c8b09..16ffda8 100644
--- a/services/tests/mockingservicestests/src/com/android/server/location/gnss/hal/FakeGnssHal.java
+++ b/services/tests/mockingservicestests/src/com/android/server/location/gnss/hal/FakeGnssHal.java
@@ -234,6 +234,7 @@
private boolean mMeasurementCollectionStarted = false;
private boolean mMeasurementCollectionFullTracking = false;
private boolean mMeasurementCollectionCorrVecOutputsEnabled = false;
+ private int mMeasurementCollectionIntervalMillis = 0;
private GnssHalPositionMode mPositionMode = new GnssHalPositionMode();
private GnssHalBatchingMode mBatchingMode = new GnssHalBatchingMode();
private final ArrayList<Location> mBatchedLocations = new ArrayList<>();
@@ -523,10 +524,11 @@
@Override
protected boolean startMeasurementCollection(boolean enableFullTracking,
- boolean enableCorrVecOutputs) {
+ boolean enableCorrVecOutputs, int intervalMillis) {
mState.mMeasurementCollectionStarted = true;
mState.mMeasurementCollectionFullTracking = enableFullTracking;
mState.mMeasurementCollectionCorrVecOutputsEnabled = enableCorrVecOutputs;
+ mState.mMeasurementCollectionIntervalMillis = intervalMillis;
return true;
}
@@ -535,6 +537,7 @@
mState.mMeasurementCollectionStarted = false;
mState.mMeasurementCollectionFullTracking = false;
mState.mMeasurementCollectionCorrVecOutputsEnabled = false;
+ mState.mMeasurementCollectionIntervalMillis = 0;
return true;
}
diff --git a/services/tests/mockingservicestests/src/com/android/server/pm/MockSystem.kt b/services/tests/mockingservicestests/src/com/android/server/pm/MockSystem.kt
index ae5984a41..44a8b30 100644
--- a/services/tests/mockingservicestests/src/com/android/server/pm/MockSystem.kt
+++ b/services/tests/mockingservicestests/src/com/android/server/pm/MockSystem.kt
@@ -121,6 +121,9 @@
/** The active map simulating the in memory storage of Settings */
private val mSettingsMap = WatchedArrayMap<String, PackageSetting>()
+ /** The shared libraries on the device */
+ private lateinit var mSharedLibraries: SharedLibrariesImpl
+
init {
PropertyInvalidatedCache.disableForTestMode()
val apply = ExtendedMockito.mockitoSession()
@@ -324,6 +327,11 @@
any(AndroidPackage::class.java), anyBoolean(), any(File::class.java))) {
PackageAbiHelper.NativeLibraryPaths("", false, "", "")
}
+ whenever(mocks.injector.bootstrap(any(PackageManagerService::class.java))) {
+ mSharedLibraries = SharedLibrariesImpl(
+ getArgument<Any>(0) as PackageManagerService, mocks.injector)
+ }
+ whenever(mocks.injector.sharedLibrariesImpl) { mSharedLibraries }
// everything visible by default
whenever(mocks.appsFilter.shouldFilterApplication(
anyInt(), nullable(), nullable(), anyInt())) { false }
diff --git a/services/tests/mockingservicestests/src/com/android/server/pm/StagingManagerTest.java b/services/tests/mockingservicestests/src/com/android/server/pm/StagingManagerTest.java
index 663bb2b..f2415b4 100644
--- a/services/tests/mockingservicestests/src/com/android/server/pm/StagingManagerTest.java
+++ b/services/tests/mockingservicestests/src/com/android/server/pm/StagingManagerTest.java
@@ -41,7 +41,7 @@
import android.content.pm.IStagedApexObserver;
import android.content.pm.PackageInstaller;
import android.content.pm.PackageInstaller.SessionInfo;
-import android.content.pm.PackageInstaller.SessionInfo.StagedSessionErrorCode;
+import android.content.pm.PackageInstaller.SessionInfo.SessionErrorCode;
import android.content.pm.StagedApexInfo;
import android.os.SystemProperties;
import android.os.storage.IStorageManager;
@@ -774,7 +774,7 @@
private boolean mIsReady = false;
private boolean mIsApplied = false;
private boolean mIsFailed = false;
- private @StagedSessionErrorCode int mErrorCode = -1;
+ private @SessionErrorCode int mErrorCode = -1;
private String mErrorMessage;
private boolean mIsDestroyed = false;
private int mParentSessionId = -1;
@@ -827,7 +827,7 @@
return this;
}
- private @StagedSessionErrorCode int getErrorCode() {
+ private @SessionErrorCode int getErrorCode() {
return mErrorCode;
}
@@ -939,7 +939,7 @@
}
@Override
- public void setSessionFailed(@StagedSessionErrorCode int errorCode, String errorMessage) {
+ public void setSessionFailed(@SessionErrorCode int errorCode, String errorMessage) {
Preconditions.checkState(!mIsApplied, "Already marked as applied");
mIsFailed = true;
mErrorCode = errorCode;
diff --git a/services/tests/servicestests/src/com/android/server/accessibility/AccessibilityServiceConnectionTest.java b/services/tests/servicestests/src/com/android/server/accessibility/AccessibilityServiceConnectionTest.java
index e93e544..82b7540 100644
--- a/services/tests/servicestests/src/com/android/server/accessibility/AccessibilityServiceConnectionTest.java
+++ b/services/tests/servicestests/src/com/android/server/accessibility/AccessibilityServiceConnectionTest.java
@@ -247,4 +247,21 @@
verify(mMockServiceClient).onPerformGestureResult(0, false);
}
+ @Test
+ public void unbind_resetAllMagnification() {
+ mConnection.unbindLocked();
+ verify(mMockMagnificationProcessor).resetAllIfNeeded(anyInt());
+ }
+
+ @Test
+ public void binderDied_resetAllMagnification() {
+ setServiceBinding(COMPONENT_NAME);
+ mConnection.bindLocked();
+ mConnection.onServiceConnected(COMPONENT_NAME, mMockIBinder);
+
+ mConnection.binderDied();
+
+ verify(mMockMagnificationProcessor).resetAllIfNeeded(anyInt());
+ }
+
}
diff --git a/services/tests/servicestests/src/com/android/server/accessibility/MagnificationProcessorTest.java b/services/tests/servicestests/src/com/android/server/accessibility/MagnificationProcessorTest.java
index 621507e..99d6c2a 100644
--- a/services/tests/servicestests/src/com/android/server/accessibility/MagnificationProcessorTest.java
+++ b/services/tests/servicestests/src/com/android/server/accessibility/MagnificationProcessorTest.java
@@ -24,6 +24,7 @@
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.anyBoolean;
import static org.mockito.ArgumentMatchers.anyFloat;
+import static org.mockito.ArgumentMatchers.anyInt;
import static org.mockito.ArgumentMatchers.eq;
import static org.mockito.Mockito.doAnswer;
import static org.mockito.Mockito.verify;
@@ -235,7 +236,17 @@
mMagnificationProcessor.resetCurrentMagnification(TEST_DISPLAY, /* animate= */false);
- verify(mMockWindowMagnificationManager).reset(TEST_DISPLAY);
+ verify(mMockWindowMagnificationManager).disableWindowMagnification(TEST_DISPLAY, false,
+ null);
+ }
+
+ @Test
+ public void resetAllIfNeeded_resetFullscreenAndWindowMagnificationByConnectionId() {
+ final int connectionId = 1;
+ mMagnificationProcessor.resetAllIfNeeded(connectionId);
+
+ verify(mMockFullScreenMagnificationController).resetAllIfNeeded(eq(connectionId));
+ verify(mMockWindowMagnificationManager).resetAllIfNeeded(eq(connectionId));
}
@Test
@@ -322,7 +333,7 @@
final MagnificationConfig result = mMagnificationProcessor.getMagnificationConfig(
TEST_DISPLAY);
verify(mMockMagnificationController).transitionMagnificationConfigMode(eq(TEST_DISPLAY),
- eq(newConfig), anyBoolean());
+ eq(newConfig), anyBoolean(), anyInt());
assertConfigEquals(newConfig, result);
}
@@ -438,7 +449,7 @@
anyFloat(), anyFloat(), anyFloat());
doAnswer(enableWindowMagnificationStubAnswer).when(
mWindowMagnificationManager).enableWindowMagnification(eq(TEST_DISPLAY),
- anyFloat(), anyFloat(), anyFloat(), any());
+ anyFloat(), anyFloat(), anyFloat(), any(), anyInt());
}
public void resetAndStubMethods() {
diff --git a/services/tests/servicestests/src/com/android/server/accessibility/magnification/MagnificationControllerTest.java b/services/tests/servicestests/src/com/android/server/accessibility/magnification/MagnificationControllerTest.java
index 08421de..0054fc3 100644
--- a/services/tests/servicestests/src/com/android/server/accessibility/magnification/MagnificationControllerTest.java
+++ b/services/tests/servicestests/src/com/android/server/accessibility/magnification/MagnificationControllerTest.java
@@ -125,6 +125,7 @@
public void setUp() throws Exception {
MockitoAnnotations.initMocks(this);
FakeSettingsProvider.clearSettingsProvider();
+ final Object globalLock = new Object();
LocalServices.removeServiceForTest(WindowManagerInternal.class);
LocalServices.addService(WindowManagerInternal.class, mMockWindowManagerInternal);
@@ -139,14 +140,14 @@
CURRENT_USER_ID);
mScaleProvider = spy(new MagnificationScaleProvider(mContext));
mWindowMagnificationManager = Mockito.spy(
- new WindowMagnificationManager(mContext, CURRENT_USER_ID,
+ new WindowMagnificationManager(mContext, globalLock,
mock(WindowMagnificationManager.Callback.class), mTraceManager,
mScaleProvider));
mMockConnection = new MockWindowMagnificationConnection(true);
mWindowMagnificationManager.setConnection(mMockConnection.getConnection());
mScreenMagnificationControllerStubber = new FullScreenMagnificationControllerStubber(
mScreenMagnificationController);
- mMagnificationController = new MagnificationController(mService, new Object(), mContext,
+ mMagnificationController = new MagnificationController(mService, globalLock, mContext,
mScreenMagnificationController, mWindowMagnificationManager, mScaleProvider);
mMagnificationController.setMagnificationCapabilities(
@@ -293,7 +294,7 @@
// Enable window magnification while animating.
mWindowMagnificationManager.enableWindowMagnification(TEST_DISPLAY, DEFAULT_SCALE,
- Float.NaN, Float.NaN, null);
+ Float.NaN, Float.NaN, null, TEST_SERVICE_ID);
mMockConnection.invokeCallbacks();
assertTrue(mWindowMagnificationManager.isWindowMagnifierEnabled(TEST_DISPLAY));
@@ -310,7 +311,7 @@
mMagnificationController.transitionMagnificationConfigMode(TEST_DISPLAY,
obtainMagnificationConfig(MODE_WINDOW),
- false);
+ false, TEST_SERVICE_ID);
verify(mScreenMagnificationController).reset(eq(TEST_DISPLAY), eq(false));
mMockConnection.invokeCallbacks();
@@ -325,13 +326,13 @@
activateMagnifier(MODE_WINDOW, MAGNIFIED_CENTER_X, MAGNIFIED_CENTER_Y);
mMagnificationController.transitionMagnificationConfigMode(TEST_DISPLAY,
obtainMagnificationConfig(MODE_FULLSCREEN),
- animate);
+ animate, TEST_SERVICE_ID);
mMockConnection.invokeCallbacks();
assertFalse(mWindowMagnificationManager.isWindowMagnifierEnabled(TEST_DISPLAY));
verify(mScreenMagnificationController).setScaleAndCenter(TEST_DISPLAY,
DEFAULT_SCALE, MAGNIFIED_CENTER_X, MAGNIFIED_CENTER_Y,
- animate, MAGNIFICATION_GESTURE_HANDLER_ID);
+ animate, TEST_SERVICE_ID);
}
@Test
@@ -345,7 +346,7 @@
// Config-setting mode
mMagnificationController.transitionMagnificationConfigMode(TEST_DISPLAY,
obtainMagnificationConfig(MODE_FULLSCREEN),
- true);
+ true, TEST_SERVICE_ID);
assertEquals(DEFAULT_SCALE, mScreenMagnificationController.getScale(TEST_DISPLAY), 0);
assertEquals(MAGNIFIED_CENTER_X, mScreenMagnificationController.getCenterX(TEST_DISPLAY),
@@ -365,7 +366,7 @@
// Config-setting mode
mMagnificationController.transitionMagnificationConfigMode(TEST_DISPLAY,
obtainMagnificationConfig(MODE_FULLSCREEN),
- true);
+ true, TEST_SERVICE_ID);
verify(mTransitionCallBack, never()).onResult(TEST_DISPLAY, true);
}
@@ -772,7 +773,7 @@
centerY, true, AccessibilityManagerService.MAGNIFICATION_GESTURE_HANDLER_ID);
} else {
mWindowMagnificationManager.enableWindowMagnification(TEST_DISPLAY, DEFAULT_SCALE,
- centerX, centerY, null);
+ centerX, centerY, null, TEST_SERVICE_ID);
mMockConnection.invokeCallbacks();
}
}
diff --git a/services/tests/servicestests/src/com/android/server/accessibility/magnification/MockWindowMagnificationConnection.java b/services/tests/servicestests/src/com/android/server/accessibility/magnification/MockWindowMagnificationConnection.java
index 0659a60..4c03ec3 100644
--- a/services/tests/servicestests/src/com/android/server/accessibility/magnification/MockWindowMagnificationConnection.java
+++ b/services/tests/servicestests/src/com/android/server/accessibility/magnification/MockWindowMagnificationConnection.java
@@ -35,6 +35,9 @@
import android.view.accessibility.IWindowMagnificationConnection;
import android.view.accessibility.IWindowMagnificationConnectionCallback;
+import java.util.ArrayList;
+import java.util.List;
+
/**
* Mocks the basic logic of window magnification in System UI. We assume the screen size is
* unlimited, so source bounds is always on the center of the mirror window bounds.
@@ -42,6 +45,8 @@
class MockWindowMagnificationConnection {
public static final int TEST_DISPLAY = Display.DEFAULT_DISPLAY;
+ public static final int TEST_DISPLAY_2 = Display.DEFAULT_DISPLAY + 1;
+ private final List mValidDisplayIds;
private final IWindowMagnificationConnection mConnection;
private final Binder mBinder;
private final boolean mSuspendCallback;
@@ -60,6 +65,10 @@
}
MockWindowMagnificationConnection(boolean suspendCallback) throws RemoteException {
+ mValidDisplayIds = new ArrayList();
+ mValidDisplayIds.add(TEST_DISPLAY);
+ mValidDisplayIds.add(TEST_DISPLAY_2);
+
mSuspendCallback = suspendCallback;
mConnection = mock(IWindowMagnificationConnection.class);
mBinder = mock(Binder.class);
@@ -86,8 +95,8 @@
private void stubEnableWindowMagnification() throws RemoteException {
doAnswer((invocation) -> {
final int displayId = invocation.getArgument(0);
- if (displayId != TEST_DISPLAY) {
- throw new IllegalArgumentException("only support default display :" + displayId);
+ if (!mValidDisplayIds.contains(displayId)) {
+ throw new IllegalArgumentException("Not support display :" + displayId);
}
mWindowMagnificationEnabled = true;
final float scale = invocation.getArgument(1);
@@ -107,8 +116,8 @@
private void stubDisableWindowMagnification() throws RemoteException {
doAnswer((invocation) -> {
final int displayId = invocation.getArgument(0);
- if (displayId != TEST_DISPLAY) {
- throw new IllegalArgumentException("only support default display :" + displayId);
+ if (!mValidDisplayIds.contains(displayId)) {
+ throw new IllegalArgumentException("Not support display :" + displayId);
}
setAnimationCallback(invocation.getArgument(1));
mHasPendingCallback = true;
diff --git a/services/tests/servicestests/src/com/android/server/accessibility/magnification/WindowMagnificationGestureHandlerTest.java b/services/tests/servicestests/src/com/android/server/accessibility/magnification/WindowMagnificationGestureHandlerTest.java
index b807c11..e9f0bd9 100644
--- a/services/tests/servicestests/src/com/android/server/accessibility/magnification/WindowMagnificationGestureHandlerTest.java
+++ b/services/tests/servicestests/src/com/android/server/accessibility/magnification/WindowMagnificationGestureHandlerTest.java
@@ -85,7 +85,7 @@
@Before
public void setUp() throws RemoteException {
MockitoAnnotations.initMocks(this);
- mWindowMagnificationManager = new WindowMagnificationManager(mContext, 0,
+ mWindowMagnificationManager = new WindowMagnificationManager(mContext, new Object(),
mock(WindowMagnificationManager.Callback.class), mMockTrace,
new MagnificationScaleProvider(mContext));
mMockConnection = new MockWindowMagnificationConnection();
diff --git a/services/tests/servicestests/src/com/android/server/accessibility/magnification/WindowMagnificationManagerTest.java b/services/tests/servicestests/src/com/android/server/accessibility/magnification/WindowMagnificationManagerTest.java
index 85512f3..a62c0d5 100644
--- a/services/tests/servicestests/src/com/android/server/accessibility/magnification/WindowMagnificationManagerTest.java
+++ b/services/tests/servicestests/src/com/android/server/accessibility/magnification/WindowMagnificationManagerTest.java
@@ -16,6 +16,9 @@
package com.android.server.accessibility.magnification;
+import static com.android.server.accessibility.magnification.MockWindowMagnificationConnection.TEST_DISPLAY;
+import static com.android.server.accessibility.magnification.MockWindowMagnificationConnection.TEST_DISPLAY_2;
+
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.anyBoolean;
import static org.mockito.ArgumentMatchers.eq;
@@ -38,13 +41,13 @@
import android.content.IntentFilter;
import android.graphics.PointF;
import android.graphics.Rect;
+import android.graphics.Region;
import android.os.IBinder;
import android.os.RemoteException;
import android.os.SystemClock;
import android.os.UserHandle;
import android.provider.Settings;
import android.test.mock.MockContentResolver;
-import android.view.Display;
import android.view.InputDevice;
import android.view.MotionEvent;
import android.view.accessibility.IRemoteMagnificationAnimationCallback;
@@ -67,8 +70,8 @@
*/
public class WindowMagnificationManagerTest {
- private static final int TEST_DISPLAY = Display.DEFAULT_DISPLAY;
private static final int CURRENT_USER_ID = UserHandle.USER_SYSTEM;
+ private static final int SERVICE_ID = 1;
private MockWindowMagnificationConnection mMockConnection;
@Mock
@@ -91,7 +94,7 @@
LocalServices.addService(StatusBarManagerInternal.class, mMockStatusBarManagerInternal);
mResolver = new MockContentResolver();
mMockConnection = new MockWindowMagnificationConnection();
- mWindowMagnificationManager = new WindowMagnificationManager(mContext, CURRENT_USER_ID,
+ mWindowMagnificationManager = new WindowMagnificationManager(mContext, new Object(),
mMockCallback, mMockTrace, new MagnificationScaleProvider(mContext));
when(mContext.getContentResolver()).thenReturn(mResolver);
@@ -185,7 +188,7 @@
mWindowMagnificationManager.setConnection(mMockConnection.getConnection());
mWindowMagnificationManager.enableWindowMagnification(TEST_DISPLAY, 2f, 200f, 300f,
- mAnimationCallback);
+ mAnimationCallback, SERVICE_ID);
verify(mMockConnection.getConnection()).enableWindowMagnification(eq(TEST_DISPLAY), eq(2f),
eq(200f), eq(300f), eq(0f), eq(0f),
@@ -377,14 +380,51 @@
}
@Test
- public void resetMagnification_enabled_windowMagnifierDisabled() {
+ public void requestConnectionToNull_expectedGetterResults() {
mWindowMagnificationManager.setConnection(mMockConnection.getConnection());
- mWindowMagnificationManager.enableWindowMagnification(TEST_DISPLAY, 3f, NaN, NaN);
- assertTrue(mWindowMagnificationManager.isWindowMagnifierEnabled(TEST_DISPLAY));
+ mWindowMagnificationManager.enableWindowMagnification(TEST_DISPLAY, 3f, 1, 1);
- mWindowMagnificationManager.reset(TEST_DISPLAY);
+ mWindowMagnificationManager.requestConnection(false);
+
+ assertEquals(1f, mWindowMagnificationManager.getScale(TEST_DISPLAY), 0);
+ assertTrue(Float.isNaN(mWindowMagnificationManager.getCenterX(TEST_DISPLAY)));
+ assertTrue(Float.isNaN(mWindowMagnificationManager.getCenterY(TEST_DISPLAY)));
+ final Region bounds = new Region();
+ mWindowMagnificationManager.getMagnificationSourceBounds(TEST_DISPLAY, bounds);
+ assertTrue(bounds.isEmpty());
+ }
+
+ @Test
+ public void resetAllMagnification_enabledBySameId_windowMagnifiersDisabled() {
+ mWindowMagnificationManager.setConnection(mMockConnection.getConnection());
+ mWindowMagnificationManager.enableWindowMagnification(TEST_DISPLAY, 3f,
+ 100f, 200f, null, WindowMagnificationManager.WINDOW_POSITION_AT_CENTER, SERVICE_ID);
+ mWindowMagnificationManager.enableWindowMagnification(TEST_DISPLAY_2, 3f,
+ 100f, 200f, null, WindowMagnificationManager.WINDOW_POSITION_AT_CENTER, SERVICE_ID);
+ assertTrue(mWindowMagnificationManager.isWindowMagnifierEnabled(TEST_DISPLAY));
+ assertTrue(mWindowMagnificationManager.isWindowMagnifierEnabled(TEST_DISPLAY_2));
+
+ mWindowMagnificationManager.resetAllIfNeeded(SERVICE_ID);
assertFalse(mWindowMagnificationManager.isWindowMagnifierEnabled(TEST_DISPLAY));
+ assertFalse(mWindowMagnificationManager.isWindowMagnifierEnabled(TEST_DISPLAY_2));
+ }
+
+ @Test
+ public void resetAllMagnification_enabledByDifferentId_windowMagnifierDisabled() {
+ final int serviceId2 = SERVICE_ID + 1;
+ mWindowMagnificationManager.setConnection(mMockConnection.getConnection());
+ mWindowMagnificationManager.enableWindowMagnification(TEST_DISPLAY, 3f,
+ 100f, 200f, null, WindowMagnificationManager.WINDOW_POSITION_AT_CENTER, SERVICE_ID);
+ mWindowMagnificationManager.enableWindowMagnification(TEST_DISPLAY_2, 3f,
+ 100f, 200f, null, WindowMagnificationManager.WINDOW_POSITION_AT_CENTER, serviceId2);
+ assertTrue(mWindowMagnificationManager.isWindowMagnifierEnabled(TEST_DISPLAY));
+ assertTrue(mWindowMagnificationManager.isWindowMagnifierEnabled(TEST_DISPLAY_2));
+
+ mWindowMagnificationManager.resetAllIfNeeded(SERVICE_ID);
+
+ assertFalse(mWindowMagnificationManager.isWindowMagnifierEnabled(TEST_DISPLAY));
+ assertTrue(mWindowMagnificationManager.isWindowMagnifierEnabled(TEST_DISPLAY_2));
}
@Test
@@ -439,6 +479,22 @@
}
@Test
+ public void magnifierGetters_disabled_expectedValues() {
+ mWindowMagnificationManager.requestConnection(true);
+ mWindowMagnificationManager.enableWindowMagnification(TEST_DISPLAY, 3f,
+ 100f, 200f, WindowMagnificationManager.WINDOW_POSITION_AT_CENTER);
+
+ mWindowMagnificationManager.disableWindowMagnification(TEST_DISPLAY, false);
+
+ assertEquals(1f, mWindowMagnificationManager.getScale(TEST_DISPLAY), 0);
+ assertTrue(Float.isNaN(mWindowMagnificationManager.getCenterX(TEST_DISPLAY)));
+ assertTrue(Float.isNaN(mWindowMagnificationManager.getCenterY(TEST_DISPLAY)));
+ final Region bounds = new Region();
+ mWindowMagnificationManager.getMagnificationSourceBounds(TEST_DISPLAY, bounds);
+ assertTrue(bounds.isEmpty());
+ }
+
+ @Test
public void onDisplayRemoved_enabledOnTestDisplay_disabled() {
mWindowMagnificationManager.requestConnection(true);
mWindowMagnificationManager.enableWindowMagnification(TEST_DISPLAY, 3f, 100f, 200f);
diff --git a/services/tests/servicestests/src/com/android/server/net/NetworkPolicyManagerServiceTest.java b/services/tests/servicestests/src/com/android/server/net/NetworkPolicyManagerServiceTest.java
index eda05bf..b811e28 100644
--- a/services/tests/servicestests/src/com/android/server/net/NetworkPolicyManagerServiceTest.java
+++ b/services/tests/servicestests/src/com/android/server/net/NetworkPolicyManagerServiceTest.java
@@ -129,6 +129,7 @@
import android.net.NetworkStatsHistory;
import android.net.NetworkTemplate;
import android.net.TelephonyNetworkSpecifier;
+import android.net.wifi.WifiInfo;
import android.os.Binder;
import android.os.Handler;
import android.os.INetworkManagementService;
@@ -226,13 +227,13 @@
private static final long TEST_START = 1194220800000L;
private static final String TEST_IFACE = "test0";
- private static final String TEST_SSID = "AndroidAP";
+ private static final String TEST_WIFI_NETWORK_KEY = "TestWifiNetworkKey";
private static final String TEST_IMSI = "310210";
private static final int TEST_SUB_ID = 42;
private static final Network TEST_NETWORK = mock(Network.class, CALLS_REAL_METHODS);
- private static NetworkTemplate sTemplateWifi = buildTemplateWifi(TEST_SSID);
+ private static NetworkTemplate sTemplateWifi = buildTemplateWifi(TEST_WIFI_NETWORK_KEY);
private static NetworkTemplate sTemplateCarrierMetered =
buildTemplateCarrierMetered(TEST_IMSI);
@@ -2096,10 +2097,13 @@
}
private static NetworkStateSnapshot buildWifi() {
+ WifiInfo mockWifiInfo = mock(WifiInfo.class);
+ when(mockWifiInfo.makeCopy(anyLong())).thenReturn(mockWifiInfo);
+ when(mockWifiInfo.getCurrentNetworkKey()).thenReturn(TEST_WIFI_NETWORK_KEY);
final LinkProperties prop = new LinkProperties();
prop.setInterfaceName(TEST_IFACE);
final NetworkCapabilities networkCapabilities = new NetworkCapabilities.Builder()
- .addTransportType(TRANSPORT_WIFI).setSsid(TEST_SSID).build();
+ .addTransportType(TRANSPORT_WIFI).setTransportInfo(mockWifiInfo).build();
return new NetworkStateSnapshot(TEST_NETWORK, networkCapabilities, prop,
null /*subscriberId*/, TYPE_WIFI);
}
diff --git a/services/tests/servicestests/src/com/android/server/pm/PackageInstallerSessionTest.java b/services/tests/servicestests/src/com/android/server/pm/PackageInstallerSessionTest.java
index 6c4ae6f..62a2b1b 100644
--- a/services/tests/servicestests/src/com/android/server/pm/PackageInstallerSessionTest.java
+++ b/services/tests/servicestests/src/com/android/server/pm/PackageInstallerSessionTest.java
@@ -306,12 +306,12 @@
assertEquals(expected.stageCid, actual.stageCid);
assertEquals(expected.isPrepared(), actual.isPrepared());
assertEquals(expected.isStaged(), actual.isStaged());
- assertEquals(expected.isStagedSessionApplied(), actual.isStagedSessionApplied());
- assertEquals(expected.isStagedSessionFailed(), actual.isStagedSessionFailed());
- assertEquals(expected.isStagedSessionReady(), actual.isStagedSessionReady());
- assertEquals(expected.getStagedSessionErrorCode(), actual.getStagedSessionErrorCode());
- assertEquals(expected.getStagedSessionErrorMessage(),
- actual.getStagedSessionErrorMessage());
+ assertEquals(expected.isSessionApplied(), actual.isSessionApplied());
+ assertEquals(expected.isSessionFailed(), actual.isSessionFailed());
+ assertEquals(expected.isSessionReady(), actual.isSessionReady());
+ assertEquals(expected.getSessionErrorCode(), actual.getSessionErrorCode());
+ assertEquals(expected.getSessionErrorMessage(),
+ actual.getSessionErrorMessage());
assertEquals(expected.isPrepared(), actual.isPrepared());
assertEquals(expected.isCommitted(), actual.isCommitted());
assertEquals(expected.createdMillis, actual.createdMillis);
diff --git a/services/tests/servicestests/src/com/android/server/pm/PackageManagerSettingsTests.java b/services/tests/servicestests/src/com/android/server/pm/PackageManagerSettingsTests.java
index 6c9f8fe..1b898fa 100644
--- a/services/tests/servicestests/src/com/android/server/pm/PackageManagerSettingsTests.java
+++ b/services/tests/servicestests/src/com/android/server/pm/PackageManagerSettingsTests.java
@@ -264,17 +264,17 @@
final SuspendParams params = packageUserState1.getSuspendParams().valueAt(0);
watcher.verifyNoChangeReported("fetch user state");
assertThat(params, is(notNullValue()));
- assertThat(params.appExtras.size(), is(1));
- assertThat(params.appExtras.getString("app_extra_string"), is("value"));
- assertThat(params.launcherExtras.size(), is(1));
- assertThat(params.launcherExtras.getLong("launcher_extra_long"), is(4L));
- assertThat(params.dialogInfo, is(notNullValue()));
- assertThat(params.dialogInfo.getDialogMessage(), is("Dialog Message"));
- assertThat(params.dialogInfo.getTitleResId(), is(ID_NULL));
- assertThat(params.dialogInfo.getIconResId(), is(TEST_RESOURCE_ID));
- assertThat(params.dialogInfo.getNeutralButtonTextResId(), is(ID_NULL));
- assertThat(params.dialogInfo.getNeutralButtonAction(), is(BUTTON_ACTION_MORE_DETAILS));
- assertThat(params.dialogInfo.getDialogMessageResId(), is(ID_NULL));
+ assertThat(params.getAppExtras().size(), is(1));
+ assertThat(params.getAppExtras().getString("app_extra_string"), is("value"));
+ assertThat(params.getLauncherExtras().size(), is(1));
+ assertThat(params.getLauncherExtras().getLong("launcher_extra_long"), is(4L));
+ assertThat(params.getDialogInfo(), is(notNullValue()));
+ assertThat(params.getDialogInfo().getDialogMessage(), is("Dialog Message"));
+ assertThat(params.getDialogInfo().getTitleResId(), is(ID_NULL));
+ assertThat(params.getDialogInfo().getIconResId(), is(TEST_RESOURCE_ID));
+ assertThat(params.getDialogInfo().getNeutralButtonTextResId(), is(ID_NULL));
+ assertThat(params.getDialogInfo().getNeutralButtonAction(), is(BUTTON_ACTION_MORE_DETAILS));
+ assertThat(params.getDialogInfo().getDialogMessageResId(), is(ID_NULL));
}
@Test
@@ -351,18 +351,18 @@
final SuspendParams params11 = readPus1.getSuspendParams().valueAt(0);
watcher.verifyNoChangeReported("read package param");
assertThat(params11, is(notNullValue()));
- assertThat(params11.dialogInfo, is(dialogInfo1));
- assertThat(BaseBundle.kindofEquals(params11.appExtras, appExtras1), is(true));
- assertThat(BaseBundle.kindofEquals(params11.launcherExtras, launcherExtras1),
+ assertThat(params11.getDialogInfo(), is(dialogInfo1));
+ assertThat(BaseBundle.kindofEquals(params11.getAppExtras(), appExtras1), is(true));
+ assertThat(BaseBundle.kindofEquals(params11.getLauncherExtras(), launcherExtras1),
is(true));
watcher.verifyNoChangeReported("read package param");
assertThat(readPus1.getSuspendParams().keyAt(1), is("suspendingPackage2"));
final SuspendParams params12 = readPus1.getSuspendParams().valueAt(1);
assertThat(params12, is(notNullValue()));
- assertThat(params12.dialogInfo, is(dialogInfo2));
- assertThat(BaseBundle.kindofEquals(params12.appExtras, appExtras2), is(true));
- assertThat(BaseBundle.kindofEquals(params12.launcherExtras, launcherExtras2),
+ assertThat(params12.getDialogInfo(), is(dialogInfo2));
+ assertThat(BaseBundle.kindofEquals(params12.getAppExtras(), appExtras2), is(true));
+ assertThat(BaseBundle.kindofEquals(params12.getLauncherExtras(), launcherExtras2),
is(true));
watcher.verifyNoChangeReported("read package param");
@@ -373,9 +373,9 @@
assertThat(readPus2.getSuspendParams().keyAt(0), is("suspendingPackage3"));
final SuspendParams params21 = readPus2.getSuspendParams().valueAt(0);
assertThat(params21, is(notNullValue()));
- assertThat(params21.dialogInfo, is(nullValue()));
- assertThat(BaseBundle.kindofEquals(params21.appExtras, appExtras1), is(true));
- assertThat(params21.launcherExtras, is(nullValue()));
+ assertThat(params21.getDialogInfo(), is(nullValue()));
+ assertThat(BaseBundle.kindofEquals(params21.getAppExtras(), appExtras1), is(true));
+ assertThat(params21.getLauncherExtras(), is(nullValue()));
watcher.verifyNoChangeReported("read package param");
final PackageUserStateInternal readPus3 = settingsUnderTest.mPackages.get(PACKAGE_NAME_3)
@@ -388,7 +388,7 @@
@Test
public void testPackageRestrictionsSuspendedDefault() {
final PackageSetting defaultSetting = createPackageSetting(PACKAGE_NAME_1);
- assertThat(defaultSetting.getSuspended(0), is(false));
+ assertThat(defaultSetting.getUserStateOrDefault(0).isSuspended(), is(false));
}
@Test
diff --git a/services/tests/servicestests/src/com/android/server/pm/PackageUserStateTest.java b/services/tests/servicestests/src/com/android/server/pm/PackageUserStateTest.java
index 828d419c..1e4134e 100644
--- a/services/tests/servicestests/src/com/android/server/pm/PackageUserStateTest.java
+++ b/services/tests/servicestests/src/com/android/server/pm/PackageUserStateTest.java
@@ -82,7 +82,8 @@
assertThat(testUserState.equals(oldUserState), is(false));
oldUserState = new PackageUserStateImpl();
- oldUserState.setSuspended(true);
+ oldUserState.putSuspendParams("suspendingPackage",
+ SuspendParams.getInstanceOrNull(null, new PersistableBundle(), null));
assertThat(testUserState.equals(oldUserState), is(false));
oldUserState = new PackageUserStateImpl();
@@ -231,7 +232,6 @@
final PackageUserStateImpl testUserState1 = new PackageUserStateImpl();
- testUserState1.setSuspended(true);
testUserState1.setSuspendParams(paramsMap1);
PackageUserStateImpl testUserState2 =
diff --git a/services/tests/wmtests/src/com/android/server/wm/ActivityRecordTests.java b/services/tests/wmtests/src/com/android/server/wm/ActivityRecordTests.java
index 2e62286..8a057f7 100644
--- a/services/tests/wmtests/src/com/android/server/wm/ActivityRecordTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/ActivityRecordTests.java
@@ -2311,17 +2311,15 @@
// Set initial orientation and update.
activity.setOrientation(SCREEN_ORIENTATION_LANDSCAPE);
- mDisplayContent.updateOrientation(
- mDisplayContent.getRequestedOverrideConfiguration(),
- null /* freezeThisOneIfNeeded */, false /* forceUpdate */);
+ mDisplayContent.updateOrientation(null /* freezeThisOneIfNeeded */,
+ false /* forceUpdate */);
assertEquals(SCREEN_ORIENTATION_LANDSCAPE, mDisplayContent.getLastOrientation());
appWindow.mResizeReported = false;
// Update the orientation to perform 180 degree rotation and check that resize was reported.
activity.setOrientation(SCREEN_ORIENTATION_REVERSE_LANDSCAPE);
- mDisplayContent.updateOrientation(
- mDisplayContent.getRequestedOverrideConfiguration(),
- null /* freezeThisOneIfNeeded */, false /* forceUpdate */);
+ mDisplayContent.updateOrientation(null /* freezeThisOneIfNeeded */,
+ false /* forceUpdate */);
// In this test, DC will not get config update. Set the waiting flag to false.
mDisplayContent.mWaitingForConfig = false;
mWm.mRoot.performSurfacePlacement();
diff --git a/services/tests/wmtests/src/com/android/server/wm/SplashScreenExceptionListTest.java b/services/tests/wmtests/src/com/android/server/wm/SplashScreenExceptionListTest.java
index 3714d99..f5d915d 100644
--- a/services/tests/wmtests/src/com/android/server/wm/SplashScreenExceptionListTest.java
+++ b/services/tests/wmtests/src/com/android/server/wm/SplashScreenExceptionListTest.java
@@ -39,6 +39,7 @@
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.TimeUnit;
+import java.util.function.Consumer;
/**
* Test for the splash screen exception list
@@ -55,7 +56,16 @@
private DeviceConfig.Properties mInitialWindowManagerProperties;
private final HandlerExecutor mExecutor = new HandlerExecutor(
new Handler(Looper.getMainLooper()));
- private final SplashScreenExceptionList mList = new SplashScreenExceptionList(mExecutor);
+ private final SplashScreenExceptionList mList = new SplashScreenExceptionList(mExecutor) {
+ @Override
+ void updateDeviceConfig(String rawList) {
+ super.updateDeviceConfig(rawList);
+ if (mOnUpdateDeviceConfig != null) {
+ mOnUpdateDeviceConfig.accept(rawList);
+ }
+ }
+ };
+ private Consumer<String> mOnUpdateDeviceConfig;
@Before
public void setUp() throws Exception {
@@ -91,13 +101,11 @@
private void setExceptionListAndWaitForCallback(String commaSeparatedList) {
CountDownLatch latch = new CountDownLatch(1);
- DeviceConfig.OnPropertiesChangedListener onPropertiesChangedListener = (p) -> {
- if (commaSeparatedList.equals(p.getString(KEY_SPLASH_SCREEN_EXCEPTION_LIST, null))) {
+ mOnUpdateDeviceConfig = rawList -> {
+ if (commaSeparatedList.equals(rawList)) {
latch.countDown();
}
};
- DeviceConfig.addOnPropertiesChangedListener(DeviceConfig.NAMESPACE_WINDOW_MANAGER,
- mExecutor, onPropertiesChangedListener);
DeviceConfig.setProperty(DeviceConfig.NAMESPACE_WINDOW_MANAGER,
KEY_SPLASH_SCREEN_EXCEPTION_LIST, commaSeparatedList, false);
try {
@@ -105,8 +113,6 @@
latch.await(1, TimeUnit.SECONDS));
} catch (InterruptedException e) {
Assert.fail(e.getMessage());
- } finally {
- DeviceConfig.removeOnPropertiesChangedListener(onPropertiesChangedListener);
}
}
diff --git a/services/tests/wmtests/src/com/android/server/wm/TaskStackChangedListenerTest.java b/services/tests/wmtests/src/com/android/server/wm/TaskStackChangedListenerTest.java
index a1d0eb8..790b154 100644
--- a/services/tests/wmtests/src/com/android/server/wm/TaskStackChangedListenerTest.java
+++ b/services/tests/wmtests/src/com/android/server/wm/TaskStackChangedListenerTest.java
@@ -25,11 +25,10 @@
import static com.android.server.wm.utils.CommonUtils.runWithShellPermissionIdentity;
import static org.junit.Assert.assertEquals;
-import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertNotNull;
-import static org.junit.Assert.assertTrue;
import android.app.Activity;
+import android.app.ActivityManager.RunningTaskInfo;
import android.app.ActivityManager.TaskDescription;
import android.app.ActivityOptions;
import android.app.ActivityTaskManager;
@@ -57,7 +56,6 @@
import androidx.test.filters.MediumTest;
import org.junit.After;
-import org.junit.Before;
import org.junit.Test;
import java.util.Arrays;
@@ -73,49 +71,42 @@
@MediumTest
public class TaskStackChangedListenerTest {
- private static final int VIRTUAL_DISPLAY_WIDTH = 800;
- private static final int VIRTUAL_DISPLAY_HEIGHT = 600;
- private static final int VIRTUAL_DISPLAY_DENSITY = 160;
-
private ITaskStackListener mTaskStackListener;
- private DisplayManager mDisplayManager;
private VirtualDisplay mVirtualDisplay;
+ private ImageReader mImageReader;
private static final int WAIT_TIMEOUT_MS = 5000;
private static final Object sLock = new Object();
- @Before
- public void setUp() {
- mDisplayManager = getInstrumentation().getContext().getSystemService(
- DisplayManager.class);
- mVirtualDisplay = createVirtualDisplay(
- getClass().getSimpleName() + "_virtualDisplay",
- VIRTUAL_DISPLAY_WIDTH, VIRTUAL_DISPLAY_HEIGHT, VIRTUAL_DISPLAY_DENSITY);
- }
-
@After
public void tearDown() throws Exception {
- ActivityTaskManager.getService().unregisterTaskStackListener(mTaskStackListener);
- mTaskStackListener = null;
- mVirtualDisplay.release();
+ if (mTaskStackListener != null) {
+ ActivityTaskManager.getService().unregisterTaskStackListener(mTaskStackListener);
+ }
+ if (mVirtualDisplay != null) {
+ mVirtualDisplay.release();
+ mImageReader.close();
+ }
}
- private VirtualDisplay createVirtualDisplay(String name, int width, int height, int density) {
- VirtualDisplay virtualDisplay = null;
- try (ImageReader reader = ImageReader.newInstance(width, height,
- /* format= */ PixelFormat.RGBA_8888, /* maxImages= */ 2)) {
- int flags = VIRTUAL_DISPLAY_FLAG_PRESENTATION | VIRTUAL_DISPLAY_FLAG_OWN_CONTENT_ONLY
- | VIRTUAL_DISPLAY_FLAG_PUBLIC;
- virtualDisplay = mDisplayManager.createVirtualDisplay(
- name, width, height, density, reader.getSurface(), flags);
- virtualDisplay.setSurface(reader.getSurface());
- }
- assertTrue("display id must be unique",
- virtualDisplay.getDisplay().getDisplayId() != Display.DEFAULT_DISPLAY);
+ private VirtualDisplay createVirtualDisplay() {
+ final int width = 800;
+ final int height = 600;
+ final int density = 160;
+ final DisplayManager displayManager = getInstrumentation().getContext().getSystemService(
+ DisplayManager.class);
+ mImageReader = ImageReader.newInstance(width, height, PixelFormat.RGBA_8888,
+ 2 /* maxImages */);
+ final int flags = VIRTUAL_DISPLAY_FLAG_PRESENTATION | VIRTUAL_DISPLAY_FLAG_OWN_CONTENT_ONLY
+ | VIRTUAL_DISPLAY_FLAG_PUBLIC;
+ final String name = getClass().getSimpleName() + "_VirtualDisplay";
+ mVirtualDisplay = displayManager.createVirtualDisplay(name, width, height, density,
+ mImageReader.getSurface(), flags);
+ mVirtualDisplay.setSurface(mImageReader.getSurface());
assertNotNull("display must be registered",
- Arrays.asList(mDisplayManager.getDisplays()).stream().filter(
+ Arrays.stream(displayManager.getDisplays()).filter(
d -> d.getName().equals(name)).findAny());
- return virtualDisplay;
+ return mVirtualDisplay;
}
@Test
@@ -163,11 +154,10 @@
mTaskId = taskId;
}
@Override
- public void onTaskDescriptionChanged(int taskId, TaskDescription td)
- throws RemoteException {
- if (mTaskId == taskId && !TextUtils.isEmpty(td.getLabel())) {
- params[0] = taskId;
- params[1] = td;
+ public void onTaskDescriptionChanged(RunningTaskInfo info) {
+ if (mTaskId == info.taskId && !TextUtils.isEmpty(info.taskDescription.getLabel())) {
+ params[0] = info.taskId;
+ params[1] = info.taskDescription;
latch.countDown();
}
}
@@ -211,75 +201,71 @@
@Test
@Presubmit
public void testTaskChangeCallBacks() throws Exception {
- final Object[] params = new Object[2];
final CountDownLatch taskCreatedLaunchLatch = new CountDownLatch(1);
final CountDownLatch taskMovedToFrontLatch = new CountDownLatch(1);
final CountDownLatch taskRemovedLatch = new CountDownLatch(1);
final CountDownLatch taskRemovalStartedLatch = new CountDownLatch(1);
- final CountDownLatch onDetachedFromWindowLatch = new CountDownLatch(1);
+ final int[] expectedTaskId = { -1 };
+ final int[] receivedTaskId = { -1 };
+ final ComponentName expectedName = new ComponentName(getInstrumentation().getContext(),
+ ActivityTaskChangeCallbacks.class);
registerTaskStackChangedListener(new TaskStackListener() {
@Override
- public void onTaskCreated(int taskId, ComponentName componentName)
- throws RemoteException {
- params[0] = taskId;
- params[1] = componentName;
- taskCreatedLaunchLatch.countDown();
+ public void onTaskCreated(int taskId, ComponentName componentName) {
+ receivedTaskId[0] = taskId;
+ if (expectedName.equals(componentName)) {
+ taskCreatedLaunchLatch.countDown();
+ }
}
@Override
- public void onTaskMovedToFront(int taskId) throws RemoteException {
- params[0] = taskId;
+ public void onTaskMovedToFront(RunningTaskInfo info) {
+ receivedTaskId[0] = info.taskId;
taskMovedToFrontLatch.countDown();
}
@Override
- public void onTaskRemovalStarted(int taskId) {
- params[0] = taskId;
- taskRemovalStartedLatch.countDown();
+ public void onTaskRemovalStarted(RunningTaskInfo info) {
+ if (expectedTaskId[0] == info.taskId) {
+ taskRemovalStartedLatch.countDown();
+ }
}
@Override
- public void onTaskRemoved(int taskId) throws RemoteException {
- if (taskCreatedLaunchLatch.getCount() == 1) {
- // The test activity hasn't started. Ignore the noise from previous test.
- return;
+ public void onTaskRemoved(int taskId) {
+ if (expectedTaskId[0] == taskId) {
+ taskRemovedLatch.countDown();
}
- params[0] = taskId;
- taskRemovedLatch.countDown();
}
});
final ActivityTaskChangeCallbacks activity =
(ActivityTaskChangeCallbacks) startTestActivity(ActivityTaskChangeCallbacks.class);
- activity.setDetachedFromWindowLatch(onDetachedFromWindowLatch);
- final int id = activity.getTaskId();
+ expectedTaskId[0] = activity.getTaskId();
// Test for onTaskCreated and onTaskMovedToFront
waitForCallback(taskMovedToFrontLatch);
assertEquals(0, taskCreatedLaunchLatch.getCount());
- assertEquals(id, params[0]);
- ComponentName componentName = (ComponentName) params[1];
- assertEquals(ActivityTaskChangeCallbacks.class.getName(), componentName.getClassName());
+ assertEquals(expectedTaskId[0], receivedTaskId[0]);
+ // Ensure that the window is attached before removal so there will be a detached callback.
+ waitForCallback(activity.mOnAttachedToWindowCountDownLatch);
// Test for onTaskRemovalStarted.
assertEquals(1, taskRemovalStartedLatch.getCount());
assertEquals(1, taskRemovedLatch.getCount());
activity.finishAndRemoveTask();
waitForCallback(taskRemovalStartedLatch);
// onTaskRemovalStarted happens before the activity's window is removed.
- assertFalse(activity.mOnDetachedFromWindowCalled);
- assertEquals(id, params[0]);
+ assertEquals(1, activity.mOnDetachedFromWindowCountDownLatch.getCount());
// Test for onTaskRemoved.
waitForCallback(taskRemovedLatch);
- assertEquals(id, params[0]);
- waitForCallback(onDetachedFromWindowLatch);
- assertTrue(activity.mOnDetachedFromWindowCalled);
+ waitForCallback(activity.mOnDetachedFromWindowCountDownLatch);
}
@Test
public void testTaskDisplayChanged() throws Exception {
- int virtualDisplayId = mVirtualDisplay.getDisplay().getDisplayId();
+ final int virtualDisplayId = createVirtualDisplay().getDisplay().getDisplayId();
// Launch a Activity inside VirtualDisplay
CountDownLatch displayChangedLatch1 = new CountDownLatch(1);
@@ -498,18 +484,18 @@
}
public static class ActivityTaskChangeCallbacks extends TestActivity {
- public boolean mOnDetachedFromWindowCalled = false;
- private CountDownLatch mOnDetachedFromWindowCountDownLatch;
+ final CountDownLatch mOnAttachedToWindowCountDownLatch = new CountDownLatch(1);
+ final CountDownLatch mOnDetachedFromWindowCountDownLatch = new CountDownLatch(1);
+
+ @Override
+ public void onAttachedToWindow() {
+ mOnAttachedToWindowCountDownLatch.countDown();
+ }
@Override
public void onDetachedFromWindow() {
- mOnDetachedFromWindowCalled = true;
mOnDetachedFromWindowCountDownLatch.countDown();
}
-
- void setDetachedFromWindowLatch(CountDownLatch countDownLatch) {
- mOnDetachedFromWindowCountDownLatch = countDownLatch;
- }
}
public static class ActivityInVirtualDisplay extends TestActivity {
diff --git a/services/tests/wmtests/src/com/android/server/wm/TransitionTests.java b/services/tests/wmtests/src/com/android/server/wm/TransitionTests.java
index ec6cd92..ed3888c 100644
--- a/services/tests/wmtests/src/com/android/server/wm/TransitionTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/TransitionTests.java
@@ -556,8 +556,15 @@
// The redrawn window will be faded in when the transition finishes. And because this test
// only use one non-activity window, the fade rotation controller should also be cleared.
- statusBar.mWinAnimator.mDrawState = WindowStateAnimator.HAS_DRAWN;
+ statusBar.mWinAnimator.mDrawState = WindowStateAnimator.DRAW_PENDING;
+ final SurfaceControl.Transaction postDrawTransaction =
+ mock(SurfaceControl.Transaction.class);
+ final boolean layoutNeeded = statusBar.finishDrawing(postDrawTransaction);
+ assertFalse(layoutNeeded);
player.finish();
+ // The controller should capture the draw transaction and merge it when preparing to run
+ // fade-in animation.
+ verify(mDisplayContent.getPendingTransaction()).merge(eq(postDrawTransaction));
assertNull(mDisplayContent.getFadeRotationAnimationController());
}
diff --git a/services/tests/wmtests/src/com/android/server/wm/WindowTestsBase.java b/services/tests/wmtests/src/com/android/server/wm/WindowTestsBase.java
index a985de5..34038c5 100644
--- a/services/tests/wmtests/src/com/android/server/wm/WindowTestsBase.java
+++ b/services/tests/wmtests/src/com/android/server/wm/WindowTestsBase.java
@@ -132,6 +132,9 @@
// Default base activity name
private static final String DEFAULT_COMPONENT_CLASS_NAME = ".BarActivity";
+ // An id appended to the end of the component name to make it unique
+ static int sCurrentActivityId = 0;
+
ActivityTaskManagerService mAtm;
RootWindowContainer mRootWindowContainer;
ActivityTaskSupervisor mSupervisor;
@@ -895,13 +898,16 @@
doReturn(100).when(hardwareBuffer).getHeight();
}
+ private static ComponentName getUniqueComponentName() {
+ return ComponentName.createRelative(DEFAULT_COMPONENT_PACKAGE_NAME,
+ DEFAULT_COMPONENT_CLASS_NAME + sCurrentActivityId++);
+ }
+
/**
* Builder for creating new activities.
*/
protected static class ActivityBuilder {
static final int DEFAULT_FAKE_UID = 12345;
- // An id appended to the end of the component name to make it unique
- private static int sCurrentActivityId = 0;
private final ActivityTaskManagerService mService;
@@ -1077,9 +1083,7 @@
ActivityRecord buildInner() {
if (mComponent == null) {
- final int id = sCurrentActivityId++;
- mComponent = ComponentName.createRelative(DEFAULT_COMPONENT_PACKAGE_NAME,
- DEFAULT_COMPONENT_CLASS_NAME + id);
+ mComponent = getUniqueComponentName();
}
Intent intent = new Intent();
@@ -1388,8 +1392,7 @@
if (mIntent == null) {
mIntent = new Intent();
if (mComponent == null) {
- mComponent = ComponentName.createRelative(DEFAULT_COMPONENT_PACKAGE_NAME,
- DEFAULT_COMPONENT_CLASS_NAME);
+ mComponent = getUniqueComponentName();
}
mIntent.setComponent(mComponent);
mIntent.setFlags(mFlags);
@@ -1422,10 +1425,11 @@
doNothing().when(rootTask).startActivityLocked(
any(), any(), anyBoolean(), anyBoolean(), any(), any());
- // Create child task with activity.
+ // Create child activity.
if (mCreateActivity) {
new ActivityBuilder(mSupervisor.mService)
.setTask(task)
+ .setComponent(mComponent)
.build();
if (mOnTop) {
// We move the task to front again in order to regain focus after activity
diff --git a/telephony/java/android/telephony/CarrierConfigManager.java b/telephony/java/android/telephony/CarrierConfigManager.java
index 7d17894..82113f2 100644
--- a/telephony/java/android/telephony/CarrierConfigManager.java
+++ b/telephony/java/android/telephony/CarrierConfigManager.java
@@ -4181,6 +4181,56 @@
"gba_ua_tls_cipher_suite_int";
/**
+ * The data stall recovery timers array in milliseconds, each element is the delay before
+ * performining next recovery action.
+ *
+ * The default value of timers array are: [180000ms, 180000ms, 180000ms] (3 minutes)
+ * Array[0]: It's the timer between RECOVERY_ACTION GET_DATA_CALL_LIST and CLEANUP, if data
+ * stall symptom still occurred, it will perform next recovery action after 180000ms.
+ * Array[1]: It's the timer between RECOVERY_ACTION CLEANUP and RADIO_RESTART, if data stall
+ * symptom still occurred, it will perform next recovery action after 180000ms.
+ * Array[2]: It's the timer between RECOVERY_ACTION RADIO_RESTART and RESET_MODEM, if data stall
+ * symptom still occurred, it will perform next recovery action after 180000ms.
+ *
+ * See the {@code RECOVERY_ACTION_*} constants in
+ * {@link com.android.internal.telephony.data.DataStallRecoveryManager}
+ * @hide
+ */
+ public static final String KEY_DATA_STALL_RECOVERY_TIMERS_LONG_ARRAY =
+ "data_stall_recovery_timers_long_array";
+
+ /**
+ * The data stall recovery action boolean array, we use this array to determine if the
+ * data stall recovery action needs to be skipped.
+ *
+ * For example, if the carrier use the same APN for both of IA and default type,
+ * the data call will not disconnect in modem side (so the RECOVERY_ACTION_CLEANUP
+ * did not effect). In this case, we can config the boolean variable of action
+ * RECOVERY_ACTION_CLEANUP to true, then it can be ignored to speed up the recovery
+ * action procedure.
+ *
+ * The default value of boolean array are: [false, false, false, false]
+ * Array[0]: When performing the recovery action, we can use this boolean value to determine
+ * if we need to perform RECOVERY_ACTION_GET_DATA_CALL_LIST.
+ * Array[1]: If data stall symptom still occurred, we can use this boolean value to determine
+ * if we need to perform RECOVERY_ACTION_CLEANUP. For example, if the carrier use the same APN
+ * for both of IA and default type, the data call will not disconnect in modem side
+ * (so the RECOVERY_ACTION_CLEANUP did not effect). In this case, we can config the boolean
+ * variable of action RECOVERY_ACTION_CLEANUP to true, then it can be ignored to speed up the
+ * recovery action procedure.
+ * Array[2]: If data stall symptom still occurred, we can use this boolean value to determine
+ * if we need to perform RECOVERY_ACTION_RADIO_RESTART.
+ * Array[3]: If data stall symptom still occurred, we can use this boolean value to determine
+ * if we need to perform RECOVERY_ACTION_MODEM_RESET.
+ *
+ * See the {@code RECOVERY_ACTION_*} constants in
+ * {@link com.android.internal.telephony.data.DataStallRecoveryManager}
+ * @hide
+ */
+ public static final String KEY_DATA_STALL_RECOVERY_SHOULD_SKIP_BOOL_ARRAY =
+ "data_stall_recovery_should_skip_bool_array";
+
+ /**
* Configs used by ImsServiceEntitlement.
*/
public static final class ImsServiceEntitlement {
@@ -6172,6 +6222,11 @@
+ "target=GERAN|UTRAN|EUTRAN|NGRAN|IWLAN, type=allowed"});
sDefaults.putInt(KEY_CELLULAR_USAGE_SETTING_INT,
SubscriptionManager.USAGE_SETTING_UNKNOWN);
+ // Default data stall recovery configurations.
+ sDefaults.putLongArray(KEY_DATA_STALL_RECOVERY_TIMERS_LONG_ARRAY,
+ new long[] {180000, 180000, 180000});
+ sDefaults.putBooleanArray(KEY_DATA_STALL_RECOVERY_SHOULD_SKIP_BOOL_ARRAY,
+ new boolean[] {false, false, false, false});
}
/**
diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/helpers/TwoActivitiesAppHelper.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/helpers/TwoActivitiesAppHelper.kt
index 59e8dc8..8fe0029 100644
--- a/tests/FlickerTests/src/com/android/server/wm/flicker/helpers/TwoActivitiesAppHelper.kt
+++ b/tests/FlickerTests/src/com/android/server/wm/flicker/helpers/TwoActivitiesAppHelper.kt
@@ -37,16 +37,21 @@
.launcherStrategy
) : StandardAppHelper(instr, launcherName, component, launcherStrategy) {
fun openSecondActivity(device: UiDevice, wmHelper: WindowManagerStateHelper) {
- val button = device.wait(
- Until.findObject(By.res(getPackage(), "launch_second_activity")),
- FIND_TIMEOUT)
+ val launchActivityButton = By.res(getPackage(), LAUNCH_SECOND_ACTIVITY)
+ val button = device.wait(Until.findObject(launchActivityButton), FIND_TIMEOUT)
require(button != null) {
"Button not found, this usually happens when the device " +
"was left in an unknown state (e.g. in split screen)"
}
button.click()
+
+ device.wait(Until.gone(launchActivityButton), FIND_TIMEOUT)
wmHelper.waitForAppTransitionIdle()
wmHelper.waitForFullScreenApp(component)
}
+
+ companion object {
+ private const val LAUNCH_SECOND_ACTIVITY = "launch_second_activity"
+ }
}
\ No newline at end of file
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 a7a9fe2..19e2c92 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
@@ -86,8 +86,8 @@
// [Step1]: Swipe right from imeTestApp to testApp task
createTag(TAG_IME_VISIBLE)
val displayBounds = WindowUtils.getDisplayBounds(testSpec.startRotation)
- device.swipe(0, displayBounds.bounds.height(),
- displayBounds.bounds.width(), displayBounds.bounds.height(), 50)
+ device.swipe(0, displayBounds.bounds.height,
+ displayBounds.bounds.width, displayBounds.bounds.height, 50)
wmHelper.waitForFullScreenApp(testApp.component)
wmHelper.waitForAppTransitionIdle()
@@ -96,8 +96,8 @@
transitions {
// [Step2]: Swipe left to back to imeTestApp task
val displayBounds = WindowUtils.getDisplayBounds(testSpec.startRotation)
- device.swipe(displayBounds.bounds.width(), displayBounds.bounds.height(),
- 0, displayBounds.bounds.height(), 50)
+ 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/TaskTransitionTest.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/launch/TaskTransitionTest.kt
index 769cb1a..882e128 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
@@ -148,22 +148,28 @@
val displayBounds = WindowUtils.getDisplayBounds(testSpec.startRotation)
testSpec.assertLayers {
- this.coversExactly(displayBounds, LAUNCH_NEW_TASK_ACTIVITY)
+ this.invoke("LAUNCH_NEW_TASK_ACTIVITY coversExactly displayBounds") {
+ it.visibleRegion(LAUNCH_NEW_TASK_ACTIVITY).coversExactly(displayBounds)
+ }
.isInvisible(bgColorLayer)
.then()
// Transitioning
.isVisible(bgColorLayer)
.then()
// Fully transitioned to simple SIMPLE_ACTIVITY
- .coversExactly(displayBounds, SIMPLE_ACTIVITY)
+ .invoke("SIMPLE_ACTIVITY coversExactly displayBounds") {
+ it.visibleRegion(SIMPLE_ACTIVITY).coversExactly(displayBounds)
+ }
.isInvisible(bgColorLayer)
.then()
// Transitioning back
.isVisible(bgColorLayer)
.then()
// Fully transitioned back to LAUNCH_NEW_TASK_ACTIVITY
+ .invoke("LAUNCH_NEW_TASK_ACTIVITY coversExactly displayBounds") {
+ it.visibleRegion(LAUNCH_NEW_TASK_ACTIVITY).coversExactly(displayBounds)
+ }
.isInvisible(bgColorLayer)
- .coversExactly(displayBounds, LAUNCH_NEW_TASK_ACTIVITY)
}
}
diff --git a/tests/HwAccelerationTest/Android.bp b/tests/HwAccelerationTest/Android.bp
index e618ed1..51848f2 100644
--- a/tests/HwAccelerationTest/Android.bp
+++ b/tests/HwAccelerationTest/Android.bp
@@ -32,6 +32,10 @@
"**/*.java",
"**/*.kt",
],
+ static_libs: [
+ "androidx.cardview_cardview",
+ ],
+
platform_apis: true,
certificate: "platform",
}
diff --git a/tests/HwAccelerationTest/AndroidManifest.xml b/tests/HwAccelerationTest/AndroidManifest.xml
index 22fe424..b0ccbd1 100644
--- a/tests/HwAccelerationTest/AndroidManifest.xml
+++ b/tests/HwAccelerationTest/AndroidManifest.xml
@@ -789,6 +789,15 @@
</intent-filter>
</activity>
+ <activity android:name="RenderEffectViewActivity"
+ android:label="RenderEffect/View"
+ android:exported="true">
+ <intent-filter>
+ <action android:name="android.intent.action.MAIN"/>
+ <category android:name="com.android.test.hwui.TEST"/>
+ </intent-filter>
+ </activity>
+
<activity android:name="StretchShaderActivity"
android:label="RenderEffect/Stretch"
android:exported="true">
diff --git a/tests/HwAccelerationTest/res/drawable-nodpi/scratches.png b/tests/HwAccelerationTest/res/drawable-nodpi/scratches.png
new file mode 100644
index 0000000..cc8adf1
--- /dev/null
+++ b/tests/HwAccelerationTest/res/drawable-nodpi/scratches.png
Binary files differ
diff --git a/tests/HwAccelerationTest/res/drawable-nodpi/weather_2.jpg b/tests/HwAccelerationTest/res/drawable-nodpi/weather_2.jpg
new file mode 100644
index 0000000..b5aff10
--- /dev/null
+++ b/tests/HwAccelerationTest/res/drawable-nodpi/weather_2.jpg
Binary files differ
diff --git a/tests/HwAccelerationTest/res/layout/view_runtime_shader.xml b/tests/HwAccelerationTest/res/layout/view_runtime_shader.xml
new file mode 100644
index 0000000..b91377d
--- /dev/null
+++ b/tests/HwAccelerationTest/res/layout/view_runtime_shader.xml
@@ -0,0 +1,205 @@
+<?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.
+ -->
+
+<ScrollView
+ xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:app="http://schemas.android.com/apk/res-auto"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent">
+ <LinearLayout
+ android:id="@+id/TopLayout"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:paddingBottom="8dp"
+ android:orientation="vertical">
+
+ <TextView
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_margin="16dp"
+ android:text="Sample Card #1"/>
+
+ <androidx.cardview.widget.CardView
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:layout_margin="8dp"
+ android:layout_marginBottom="16dp"
+ android:clickable="true"
+ android:focusable="true"
+ android:minHeight="148dp">
+
+ <LinearLayout
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:orientation="vertical">
+
+ <LinearLayout
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:padding="16dp"
+ android:paddingBottom="8dp">
+
+ <ImageView
+ android:layout_width="50dp"
+ android:layout_height="50dp"
+ android:layout_marginEnd="8dp"
+ android:layout_marginRight="8dp"
+ android:contentDescription="Logo"
+ android:src="@drawable/icon"/>
+
+ <LinearLayout
+ android:layout_width="0dp"
+ android:layout_height="wrap_content"
+ android:layout_weight="1.0"
+ android:layout_marginStart="8dp"
+ android:layout_marginLeft="8dp"
+ android:orientation="vertical">
+ <TextView
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:textStyle="bold"
+ android:text="Image Transition"/>
+
+ <TextView
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_marginTop="8dp"
+ android:ellipsize="end"
+ android:maxLines="1"
+ android:text="Touch the image to trigger the animation"/>
+ </LinearLayout>
+ </LinearLayout>
+
+ <com.android.test.hwui.BitmapTransitionView
+ android:layout_width="match_parent"
+ android:layout_height="194dp"
+ android:padding="8dp"/>
+
+ </LinearLayout>
+
+ </androidx.cardview.widget.CardView>
+
+ <TextView
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_margin="16dp"
+ android:text="Sample Card #2"/>
+
+ <androidx.cardview.widget.CardView
+ android:id="@+id/CardView"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:layout_margin="8dp"
+ android:layout_marginBottom="16dp"
+ android:clickable="true"
+ android:focusable="true"
+ android:minHeight="148dp">
+
+ <LinearLayout
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:paddingTop="16dp"
+ android:orientation="vertical">
+
+ <LinearLayout
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:padding="16dp"
+ android:paddingBottom="8dp">
+
+ <ImageView
+ android:layout_width="50dp"
+ android:layout_height="50dp"
+ android:layout_marginEnd="8dp"
+ android:layout_marginRight="8dp"
+ android:contentDescription="Logo"
+ android:src="@drawable/icon"/>
+
+ <LinearLayout
+ android:layout_width="0dp"
+ android:layout_height="wrap_content"
+ android:layout_weight="1.0"
+ android:layout_marginStart="8dp"
+ android:layout_marginLeft="8dp"
+ android:orientation="vertical">
+ <TextView
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:textStyle="bold"
+ android:text="View Group Manipulation"/>
+
+ <TextView
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_marginTop="8dp"
+ android:ellipsize="end"
+ android:maxLines="1"
+ android:text="Tap the card to trigger the animation"/>
+ </LinearLayout>
+ </LinearLayout>
+
+ <ImageView
+ android:layout_width="match_parent"
+ android:layout_height="194dp"
+ android:background="@android:color/transparent"
+ android:src="@drawable/weather_2"/>
+
+ <LinearLayout
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:padding="16dp"
+ android:paddingBottom="8dp"
+ android:orientation="vertical">
+
+ <LinearLayout
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content">
+ <RatingBar
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:contentDescription="Card Rating"
+ android:isIndicator="true"
+ android:numStars="5"
+ android:rating="4.5"
+
+ android:stepSize="0.5"/>
+ <TextView
+ android:layout_width="wrap_content"
+ android:layout_height="match_parent"
+ android:gravity="center_vertical"
+ android:text="Category 4.5 Storm"/>
+ </LinearLayout>
+
+ <TextView
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_marginTop="8dp"
+ android:maxLines="3"
+ android:textIsSelectable="true"
+ android:text="Lorem ipsum dolor sit amet, nec no nominavi scaevola. Per et
+ sint sapientem, nobis perpetua salutandi mei te. Quo tamquam probatus
+ reprehendunt in. Eos esse purto eruditi ea. Enim tation persius ut sea,
+ eos ad consul populo. Ne eum solet altera. Cibo eligendi et est, electram
+ theophrastus te vel eu."/>
+
+ </LinearLayout>
+
+ </LinearLayout>
+
+ </androidx.cardview.widget.CardView>
+ </LinearLayout>
+</ScrollView>
diff --git a/tests/HwAccelerationTest/res/values/styles.xml b/tests/HwAccelerationTest/res/values/styles.xml
index fa5437f..55f4dd69 100644
--- a/tests/HwAccelerationTest/res/values/styles.xml
+++ b/tests/HwAccelerationTest/res/values/styles.xml
@@ -41,4 +41,5 @@
<item name="android:spotShadowAlpha">1</item>
-->
</style>
+
</resources>
diff --git a/tests/HwAccelerationTest/src/com/android/test/hwui/BitmapTransitionView.kt b/tests/HwAccelerationTest/src/com/android/test/hwui/BitmapTransitionView.kt
new file mode 100644
index 0000000..d3ad9e8
--- /dev/null
+++ b/tests/HwAccelerationTest/src/com/android/test/hwui/BitmapTransitionView.kt
@@ -0,0 +1,138 @@
+/*
+ * 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.test.hwui
+
+import android.animation.ValueAnimator
+import android.content.Context
+import android.graphics.BitmapShader
+import android.graphics.Canvas
+import android.graphics.ImageDecoder
+import android.graphics.Matrix
+import android.graphics.Paint
+import android.graphics.RuntimeShader
+import android.graphics.Shader
+import android.util.AttributeSet
+import android.view.View
+
+class BitmapTransitionView @JvmOverloads constructor(
+ context: Context,
+ attrs: AttributeSet? = null,
+ defStyleAttr: Int = 0
+) : View(context, attrs, defStyleAttr) {
+
+ private val mPaint = Paint()
+ private val mImageA = ImageDecoder.decodeBitmap(
+ ImageDecoder.createSource(context.resources, R.drawable.large_photo))
+ private val mImageB = ImageDecoder.decodeBitmap(
+ ImageDecoder.createSource(context.resources, R.drawable.very_large_photo))
+ private val mShaderA = BitmapShader(mImageA, Shader.TileMode.CLAMP, Shader.TileMode.CLAMP)
+ private val mShaderB = BitmapShader(mImageB, Shader.TileMode.CLAMP, Shader.TileMode.CLAMP)
+ private val mShader = RuntimeShader(AGSL, false)
+ private var mCurrentProgress = -1f
+ private var mForwardProgress = true
+ private var mCurrentAnimator = ValueAnimator.ofFloat(-1f, 1f)
+
+ init {
+ isClickable = true
+
+ mCurrentAnimator.duration = 1500
+ mCurrentAnimator.addUpdateListener { animation ->
+ mCurrentProgress = animation.animatedValue as Float
+ postInvalidate()
+ }
+ }
+
+ override fun performClick(): Boolean {
+ if (super.performClick()) return true
+
+ if (mCurrentAnimator.isRunning) {
+ mCurrentAnimator.reverse()
+ return true
+ }
+
+ if (mForwardProgress) {
+ mCurrentAnimator.setFloatValues(-1f, 1f)
+ mForwardProgress = false
+ } else {
+ mCurrentAnimator.setFloatValues(1f, -1f)
+ mForwardProgress = true
+ }
+
+ mCurrentAnimator.start()
+ postInvalidate()
+ return true
+ }
+
+ override fun onSizeChanged(width: Int, height: Int, oldWidth: Int, oldHeight: Int) {
+ val matrixA = Matrix()
+ val matrixB = Matrix()
+
+ matrixA.postScale(width.toFloat() / mImageA.width, height.toFloat() / mImageA.height)
+ matrixB.postScale(width.toFloat() / mImageB.width, height.toFloat() / mImageB.height)
+
+ mShaderA.setLocalMatrix(matrixA)
+ mShaderB.setLocalMatrix(matrixB)
+ }
+
+ override fun onDraw(canvas: Canvas) {
+ super.onDraw(canvas)
+
+ mShader.setInputShader("imageA", mShaderA)
+ mShader.setInputShader("imageB", mShaderB)
+ mShader.setIntUniform("imageDimensions", width, height)
+ mShader.setFloatUniform("progress", mCurrentProgress)
+
+ mPaint.shader = mShader
+ canvas.drawRect(0f, 0f, width.toFloat(), height.toFloat(), mPaint)
+ }
+
+ private companion object {
+ const val AGSL = """
+ uniform shader imageA;
+ uniform shader imageB;
+ uniform ivec2 imageDimensions;
+ uniform float progress;
+
+ const vec2 iSize = vec2(48.0, 48.0);
+ const float iDir = 0.5;
+ const float iRand = 0.81;
+
+ float hash12(vec2 p) {
+ vec3 p3 = fract(vec3(p.xyx) * .1031);
+ p3 += dot(p3, p3.yzx + 33.33);
+ return fract((p3.x + p3.y) * p3.z);
+ }
+
+ float ramp(float2 p) {
+ return mix(hash12(p),
+ dot(p/vec2(imageDimensions), float2(iDir, 1 - iDir)),
+ iRand);
+ }
+
+ half4 main(float2 p) {
+ float2 lowRes = p / iSize;
+ float2 cellCenter = (floor(lowRes) + 0.5) * iSize;
+ float2 posInCell = fract(lowRes) * 2 - 1;
+
+ float v = ramp(cellCenter) + progress;
+ float distToCenter = max(abs(posInCell.x), abs(posInCell.y));
+
+ return distToCenter > v ? imageA.eval(p).rgb1 : imageB.eval(p).rgb1;
+ }
+ """
+ }
+}
\ No newline at end of file
diff --git a/tests/HwAccelerationTest/src/com/android/test/hwui/RenderEffectViewActivity.kt b/tests/HwAccelerationTest/src/com/android/test/hwui/RenderEffectViewActivity.kt
new file mode 100644
index 0000000..06280d2
--- /dev/null
+++ b/tests/HwAccelerationTest/src/com/android/test/hwui/RenderEffectViewActivity.kt
@@ -0,0 +1,109 @@
+/*
+ * 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.test.hwui
+
+import android.animation.ValueAnimator
+import android.app.Activity
+import android.graphics.Bitmap
+import android.graphics.BitmapShader
+import android.graphics.ImageDecoder
+import android.graphics.Matrix
+import android.graphics.Shader
+import android.graphics.RenderEffect
+import android.graphics.RuntimeShader
+import android.os.Bundle
+import android.view.View
+
+class RenderEffectViewActivity : Activity() {
+
+ private val mDropsShader = RuntimeShader(dropsAGSL, false)
+ private var mDropsAnimator = ValueAnimator.ofFloat(0f, 1f)
+ private var mStartTime = System.currentTimeMillis()
+ private lateinit var mScratchesImage: Bitmap
+ private lateinit var mScratchesShader: BitmapShader
+
+ override fun onCreate(savedInstanceState: Bundle?) {
+ super.onCreate(savedInstanceState)
+ setContentView(R.layout.view_runtime_shader)
+
+ val dropsView = findViewById<View>(R.id.CardView)!!
+ dropsView.isClickable = true
+ dropsView.setOnClickListener {
+ if (mDropsAnimator.isRunning) {
+ mDropsAnimator.cancel()
+ dropsView.setRenderEffect(null)
+ } else {
+ mDropsAnimator.start()
+ }
+ }
+
+ val imgSource = ImageDecoder.createSource(resources, R.drawable.scratches)
+ mScratchesImage = ImageDecoder.decodeBitmap(imgSource)
+ mScratchesShader = BitmapShader(mScratchesImage,
+ Shader.TileMode.CLAMP, Shader.TileMode.CLAMP)
+
+ mDropsAnimator.duration = 1000
+ mDropsAnimator.repeatCount = ValueAnimator.INFINITE
+ mDropsAnimator.addUpdateListener { _ ->
+ val viewWidth = dropsView.width.toFloat()
+ val viewHeight = dropsView.height.toFloat()
+ val scratchesMatrix = Matrix()
+ scratchesMatrix.postScale(viewWidth / mScratchesImage.width,
+ viewHeight / mScratchesImage.height)
+ mScratchesShader.setLocalMatrix(scratchesMatrix)
+
+ mDropsShader.setInputShader("scratches", mScratchesShader)
+ mDropsShader.setFloatUniform("elapsedSeconds",
+ (System.currentTimeMillis() - mStartTime) / 1000f)
+ mDropsShader.setFloatUniform("viewDimensions", viewWidth, viewHeight)
+
+ val dropsEffect = RenderEffect.createRuntimeShaderEffect(mDropsShader, "background")
+ val blurEffect = RenderEffect.createBlurEffect(10f, 10f, Shader.TileMode.CLAMP)
+
+ dropsView.setRenderEffect(RenderEffect.createChainEffect(dropsEffect, blurEffect))
+ }
+ }
+
+ private companion object {
+ const val dropsAGSL = """
+ uniform float elapsedSeconds;
+ uniform vec2 viewDimensions;
+ uniform shader background;
+ uniform shader scratches;
+
+ vec2 dropsUV(vec2 fragCoord ) {
+ vec2 uv = fragCoord.xy / viewDimensions.xy; // 0 <> 1
+ vec2 offs = vec2(0.);
+ return (offs + uv).xy;
+ }
+
+ const vec3 iFrostColorRGB = vec3(0.5, 0.5, 0.5);
+ const float iFrostColorAlpha = .3;
+
+ half4 main(float2 fragCoord) {
+ half4 bg = background.eval(dropsUV(fragCoord)*viewDimensions.xy);
+ float2 scratchCoord = fragCoord.xy / viewDimensions.xy;;
+ scratchCoord += 1.5;
+ scratchCoord = mod(scratchCoord, 1);
+ half scratch = scratches.eval(scratchCoord*viewDimensions.xy).r;
+ bg.rgb = mix(bg.rgb, iFrostColorRGB, iFrostColorAlpha);
+ bg.rgb = mix(bg.rgb, half3(1), pow(scratch,3));
+ return bg;
+ }
+ """
+ }
+}
\ No newline at end of file
diff --git a/tests/StagedInstallTest/app/src/com/android/tests/stagedinstallinternal/StagedInstallInternalTest.java b/tests/StagedInstallTest/app/src/com/android/tests/stagedinstallinternal/StagedInstallInternalTest.java
index b21d7b5..869999e 100644
--- a/tests/StagedInstallTest/app/src/com/android/tests/stagedinstallinternal/StagedInstallInternalTest.java
+++ b/tests/StagedInstallTest/app/src/com/android/tests/stagedinstallinternal/StagedInstallInternalTest.java
@@ -303,11 +303,14 @@
assertThat(InstallUtils.getInstalledVersion("test.apex.rebootless")).isEqualTo(1);
TestApp apex = new TestApp("apex", "test.apex.rebootless", 2,
/* isApex= */ true, "test.rebootless_apex_v2.apex");
+ String expectedFailMessage = "Update of APEX package test.apex.rebootless is not allowed "
+ + "for com.android.tests.stagedinstallinternal";
InstallUtils.commitExpectingFailure(
- AssertionError.class,
- "Update of APEX package test.apex.rebootless is not allowed "
- + "for com.android.tests.stagedinstallinternal",
+ AssertionError.class, expectedFailMessage,
Install.single(apex).setBypassAllowedApexUpdateCheck(false).setStaged());
+ InstallUtils.commitExpectingFailure(
+ AssertionError.class, expectedFailMessage,
+ Install.multi(apex).setBypassAllowedApexUpdateCheck(false).setStaged());
}
@Test
@@ -315,11 +318,14 @@
assertThat(InstallUtils.getInstalledVersion("test.apex.rebootless")).isEqualTo(1);
TestApp apex = new TestApp("apex", "test.apex.rebootless", 2,
/* isApex= */ true, "test.rebootless_apex_v2.apex");
+ String expectedFailMessage = "Update of APEX package test.apex.rebootless is not allowed "
+ + "for com.android.tests.stagedinstallinternal";
InstallUtils.commitExpectingFailure(
- AssertionError.class,
- "Update of APEX package test.apex.rebootless is not allowed "
- + "for com.android.tests.stagedinstallinternal",
+ AssertionError.class, expectedFailMessage,
Install.single(apex).setBypassAllowedApexUpdateCheck(false));
+ InstallUtils.commitExpectingFailure(
+ AssertionError.class, expectedFailMessage,
+ Install.multi(apex).setBypassAllowedApexUpdateCheck(false));
}
@Test
@@ -327,11 +333,14 @@
assertThat(InstallUtils.getInstalledVersion("test.apex.rebootless")).isEqualTo(1);
TestApp apex = new TestApp("apex", "test.apex.rebootless", 2,
/* isApex= */ true, "test.rebootless_apex_v2.apex");
+ String expectedFailMessage = "Update of APEX package test.apex.rebootless is not allowed "
+ + "for com.android.tests.stagedinstallinternal";
InstallUtils.commitExpectingFailure(
- AssertionError.class,
- "Update of APEX package test.apex.rebootless is not allowed "
- + "for com.android.tests.stagedinstallinternal",
+ AssertionError.class, expectedFailMessage,
Install.single(apex).setBypassAllowedApexUpdateCheck(false).setStaged());
+ InstallUtils.commitExpectingFailure(
+ AssertionError.class, expectedFailMessage,
+ Install.multi(apex).setBypassAllowedApexUpdateCheck(false).setStaged());
}
@Test
@@ -339,11 +348,14 @@
assertThat(InstallUtils.getInstalledVersion("test.apex.rebootless")).isEqualTo(1);
TestApp apex = new TestApp("apex", "test.apex.rebootless", 2,
/* isApex= */ true, "test.rebootless_apex_v2.apex");
+ String expectedFailMessage = "Update of APEX package test.apex.rebootless is not allowed "
+ + "for com.android.tests.stagedinstallinternal";
InstallUtils.commitExpectingFailure(
- AssertionError.class,
- "Update of APEX package test.apex.rebootless is not allowed "
- + "for com.android.tests.stagedinstallinternal",
+ AssertionError.class, expectedFailMessage,
Install.single(apex).setBypassAllowedApexUpdateCheck(false));
+ InstallUtils.commitExpectingFailure(
+ AssertionError.class, expectedFailMessage,
+ Install.multi(apex).setBypassAllowedApexUpdateCheck(false));
}
@Test