diff options
419 files changed, 14166 insertions, 5147 deletions
diff --git a/Android.bp b/Android.bp index 1a73e9d323ff..35f97ac57281 100644 --- a/Android.bp +++ b/Android.bp @@ -223,6 +223,9 @@ filegroup { "media/java/**/*.java", "media/java/**/*.aidl", ], + exclude_srcs: [ + ":framework-media-tv-tunerresourcemanager-sources-aidl", + ], path: "media/java", } @@ -630,6 +633,7 @@ java_defaults { // in favor of an API stubs dependency in java_library "framework" below. "mimemap", "av-types-aidl-java", + "tv_tuner_resource_manager_aidl_interface-java", "soundtrigger_middleware-aidl-java", "modules-utils-os", ], @@ -1115,6 +1119,7 @@ filegroup { "core/java/android/os/incremental/IStorageLoadingProgressListener.aidl", "core/java/android/os/incremental/IncrementalNewFileParams.aidl", "core/java/android/os/incremental/IStorageHealthListener.aidl", + "core/java/android/os/incremental/PerUidReadTimeouts.aidl", "core/java/android/os/incremental/StorageHealthCheckParams.aidl", ], path: "core/java", diff --git a/apct-tests/perftests/multiuser/OWNERS b/apct-tests/perftests/multiuser/OWNERS new file mode 100644 index 000000000000..1a206cb27c3b --- /dev/null +++ b/apct-tests/perftests/multiuser/OWNERS @@ -0,0 +1 @@ +include /MULTIUSER_OWNERS
\ No newline at end of file diff --git a/apex/jobscheduler/framework/java/android/os/IDeviceIdleController.aidl b/apex/jobscheduler/framework/java/android/os/IDeviceIdleController.aidl index 7d02d2d6cd29..5693abe4d4e1 100644 --- a/apex/jobscheduler/framework/java/android/os/IDeviceIdleController.aidl +++ b/apex/jobscheduler/framework/java/android/os/IDeviceIdleController.aidl @@ -41,7 +41,8 @@ interface IDeviceIdleController { int[] getAppIdTempWhitelist(); boolean isPowerSaveWhitelistExceptIdleApp(String name); boolean isPowerSaveWhitelistApp(String name); - @UnsupportedAppUsage + @UnsupportedAppUsage(maxTargetSdk = 30, + publicAlternatives = "Use SystemApi {@code PowerWhitelistManager#whitelistAppTemporarily(String, int, String)}.") void addPowerSaveTempWhitelistApp(String name, long duration, int userId, String reason); long addPowerSaveTempWhitelistAppForMms(String name, int userId, String reason); long addPowerSaveTempWhitelistAppForSms(String name, int userId, String reason); diff --git a/apex/jobscheduler/service/java/com/android/server/AppStateTrackerImpl.java b/apex/jobscheduler/service/java/com/android/server/AppStateTrackerImpl.java index 4dc9cf850893..cc3e9c33fe42 100644 --- a/apex/jobscheduler/service/java/com/android/server/AppStateTrackerImpl.java +++ b/apex/jobscheduler/service/java/com/android/server/AppStateTrackerImpl.java @@ -274,11 +274,8 @@ public class AppStateTrackerImpl implements AppStateTracker { int uid, @NonNull String packageName) { updateJobsForUidPackage(uid, packageName, sender.isUidActive(uid)); - if (!sender.areAlarmsRestricted(uid, packageName, /*allowWhileIdle=*/ false)) { + if (!sender.areAlarmsRestricted(uid, packageName)) { unblockAlarmsForUidPackage(uid, packageName); - } else if (!sender.areAlarmsRestricted(uid, packageName, /*allowWhileIdle=*/ true)) { - // we need to deliver the allow-while-idle alarms for this uid, package - unblockAllUnrestrictedAlarms(); } if (!sender.isRunAnyInBackgroundAppOpsAllowed(uid, packageName)) { @@ -302,6 +299,7 @@ public class AppStateTrackerImpl implements AppStateTracker { final boolean isActive = sender.isUidActive(uid); updateJobsForUid(uid, isActive); + updateAlarmsForUid(uid); if (isActive) { unblockAlarmsForUid(uid); @@ -313,7 +311,7 @@ public class AppStateTrackerImpl implements AppStateTracker { */ private void onPowerSaveUnexempted(AppStateTrackerImpl sender) { updateAllJobs(); - unblockAllUnrestrictedAlarms(); + updateAllAlarms(); } /** @@ -322,6 +320,8 @@ public class AppStateTrackerImpl implements AppStateTracker { */ private void onPowerSaveExemptionListChanged(AppStateTrackerImpl sender) { updateAllJobs(); + updateAllAlarms(); + unblockAllUnrestrictedAlarms(); } /** @@ -344,7 +344,7 @@ public class AppStateTrackerImpl implements AppStateTracker { private void onExemptedBucketChanged(AppStateTrackerImpl sender) { // This doesn't happen very often, so just re-evaluate all jobs / alarms. updateAllJobs(); - unblockAllUnrestrictedAlarms(); + updateAllAlarms(); } /** @@ -352,10 +352,7 @@ public class AppStateTrackerImpl implements AppStateTracker { */ private void onForceAllAppsStandbyChanged(AppStateTrackerImpl sender) { updateAllJobs(); - - if (!sender.isForceAllAppsStandbyEnabled()) { - unblockAllUnrestrictedAlarms(); - } + updateAllAlarms(); } /** @@ -387,6 +384,19 @@ public class AppStateTrackerImpl implements AppStateTracker { } /** + * Called when all alarms need to be re-evaluated for eligibility based on + * {@link #areAlarmsRestrictedByBatterySaver}. + */ + public void updateAllAlarms() { + } + + /** + * Called when the given uid state changes to active / idle. + */ + public void updateAlarmsForUid(int uid) { + } + + /** * Called when the job restrictions for multiple UIDs might have changed, so the alarm * manager should re-evaluate all restrictions for all blocked jobs. */ @@ -918,7 +928,7 @@ public class AppStateTrackerImpl implements AppStateTracker { // Feature flag for forced app standby changed. final boolean unblockAlarms; synchronized (mLock) { - unblockAlarms = !mForcedAppStandbyEnabled && !mForceAllAppsStandby; + unblockAlarms = !mForcedAppStandbyEnabled; } for (Listener l : cloneListeners()) { l.updateAllJobs(); @@ -1109,53 +1119,64 @@ public class AppStateTrackerImpl implements AppStateTracker { } /** - * @return whether alarms should be restricted for a UID package-name. - */ - public boolean areAlarmsRestricted(int uid, @NonNull String packageName, - boolean isExemptOnBatterySaver) { - return isRestricted(uid, packageName, /*useTempExemptionListToo=*/ false, - isExemptOnBatterySaver); - } - - /** - * @return whether jobs should be restricted for a UID package-name. + * @return whether alarms should be restricted for a UID package-name, due to explicit + * user-forced app standby. Use {{@link #areAlarmsRestrictedByBatterySaver} to check for + * restrictions induced by battery saver. */ - public boolean areJobsRestricted(int uid, @NonNull String packageName, - boolean hasForegroundExemption) { - return isRestricted(uid, packageName, /*useTempExemptionListToo=*/ true, - hasForegroundExemption); + public boolean areAlarmsRestricted(int uid, @NonNull String packageName) { + if (isUidActive(uid)) { + return false; + } + synchronized (mLock) { + final int appId = UserHandle.getAppId(uid); + if (ArrayUtils.contains(mPowerExemptAllAppIds, appId)) { + return false; + } + return (mForcedAppStandbyEnabled && isRunAnyRestrictedLocked(uid, packageName)); + } } /** - * @return whether foreground services should be suppressed in the background - * due to forced app standby for the given app + * @return whether alarms should be restricted when due to battery saver. */ - public boolean areForegroundServicesRestricted(int uid, @NonNull String packageName) { + public boolean areAlarmsRestrictedByBatterySaver(int uid, @NonNull String packageName) { + if (isUidActive(uid)) { + return false; + } synchronized (mLock) { - return isRunAnyRestrictedLocked(uid, packageName); + final int appId = UserHandle.getAppId(uid); + if (ArrayUtils.contains(mPowerExemptAllAppIds, appId)) { + return false; + } + final int userId = UserHandle.getUserId(uid); + if (mAppStandbyInternal.isAppIdleEnabled() && !mAppStandbyInternal.isInParole() + && mExemptedBucketPackages.contains(userId, packageName)) { + return false; + } + return mForceAllAppsStandby; } } + /** - * @return whether force-app-standby is effective for a UID package-name. + * @return whether jobs should be restricted for a UID package-name. This could be due to + * battery saver or user-forced app standby */ - private boolean isRestricted(int uid, @NonNull String packageName, - boolean useTempExemptionListToo, boolean exemptOnBatterySaver) { + public boolean areJobsRestricted(int uid, @NonNull String packageName, + boolean hasForegroundExemption) { if (isUidActive(uid)) { return false; } synchronized (mLock) { final int appId = UserHandle.getAppId(uid); - if (ArrayUtils.contains(mPowerExemptAllAppIds, appId)) { - return false; - } - if (useTempExemptionListToo && ArrayUtils.contains(mTempExemptAppIds, appId)) { + if (ArrayUtils.contains(mPowerExemptAllAppIds, appId) + || ArrayUtils.contains(mTempExemptAppIds, appId)) { return false; } if (mForcedAppStandbyEnabled && isRunAnyRestrictedLocked(uid, packageName)) { return true; } - if (exemptOnBatterySaver) { + if (hasForegroundExemption) { return false; } final int userId = UserHandle.getUserId(uid); diff --git a/apex/jobscheduler/service/java/com/android/server/alarm/Alarm.java b/apex/jobscheduler/service/java/com/android/server/alarm/Alarm.java index a8c0f0ec3e00..657c368d0aee 100644 --- a/apex/jobscheduler/service/java/com/android/server/alarm/Alarm.java +++ b/apex/jobscheduler/service/java/com/android/server/alarm/Alarm.java @@ -42,7 +42,7 @@ import java.util.Date; */ class Alarm { @VisibleForTesting - public static final int NUM_POLICIES = 3; + public static final int NUM_POLICIES = 4; /** * Index used to store the time the alarm was requested to expire. To be used with * {@link #setPolicyElapsed(int, long)}. @@ -59,6 +59,12 @@ class Alarm { */ public static final int DEVICE_IDLE_POLICY_INDEX = 2; + /** + * Index used to store the earliest time the alarm can expire based on battery saver policy. + * To be used with {@link #setPolicyElapsed(int, long)}. + */ + public static final int BATTERY_SAVER_POLICY_INDEX = 3; + public final int type; /** * The original trigger time supplied by the caller. This can be in the elapsed or rtc time base @@ -223,6 +229,8 @@ class Alarm { return "app_standby"; case DEVICE_IDLE_POLICY_INDEX: return "device_idle"; + case BATTERY_SAVER_POLICY_INDEX: + return "battery_saver"; default: return "--unknown--"; } diff --git a/apex/jobscheduler/service/java/com/android/server/alarm/AlarmManagerService.java b/apex/jobscheduler/service/java/com/android/server/alarm/AlarmManagerService.java index 7842d487dfb2..aa46cfdc5c8a 100644 --- a/apex/jobscheduler/service/java/com/android/server/alarm/AlarmManagerService.java +++ b/apex/jobscheduler/service/java/com/android/server/alarm/AlarmManagerService.java @@ -28,6 +28,7 @@ import static android.content.pm.PackageManager.MATCH_SYSTEM_ONLY; import static android.os.UserHandle.USER_SYSTEM; import static com.android.server.alarm.Alarm.APP_STANDBY_POLICY_INDEX; +import static com.android.server.alarm.Alarm.BATTERY_SAVER_POLICY_INDEX; import static com.android.server.alarm.Alarm.DEVICE_IDLE_POLICY_INDEX; import static com.android.server.alarm.Alarm.REQUESTER_POLICY_INDEX; @@ -156,6 +157,7 @@ public class AlarmManagerService extends SystemService { static final int TICK_HISTORY_DEPTH = 10; static final long MILLIS_IN_DAY = 24 * 60 * 60 * 1000; + static final long INDEFINITE_DELAY = 365 * MILLIS_IN_DAY; // Indices into the KEYS_APP_STANDBY_QUOTAS array. static final int ACTIVE_INDEX = 0; @@ -964,8 +966,7 @@ public class AlarmManagerService extends SystemService { * Check all alarms in {@link #mPendingBackgroundAlarms} and send the ones that are not * restricted. * - * This is only called when the global "force all apps-standby" flag changes or when the - * power save whitelist changes, so it's okay to be slow. + * This is only called when the power save whitelist changes, so it's okay to be slow. */ void sendAllUnrestrictedPendingBackgroundAlarmsLocked() { final ArrayList<Alarm> alarmsToDeliver = new ArrayList<>(); @@ -984,7 +985,6 @@ public class AlarmManagerService extends SystemService { Predicate<Alarm> isBackgroundRestricted) { for (int uidIndex = pendingAlarms.size() - 1; uidIndex >= 0; uidIndex--) { - final int uid = pendingAlarms.keyAt(uidIndex); final ArrayList<Alarm> alarmsForUid = pendingAlarms.valueAt(uidIndex); for (int alarmIndex = alarmsForUid.size() - 1; alarmIndex >= 0; alarmIndex--) { @@ -1620,6 +1620,44 @@ public class AlarmManagerService extends SystemService { } /** + * Adjusts the delivery time of the alarm based on battery saver rules. + * + * @param alarm The alarm to adjust + * @return {@code true} if the alarm delivery time was updated. + */ + private boolean adjustDeliveryTimeBasedOnBatterySaver(Alarm alarm) { + final long nowElapsed = mInjector.getElapsedRealtime(); + if (isExemptFromBatterySaver(alarm)) { + return false; + } + + if (!(mAppStateTracker != null && mAppStateTracker.areAlarmsRestrictedByBatterySaver( + alarm.creatorUid, alarm.sourcePackage))) { + return alarm.setPolicyElapsed(BATTERY_SAVER_POLICY_INDEX, nowElapsed); + } + + final long batterSaverPolicyElapsed; + if ((alarm.flags & (AlarmManager.FLAG_ALLOW_WHILE_IDLE_UNRESTRICTED)) != 0) { + // Unrestricted. + batterSaverPolicyElapsed = nowElapsed; + } else if ((alarm.flags & AlarmManager.FLAG_ALLOW_WHILE_IDLE) != 0) { + // Allowed but limited. + final long minDelay; + if (mUseAllowWhileIdleShortTime.get(alarm.creatorUid)) { + minDelay = mConstants.ALLOW_WHILE_IDLE_SHORT_TIME; + } else { + minDelay = mConstants.ALLOW_WHILE_IDLE_LONG_TIME; + } + final long lastDispatch = mLastAllowWhileIdleDispatch.get(alarm.creatorUid, 0); + batterSaverPolicyElapsed = (lastDispatch == 0) ? nowElapsed : lastDispatch + minDelay; + } else { + // Not allowed. + batterSaverPolicyElapsed = nowElapsed + INDEFINITE_DELAY; + } + return alarm.setPolicyElapsed(BATTERY_SAVER_POLICY_INDEX, batterSaverPolicyElapsed); + } + + /** * Adjusts the delivery time of the alarm based on device_idle (doze) rules. * * @param alarm The alarm to adjust @@ -1756,6 +1794,7 @@ public class AlarmManagerService extends SystemService { if (a.alarmClock != null) { mNextAlarmClockMayChange = true; } + adjustDeliveryTimeBasedOnBatterySaver(a); adjustDeliveryTimeBasedOnBucketLocked(a); mAlarmStore.add(a); rescheduleKernelAlarmsLocked(); @@ -2230,14 +2269,6 @@ public class AlarmManagerService extends SystemService { pw.print(": "); final long lastTime = mLastAllowWhileIdleDispatch.valueAt(i); TimeUtils.formatDuration(lastTime, nowELAPSED, pw); - - final long minInterval = getWhileIdleMinIntervalLocked(uid); - pw.print(" Next allowed:"); - TimeUtils.formatDuration(lastTime + minInterval, nowELAPSED, pw); - pw.print(" ("); - TimeUtils.formatDuration(minInterval, 0, pw); - pw.print(")"); - pw.println(); } pw.decreaseIndent(); @@ -2511,8 +2542,6 @@ public class AlarmManagerService extends SystemService { proto.write(AlarmManagerServiceDumpProto.LastAllowWhileIdleDispatch.UID, uid); proto.write(AlarmManagerServiceDumpProto.LastAllowWhileIdleDispatch.TIME_MS, lastTime); - proto.write(AlarmManagerServiceDumpProto.LastAllowWhileIdleDispatch.NEXT_ALLOWED_MS, - lastTime + getWhileIdleMinIntervalLocked(uid)); proto.end(token); } @@ -3119,30 +3148,36 @@ public class AlarmManagerService extends SystemService { } } + private boolean isExemptFromBatterySaver(Alarm alarm) { + if (alarm.alarmClock != null) { + return true; + } + if ((alarm.operation != null) + && (alarm.operation.isActivity() || alarm.operation.isForegroundService())) { + return true; + } + if (UserHandle.isCore(alarm.creatorUid)) { + return true; + } + return false; + } + private boolean isBackgroundRestricted(Alarm alarm) { - boolean exemptOnBatterySaver = (alarm.flags & FLAG_ALLOW_WHILE_IDLE) != 0; if (alarm.alarmClock != null) { // Don't defer alarm clocks return false; } - if (alarm.operation != null) { - if (alarm.operation.isActivity()) { - // Don't defer starting actual UI - return false; - } - if (alarm.operation.isForegroundService()) { - // FG service alarms are nearly as important; consult AST policy - exemptOnBatterySaver = true; - } + if (alarm.operation != null && alarm.operation.isActivity()) { + // Don't defer starting actual UI + return false; } final String sourcePackage = alarm.sourcePackage; final int sourceUid = alarm.creatorUid; if (UserHandle.isCore(sourceUid)) { return false; } - return (mAppStateTracker != null) && - mAppStateTracker.areAlarmsRestricted(sourceUid, sourcePackage, - exemptOnBatterySaver); + return (mAppStateTracker != null) && mAppStateTracker.areAlarmsRestricted(sourceUid, + sourcePackage); } private static native long init(); @@ -3153,46 +3188,10 @@ public class AlarmManagerService extends SystemService { private static native int setKernelTimezone(long nativeData, int minuteswest); private static native long getNextAlarm(long nativeData, int type); - private long getWhileIdleMinIntervalLocked(int uid) { - final boolean ebs = (mAppStateTracker != null) - && mAppStateTracker.isForceAllAppsStandbyEnabled(); - - if (!ebs || mUseAllowWhileIdleShortTime.get(uid)) { - // if the last allow-while-idle went off while uid was fg, or the uid - // recently came into fg, don't block the alarm for long. - return mConstants.ALLOW_WHILE_IDLE_SHORT_TIME; - } - return mConstants.ALLOW_WHILE_IDLE_LONG_TIME; - } - boolean triggerAlarmsLocked(ArrayList<Alarm> triggerList, final long nowELAPSED) { boolean hasWakeup = false; final ArrayList<Alarm> pendingAlarms = mAlarmStore.removePendingAlarms(nowELAPSED); for (final Alarm alarm : pendingAlarms) { - if ((alarm.flags & AlarmManager.FLAG_ALLOW_WHILE_IDLE) != 0) { - // If this is an ALLOW_WHILE_IDLE alarm, we constrain how frequently the app can - // schedule such alarms. The first such alarm from an app is always delivered. - final long lastTime = mLastAllowWhileIdleDispatch.get(alarm.creatorUid, -1); - final long minTime = lastTime + getWhileIdleMinIntervalLocked(alarm.creatorUid); - if (lastTime >= 0 && nowELAPSED < minTime) { - // Whoops, it hasn't been long enough since the last ALLOW_WHILE_IDLE - // alarm went off for this app. Reschedule the alarm to be in the - // correct time period. - alarm.setPolicyElapsed(REQUESTER_POLICY_INDEX, minTime); - if (RECORD_DEVICE_IDLE_ALARMS) { - IdleDispatchEntry ent = new IdleDispatchEntry(); - ent.uid = alarm.uid; - ent.pkg = alarm.operation.getCreatorPackage(); - ent.tag = alarm.operation.getTag(""); - ent.op = "RESCHEDULE"; - ent.elapsedRealtime = nowELAPSED; - ent.argRealtime = lastTime; - mAllowWhileIdleDispatches.add(ent); - } - setImplLocked(alarm); - continue; - } - } if (isBackgroundRestricted(alarm)) { // Alarms with FLAG_WAKE_FROM_IDLE or mPendingIdleUntil alarm are not deferred if (DEBUG_BG_LIMIT) { @@ -3924,8 +3923,41 @@ public class AlarmManagerService extends SystemService { } private final Listener mForceAppStandbyListener = new Listener() { + + @Override + public void updateAllAlarms() { + // Called when: + // 1. Power exemption list changes, + // 2. Battery saver state is toggled, + // 3. Any package is moved into or out of the EXEMPTED bucket. + synchronized (mLock) { + if (mAlarmStore.updateAlarmDeliveries( + a -> adjustDeliveryTimeBasedOnBatterySaver(a))) { + rescheduleKernelAlarmsLocked(); + } + } + } + + @Override + public void updateAlarmsForUid(int uid) { + // Called when the given uid's state switches b/w active and idle. + synchronized (mLock) { + if (mAlarmStore.updateAlarmDeliveries(a -> { + if (a.creatorUid != uid) { + return false; + } + return adjustDeliveryTimeBasedOnBatterySaver(a); + })) { + rescheduleKernelAlarmsLocked(); + } + } + } + @Override public void unblockAllUnrestrictedAlarms() { + // Called when: + // 1. Power exemption list changes, + // 2. User FAS feature is disabled. synchronized (mLock) { sendAllUnrestrictedPendingBackgroundAlarmsLocked(); } @@ -3934,12 +3966,14 @@ public class AlarmManagerService extends SystemService { @Override public void unblockAlarmsForUid(int uid) { synchronized (mLock) { + // Called when the given uid becomes active. sendPendingBackgroundAlarmsLocked(uid, null); } } @Override public void unblockAlarmsForUidPackage(int uid, String packageName) { + // Called when user turns off FAS for this (uid, package). synchronized (mLock) { sendPendingBackgroundAlarmsLocked(uid, packageName); } @@ -3950,9 +3984,14 @@ public class AlarmManagerService extends SystemService { synchronized (mLock) { if (foreground) { mUseAllowWhileIdleShortTime.put(uid, true); - - // Note we don't have to drain the pending while-idle alarms here, because - // this event should coincide with unblockAlarmsForUid(). + if (mAlarmStore.updateAlarmDeliveries(a -> { + if (a.creatorUid != uid || (a.flags & FLAG_ALLOW_WHILE_IDLE) == 0) { + return false; + } + return adjustDeliveryTimeBasedOnBatterySaver(a); + })) { + rescheduleKernelAlarmsLocked(); + } } } } @@ -4236,18 +4275,20 @@ public class AlarmManagerService extends SystemService { if (allowWhileIdle) { // Record the last time this uid handled an ALLOW_WHILE_IDLE alarm. mLastAllowWhileIdleDispatch.put(alarm.creatorUid, nowELAPSED); - mAlarmStore.updateAlarmDeliveries(a -> { - if (a.creatorUid != alarm.creatorUid) { - return false; - } - return adjustDeliveryTimeBasedOnDeviceIdle(a); - }); if ((mAppStateTracker == null) || mAppStateTracker.isUidInForeground(alarm.creatorUid)) { mUseAllowWhileIdleShortTime.put(alarm.creatorUid, true); } else { mUseAllowWhileIdleShortTime.put(alarm.creatorUid, false); } + mAlarmStore.updateAlarmDeliveries(a -> { + if (a.creatorUid != alarm.creatorUid + || (a.flags & FLAG_ALLOW_WHILE_IDLE) == 0) { + return false; + } + return adjustDeliveryTimeBasedOnDeviceIdle(a) + | adjustDeliveryTimeBasedOnBatterySaver(a); + }); if (RECORD_DEVICE_IDLE_ALARMS) { IdleDispatchEntry ent = new IdleDispatchEntry(); ent.uid = alarm.uid; diff --git a/core/api/current.txt b/core/api/current.txt index 4c3b1537727c..5a020d4382dd 100644 --- a/core/api/current.txt +++ b/core/api/current.txt @@ -12377,6 +12377,7 @@ package android.content.pm { field public static final String FEATURE_TOUCHSCREEN_MULTITOUCH = "android.hardware.touchscreen.multitouch"; field public static final String FEATURE_TOUCHSCREEN_MULTITOUCH_DISTINCT = "android.hardware.touchscreen.multitouch.distinct"; field public static final String FEATURE_TOUCHSCREEN_MULTITOUCH_JAZZHAND = "android.hardware.touchscreen.multitouch.jazzhand"; + field public static final String FEATURE_TRANSLATION = "android.software.translation"; field public static final String FEATURE_USB_ACCESSORY = "android.hardware.usb.accessory"; field public static final String FEATURE_USB_HOST = "android.hardware.usb.host"; field public static final String FEATURE_VERIFIED_BOOT = "android.software.verified_boot"; @@ -30377,9 +30378,9 @@ package android.os { public class DropBoxManager { ctor protected DropBoxManager(); - method public void addData(String, byte[], int); - method public void addFile(String, java.io.File, int) throws java.io.IOException; - method public void addText(String, String); + method public void addData(@NonNull String, @Nullable byte[], int); + method public void addFile(@NonNull String, @NonNull java.io.File, int) throws java.io.IOException; + method public void addText(@NonNull String, @NonNull String); method @Nullable @RequiresPermission(allOf={android.Manifest.permission.READ_LOGS, android.Manifest.permission.PACKAGE_USAGE_STATS}) public android.os.DropBoxManager.Entry getNextEntry(String, long); method public boolean isTagEnabled(String); field public static final String ACTION_DROPBOX_ENTRY_ADDED = "android.intent.action.DROPBOX_ENTRY_ADDED"; @@ -30392,17 +30393,17 @@ package android.os { } public static class DropBoxManager.Entry implements java.io.Closeable android.os.Parcelable { - ctor public DropBoxManager.Entry(String, long); - ctor public DropBoxManager.Entry(String, long, String); - ctor public DropBoxManager.Entry(String, long, byte[], int); - ctor public DropBoxManager.Entry(String, long, android.os.ParcelFileDescriptor, int); - ctor public DropBoxManager.Entry(String, long, java.io.File, int) throws java.io.IOException; + ctor public DropBoxManager.Entry(@NonNull String, long); + ctor public DropBoxManager.Entry(@NonNull String, long, @NonNull String); + ctor public DropBoxManager.Entry(@NonNull String, long, @Nullable byte[], int); + ctor public DropBoxManager.Entry(@NonNull String, long, @Nullable android.os.ParcelFileDescriptor, int); + ctor public DropBoxManager.Entry(@NonNull String, long, @NonNull java.io.File, int) throws java.io.IOException; method public void close(); method public int describeContents(); method public int getFlags(); - method public java.io.InputStream getInputStream() throws java.io.IOException; - method public String getTag(); - method public String getText(int); + method @Nullable public java.io.InputStream getInputStream() throws java.io.IOException; + method @NonNull public String getTag(); + method @Nullable public String getText(int); method public long getTimeMillis(); method public void writeToParcel(android.os.Parcel, int); field @NonNull public static final android.os.Parcelable.Creator<android.os.DropBoxManager.Entry> CREATOR; @@ -38767,6 +38768,7 @@ package android.telecom { field public static final int DIRECTION_UNKNOWN = -1; // 0xffffffff field public static final int PROPERTY_ASSISTED_DIALING = 512; // 0x200 field public static final int PROPERTY_CONFERENCE = 1; // 0x1 + field public static final int PROPERTY_CROSS_SIM = 16384; // 0x4000 field public static final int PROPERTY_EMERGENCY_CALLBACK_MODE = 4; // 0x4 field public static final int PROPERTY_ENTERPRISE_CALL = 32; // 0x20 field public static final int PROPERTY_GENERIC_CONFERENCE = 2; // 0x2 @@ -39060,6 +39062,7 @@ package android.telecom { field public static final String EXTRA_LAST_FORWARDED_NUMBER = "android.telecom.extra.LAST_FORWARDED_NUMBER"; field public static final String EXTRA_SIP_INVITE = "android.telecom.extra.SIP_INVITE"; field public static final int PROPERTY_ASSISTED_DIALING = 512; // 0x200 + field public static final int PROPERTY_CROSS_SIM = 8192; // 0x2000 field public static final int PROPERTY_HAS_CDMA_VOICE_PRIVACY = 32; // 0x20 field public static final int PROPERTY_HIGH_DEF_AUDIO = 4; // 0x4 field public static final int PROPERTY_IS_ADHOC_CONFERENCE = 4096; // 0x1000 @@ -41817,6 +41820,8 @@ package android.telephony { method @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public void switchMultiSimConfig(int); method public void unregisterPhoneStateListener(@NonNull android.telephony.PhoneStateListener); method public void updateAvailableNetworks(@NonNull java.util.List<android.telephony.AvailableNetworkInfo>, @Nullable java.util.concurrent.Executor, @Nullable java.util.function.Consumer<java.lang.Integer>); + method public void uploadCallComposerPicture(@NonNull java.nio.file.Path, @NonNull java.util.concurrent.Executor, @NonNull android.os.OutcomeReceiver<android.os.ParcelUuid,android.telephony.TelephonyManager.CallComposerException>); + method public void uploadCallComposerPicture(@NonNull java.io.InputStream, @NonNull java.util.concurrent.Executor, @NonNull android.os.OutcomeReceiver<android.os.ParcelUuid,android.telephony.TelephonyManager.CallComposerException>); field public static final String ACTION_CARRIER_MESSAGING_CLIENT_SERVICE = "android.telephony.action.CARRIER_MESSAGING_CLIENT_SERVICE"; field public static final String ACTION_CARRIER_SIGNAL_DEFAULT_NETWORK_AVAILABLE = "android.telephony.action.CARRIER_SIGNAL_DEFAULT_NETWORK_AVAILABLE"; field public static final String ACTION_CARRIER_SIGNAL_PCO_VALUE = "android.telephony.action.CARRIER_SIGNAL_PCO_VALUE"; @@ -41959,6 +41964,18 @@ package android.telephony { field public static final String VVM_TYPE_OMTP = "vvm_type_omtp"; } + public static class TelephonyManager.CallComposerException extends java.lang.Exception { + ctor public TelephonyManager.CallComposerException(int, @Nullable java.io.IOException); + method public int getErrorCode(); + method @Nullable public java.io.IOException getIOException(); + field public static final int ERROR_AUTHENTICATION_FAILED = 3; // 0x3 + field public static final int ERROR_FILE_TOO_LARGE = 2; // 0x2 + field public static final int ERROR_INPUT_CLOSED = 4; // 0x4 + field public static final int ERROR_IO_EXCEPTION = 5; // 0x5 + field public static final int ERROR_REMOTE_END_CLOSED = 1; // 0x1 + field public static final int ERROR_UNKNOWN = 0; // 0x0 + } + public abstract static class TelephonyManager.CellInfoCallback { ctor public TelephonyManager.CellInfoCallback(); method public abstract void onCellInfo(@NonNull java.util.List<android.telephony.CellInfo>); @@ -42363,6 +42380,7 @@ package android.telephony.ims { method @RequiresPermission(anyOf={"android.permission.READ_PRIVILEGED_PHONE_STATE", android.Manifest.permission.READ_PRECISE_PHONE_STATE}) public void getRegistrationTransportType(@NonNull java.util.concurrent.Executor, @NonNull java.util.function.Consumer<java.lang.Integer>); method @RequiresPermission(anyOf={"android.permission.READ_PRIVILEGED_PHONE_STATE", android.Manifest.permission.READ_PRECISE_PHONE_STATE}) public int getVoWiFiModeSetting(); method @RequiresPermission(anyOf={"android.permission.READ_PRIVILEGED_PHONE_STATE", android.Manifest.permission.READ_PRECISE_PHONE_STATE}) public boolean isAdvancedCallingSettingEnabled(); + method @RequiresPermission(anyOf={"android.permission.READ_PRIVILEGED_PHONE_STATE", android.Manifest.permission.READ_PRECISE_PHONE_STATE}) public boolean isCrossSimCallingEnabledByUser() throws android.telephony.ims.ImsException; method @RequiresPermission(anyOf={"android.permission.READ_PRIVILEGED_PHONE_STATE", android.Manifest.permission.READ_PRECISE_PHONE_STATE}) public boolean isTtyOverVolteEnabled(); method @RequiresPermission(anyOf={"android.permission.READ_PRIVILEGED_PHONE_STATE", android.Manifest.permission.READ_PRECISE_PHONE_STATE}) public boolean isVoWiFiRoamingSettingEnabled(); method @RequiresPermission(anyOf={"android.permission.READ_PRIVILEGED_PHONE_STATE", android.Manifest.permission.READ_PRECISE_PHONE_STATE}) public boolean isVoWiFiSettingEnabled(); @@ -42579,6 +42597,7 @@ package android.telephony.ims { method @RequiresPermission("android.permission.READ_PRIVILEGED_PHONE_STATE") public void getRegistrationTransportType(@NonNull java.util.concurrent.Executor, @NonNull java.util.function.Consumer<java.lang.Integer>); method @RequiresPermission("android.permission.READ_PRIVILEGED_PHONE_STATE") public void registerImsRegistrationCallback(@NonNull java.util.concurrent.Executor, @NonNull android.telephony.ims.RegistrationManager.RegistrationCallback) throws android.telephony.ims.ImsException; method @RequiresPermission("android.permission.READ_PRIVILEGED_PHONE_STATE") public void unregisterImsRegistrationCallback(@NonNull android.telephony.ims.RegistrationManager.RegistrationCallback); + field public static final int ATTR_EPDG_OVER_CELL_INTERNET = 1; // 0x1 field public static final int REGISTRATION_STATE_NOT_REGISTERED = 0; // 0x0 field public static final int REGISTRATION_STATE_REGISTERED = 2; // 0x2 field public static final int REGISTRATION_STATE_REGISTERING = 1; // 0x1 @@ -42586,8 +42605,10 @@ package android.telephony.ims { public static class RegistrationManager.RegistrationCallback { ctor public RegistrationManager.RegistrationCallback(); - method public void onRegistered(int); - method public void onRegistering(int); + method @Deprecated public void onRegistered(int); + method public void onRegistered(int, int); + method @Deprecated public void onRegistering(int); + method public void onRegistering(int, int); method public void onTechnologyChangeFailed(int, @NonNull android.telephony.ims.ImsReasonInfo); method public void onUnregistered(@NonNull android.telephony.ims.ImsReasonInfo); } @@ -45461,6 +45482,7 @@ package android.util { method public void remove(int); method public void removeAt(int); method public void removeAtRange(int, int); + method public void set(int, E); method public void setValueAt(int, E); method public int size(); method public E valueAt(int); @@ -51611,6 +51633,66 @@ package android.view.textservice { } +package android.view.translation { + + public final class TranslationManager { + method @Nullable @WorkerThread public android.view.translation.Translator createTranslator(@NonNull android.view.translation.TranslationSpec, @NonNull android.view.translation.TranslationSpec); + method @NonNull @WorkerThread public java.util.List<java.lang.String> getSupportedLocales(); + } + + public final class TranslationRequest implements android.os.Parcelable { + ctor public TranslationRequest(@Nullable CharSequence); + method public int describeContents(); + method @Nullable public android.view.autofill.AutofillId getAutofillId(); + method @Nullable public CharSequence getTranslationText(); + method public void writeToParcel(@NonNull android.os.Parcel, int); + field @NonNull public static final android.os.Parcelable.Creator<android.view.translation.TranslationRequest> CREATOR; + } + + public static final class TranslationRequest.Builder { + ctor public TranslationRequest.Builder(); + method @NonNull public android.view.translation.TranslationRequest build(); + method @NonNull public android.view.translation.TranslationRequest.Builder setAutofillId(@NonNull android.view.autofill.AutofillId); + method @NonNull public android.view.translation.TranslationRequest.Builder setTranslationText(@NonNull CharSequence); + } + + public final class TranslationResponse implements android.os.Parcelable { + method public int describeContents(); + method public int getTranslationStatus(); + method @NonNull public java.util.List<android.view.translation.TranslationRequest> getTranslations(); + method public void writeToParcel(@NonNull android.os.Parcel, int); + field @NonNull public static final android.os.Parcelable.Creator<android.view.translation.TranslationResponse> CREATOR; + field public static final int TRANSLATION_STATUS_LANGUAGE_UNAVAILABLE = 2; // 0x2 + field public static final int TRANSLATION_STATUS_SUCCESS = 0; // 0x0 + field public static final int TRANSLATION_STATUS_UNKNOWN_ERROR = 1; // 0x1 + } + + public static final class TranslationResponse.Builder { + ctor public TranslationResponse.Builder(int); + method @NonNull public android.view.translation.TranslationResponse.Builder addTranslations(@NonNull android.view.translation.TranslationRequest); + method @NonNull public android.view.translation.TranslationResponse build(); + method @NonNull public android.view.translation.TranslationResponse.Builder setTranslationStatus(int); + method @NonNull public android.view.translation.TranslationResponse.Builder setTranslations(@NonNull java.util.List<android.view.translation.TranslationRequest>); + } + + public final class TranslationSpec implements android.os.Parcelable { + ctor public TranslationSpec(@NonNull String, int); + method public int describeContents(); + method public int getDataFormat(); + method @NonNull public String getLanguage(); + method public void writeToParcel(@NonNull android.os.Parcel, int); + field @NonNull public static final android.os.Parcelable.Creator<android.view.translation.TranslationSpec> CREATOR; + field public static final int DATA_FORMAT_TEXT = 1; // 0x1 + } + + public class Translator { + method public void destroy(); + method public boolean isDestroyed(); + method @Nullable @WorkerThread public android.view.translation.TranslationResponse translate(@NonNull android.view.translation.TranslationRequest); + } + +} + package android.webkit { public abstract class ClientCertRequest { diff --git a/core/api/module-lib-current.txt b/core/api/module-lib-current.txt index ef6c8b713c2c..bf92b34a5657 100644 --- a/core/api/module-lib-current.txt +++ b/core/api/module-lib-current.txt @@ -108,7 +108,7 @@ package android.media.session { } public final class MediaSessionManager { - method public void addOnActiveSessionsChangedListener(@NonNull android.media.session.MediaSessionManager.OnActiveSessionsChangedListener, @Nullable android.content.ComponentName, int, @Nullable android.os.Handler); + method public void addOnActiveSessionsChangedListener(@NonNull android.media.session.MediaSessionManager.OnActiveSessionsChangedListener, @Nullable android.content.ComponentName, @NonNull android.os.UserHandle, @Nullable android.os.Handler); method public void dispatchMediaKeyEvent(@NonNull android.view.KeyEvent); method public void dispatchMediaKeyEvent(@NonNull android.view.KeyEvent, boolean); method public void dispatchMediaKeyEventAsSystemService(@NonNull android.view.KeyEvent); @@ -116,7 +116,7 @@ package android.media.session { method public void dispatchVolumeKeyEvent(@NonNull android.view.KeyEvent, int, boolean); method public void dispatchVolumeKeyEventAsSystemService(@NonNull android.view.KeyEvent, int); method public void dispatchVolumeKeyEventToSessionAsSystemService(@NonNull android.view.KeyEvent, @NonNull android.media.session.MediaSession.Token); - method @NonNull public java.util.List<android.media.session.MediaController> getActiveSessionsForUser(@Nullable android.content.ComponentName, int); + method @NonNull public java.util.List<android.media.session.MediaController> getActiveSessionsForUser(@Nullable android.content.ComponentName, @NonNull android.os.UserHandle); method public void registerRemoteSessionCallback(@NonNull java.util.concurrent.Executor, @NonNull android.media.session.MediaSessionManager.RemoteSessionCallback); method public void unregisterRemoteSessionCallback(@NonNull android.media.session.MediaSessionManager.RemoteSessionCallback); field public static final int RESULT_MEDIA_KEY_HANDLED = 1; // 0x1 diff --git a/core/api/system-current.txt b/core/api/system-current.txt index 473a2808cd00..9451d69c59f8 100644 --- a/core/api/system-current.txt +++ b/core/api/system-current.txt @@ -52,6 +52,7 @@ package android { field public static final String BIND_TELEPHONY_NETWORK_SERVICE = "android.permission.BIND_TELEPHONY_NETWORK_SERVICE"; field public static final String BIND_TEXTCLASSIFIER_SERVICE = "android.permission.BIND_TEXTCLASSIFIER_SERVICE"; field public static final String BIND_TIME_ZONE_PROVIDER_SERVICE = "android.permission.BIND_TIME_ZONE_PROVIDER_SERVICE"; + field public static final String BIND_TRANSLATION_SERVICE = "android.permission.BIND_TRANSLATION_SERVICE"; field public static final String BIND_TRUST_AGENT = "android.permission.BIND_TRUST_AGENT"; field public static final String BIND_TV_REMOTE_SERVICE = "android.permission.BIND_TV_REMOTE_SERVICE"; field public static final String BRICK = "android.permission.BRICK"; @@ -1941,6 +1942,7 @@ package android.content { field public static final String SYSTEM_CONFIG_SERVICE = "system_config"; field public static final String SYSTEM_UPDATE_SERVICE = "system_update"; field public static final String TETHERING_SERVICE = "tethering"; + field public static final String TRANSLATION_MANAGER_SERVICE = "transformer"; field public static final String VR_SERVICE = "vrmanager"; field public static final String WIFI_NL80211_SERVICE = "wifinl80211"; field @Deprecated public static final String WIFI_RTT_SERVICE = "rttmanager"; @@ -6639,6 +6641,7 @@ package android.net { method public long getExpiryTimeMillis(); method public long getRefreshTimeMillis(); method @Nullable public android.net.Uri getUserPortalUrl(); + method @Nullable public String getVenueFriendlyName(); method @Nullable public android.net.Uri getVenueInfoUrl(); method public boolean isCaptive(); method public boolean isSessionExtendable(); @@ -6656,6 +6659,7 @@ package android.net { method @NonNull public android.net.CaptivePortalData.Builder setRefreshTime(long); method @NonNull public android.net.CaptivePortalData.Builder setSessionExtendable(boolean); method @NonNull public android.net.CaptivePortalData.Builder setUserPortalUrl(@Nullable android.net.Uri); + method @NonNull public android.net.CaptivePortalData.Builder setVenueFriendlyName(@Nullable String); method @NonNull public android.net.CaptivePortalData.Builder setVenueInfoUrl(@Nullable android.net.Uri); } @@ -6907,13 +6911,16 @@ package android.net { } public final class NetworkCapabilities implements android.os.Parcelable { + ctor public NetworkCapabilities(@Nullable android.net.NetworkCapabilities, boolean); method @NonNull public int[] getAdministratorUids(); method @Nullable public String getSsid(); method @NonNull public int[] getTransportTypes(); method public boolean satisfiedByNetworkCapabilities(@Nullable android.net.NetworkCapabilities); + field public static final int NET_CAPABILITY_NOT_VCN_MANAGED = 28; // 0x1c field public static final int NET_CAPABILITY_OEM_PAID = 22; // 0x16 field public static final int NET_CAPABILITY_OEM_PRIVATE = 26; // 0x1a field public static final int NET_CAPABILITY_PARTIAL_CONNECTIVITY = 24; // 0x18 + field public static final int NET_CAPABILITY_VEHICLE_INTERNAL = 27; // 0x1b } public static final class NetworkCapabilities.Builder { @@ -7128,6 +7135,11 @@ package android.net { field public static final int TAG_SYSTEM_IMPERSONATION_RANGE_START = -256; // 0xffffff00 } + public interface TransportInfo { + method public default boolean hasLocationSensitiveFields(); + method @NonNull public default android.net.TransportInfo makeCopy(boolean); + } + public abstract class Uri implements java.lang.Comparable<android.net.Uri> android.os.Parcelable { method @NonNull public String toSafeString(); } @@ -8555,10 +8567,11 @@ package android.printservice.recommendation { package android.provider { public class CallLog { - method @RequiresPermission(android.Manifest.permission.WRITE_CALL_LOG) public static void storeCallComposerPictureAsUser(@NonNull android.content.Context, @Nullable android.os.UserHandle, @NonNull java.io.InputStream, @NonNull java.util.concurrent.Executor, @NonNull android.os.OutcomeReceiver<android.net.Uri,android.provider.CallLog.CallComposerLoggingException>); + method @RequiresPermission(allOf={android.Manifest.permission.WRITE_CALL_LOG, android.Manifest.permission.INTERACT_ACROSS_USERS}) public static void storeCallComposerPictureAsUser(@NonNull android.content.Context, @Nullable android.os.UserHandle, @NonNull java.io.InputStream, @NonNull java.util.concurrent.Executor, @NonNull android.os.OutcomeReceiver<android.net.Uri,android.provider.CallLog.CallComposerLoggingException>); } public static class CallLog.CallComposerLoggingException extends java.lang.Throwable { + ctor public CallLog.CallComposerLoggingException(int); method public int getErrorCode(); field public static final int ERROR_INPUT_CLOSED = 3; // 0x3 field public static final int ERROR_REMOTE_END_CLOSED = 1; // 0x1 @@ -9854,6 +9867,48 @@ package android.service.timezone { } +package android.service.translation { + + public final class TranslationRequest implements android.os.Parcelable { + ctor public TranslationRequest(int, @NonNull android.view.translation.TranslationSpec, @NonNull android.view.translation.TranslationSpec, @NonNull java.util.List<android.view.translation.TranslationRequest>); + method public int describeContents(); + method @NonNull public android.view.translation.TranslationSpec getDestSpec(); + method public int getRequestId(); + method @NonNull public android.view.translation.TranslationSpec getSourceSpec(); + method @NonNull public java.util.List<android.view.translation.TranslationRequest> getTranslationRequests(); + method public void writeToParcel(@NonNull android.os.Parcel, int); + field @NonNull public static final android.os.Parcelable.Creator<android.service.translation.TranslationRequest> CREATOR; + } + + public static final class TranslationRequest.Builder { + ctor public TranslationRequest.Builder(int, @NonNull android.view.translation.TranslationSpec, @NonNull android.view.translation.TranslationSpec, @NonNull java.util.List<android.view.translation.TranslationRequest>); + method @NonNull public android.service.translation.TranslationRequest.Builder addTranslationRequests(@NonNull android.view.translation.TranslationRequest); + method @NonNull public android.service.translation.TranslationRequest build(); + method @NonNull public android.service.translation.TranslationRequest.Builder setDestSpec(@NonNull android.view.translation.TranslationSpec); + method @NonNull public android.service.translation.TranslationRequest.Builder setRequestId(int); + method @NonNull public android.service.translation.TranslationRequest.Builder setSourceSpec(@NonNull android.view.translation.TranslationSpec); + method @NonNull public android.service.translation.TranslationRequest.Builder setTranslationRequests(@NonNull java.util.List<android.view.translation.TranslationRequest>); + } + + public abstract class TranslationService extends android.app.Service { + ctor public TranslationService(); + method @Nullable public final android.os.IBinder onBind(@NonNull android.content.Intent); + method public void onConnected(); + method public abstract void onCreateTranslationSession(@NonNull android.view.translation.TranslationSpec, @NonNull android.view.translation.TranslationSpec, int); + method public void onDisconnected(); + method public abstract void onFinishTranslationSession(int); + method public abstract void onTranslationRequest(@NonNull android.service.translation.TranslationRequest, int, @NonNull android.os.CancellationSignal, @NonNull android.service.translation.TranslationService.OnTranslationResultCallback); + field public static final String SERVICE_INTERFACE = "android.service.translation.TranslationService"; + field public static final String SERVICE_META_DATA = "android.translation_service"; + } + + public static interface TranslationService.OnTranslationResultCallback { + method public void onError(); + method public void onTranslationSuccess(@NonNull android.view.translation.TranslationResponse); + } + +} + package android.service.trust { public class TrustAgentService extends android.app.Service { @@ -11089,6 +11144,7 @@ package android.telephony { method @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public void setUiccApplicationsEnabled(int, boolean); field @RequiresPermission(android.Manifest.permission.MANAGE_SUBSCRIPTION_PLANS) public static final String ACTION_SUBSCRIPTION_PLANS_CHANGED = "android.telephony.action.SUBSCRIPTION_PLANS_CHANGED"; field @NonNull public static final android.net.Uri ADVANCED_CALLING_ENABLED_CONTENT_URI; + field @NonNull public static final android.net.Uri CROSS_SIM_ENABLED_CONTENT_URI; field @Deprecated public static final int PROFILE_CLASS_DEFAULT = -1; // 0xffffffff field public static final int PROFILE_CLASS_OPERATIONAL = 2; // 0x2 field public static final int PROFILE_CLASS_PROVISIONING = 1; // 0x1 @@ -11208,6 +11264,7 @@ package android.telephony { method @Deprecated @RequiresPermission(anyOf={android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE, android.Manifest.permission.READ_PHONE_STATE}) public boolean isOffhook(); method @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) public boolean isOpportunisticNetworkEnabled(); method @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) public boolean isPotentialEmergencyNumber(@NonNull String); + method public boolean isRadioInterfaceCapabilitySupported(@NonNull String); method @Deprecated @RequiresPermission(anyOf={android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE, android.Manifest.permission.READ_PHONE_STATE}) public boolean isRadioOn(); method @Deprecated @RequiresPermission(anyOf={android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE, android.Manifest.permission.READ_PHONE_STATE}) public boolean isRinging(); method @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public boolean isTetheringApnRequired(); @@ -11282,6 +11339,7 @@ package android.telephony { field public static final int CALL_WAITING_STATUS_ENABLED = 1; // 0x1 field public static final int CALL_WAITING_STATUS_NOT_SUPPORTED = 4; // 0x4 field public static final int CALL_WAITING_STATUS_UNKNOWN_ERROR = 3; // 0x3 + field public static final String CAPABILITY_SECONDARY_LINK_BANDWIDTH_VISIBLE = "CAPABILITY_SECONDARY_LINK_BANDWIDTH_VISIBLE"; field public static final int CARRIER_PRIVILEGE_STATUS_ERROR_LOADING_RULES = -2; // 0xfffffffe field public static final int CARRIER_PRIVILEGE_STATUS_HAS_ACCESS = 1; // 0x1 field public static final int CARRIER_PRIVILEGE_STATUS_NO_ACCESS = 0; // 0x0 @@ -12149,6 +12207,7 @@ package android.telephony.ims { field public static final String EXTRA_EXTENDING_TO_CONFERENCE_SUPPORTED = "android.telephony.ims.extra.EXTENDING_TO_CONFERENCE_SUPPORTED"; field public static final String EXTRA_FORWARDED_NUMBER = "android.telephony.ims.extra.FORWARDED_NUMBER"; field public static final String EXTRA_IS_CALL_PULL = "CallPull"; + field public static final String EXTRA_IS_CROSS_SIM_CALL = "android.telephony.ims.extra.IS_CROSS_SIM_CALL"; field public static final String EXTRA_LOCATION = "android.telephony.ims.extra.LOCATION"; field public static final String EXTRA_OI = "oi"; field public static final String EXTRA_OIR = "oir"; @@ -12278,6 +12337,7 @@ package android.telephony.ims { method @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) public void isSupported(int, int, @NonNull java.util.concurrent.Executor, @NonNull java.util.function.Consumer<java.lang.Boolean>) throws android.telephony.ims.ImsException; method @Deprecated @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) public void registerImsRegistrationCallback(@NonNull java.util.concurrent.Executor, @NonNull android.telephony.ims.ImsMmTelManager.RegistrationCallback) throws android.telephony.ims.ImsException; method @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public void setAdvancedCallingSettingEnabled(boolean); + method @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public void setCrossSimCallingEnabled(boolean) throws android.telephony.ims.ImsException; method @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public void setRttCapabilitySetting(boolean); method @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public void setVoWiFiModeSetting(int); method @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public void setVoWiFiNonPersistent(boolean, int); @@ -12290,6 +12350,8 @@ package android.telephony.ims { @Deprecated public static class ImsMmTelManager.RegistrationCallback extends android.telephony.ims.RegistrationManager.RegistrationCallback { ctor @Deprecated public ImsMmTelManager.RegistrationCallback(); + method @Deprecated public void onRegistered(int); + method @Deprecated public void onRegistering(int); } public final class ImsReasonInfo implements android.os.Parcelable { @@ -12586,7 +12648,32 @@ package android.telephony.ims { } public class RcsUceAdapter { + method @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) public void addOnPublishStateChangedListener(@NonNull java.util.concurrent.Executor, @NonNull android.telephony.ims.RcsUceAdapter.OnPublishStateChangedListener) throws android.telephony.ims.ImsException; + method @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) public int getUcePublishState() throws android.telephony.ims.ImsException; + method @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) public void removeOnPublishStateChangedListener(@NonNull android.telephony.ims.RcsUceAdapter.OnPublishStateChangedListener) throws android.telephony.ims.ImsException; method @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public void setUceSettingEnabled(boolean) throws android.telephony.ims.ImsException; + field public static final int CAPABILITY_UPDATE_TRIGGER_ETAG_EXPIRED = 1; // 0x1 + field public static final int CAPABILITY_UPDATE_TRIGGER_MOVE_TO_2G = 7; // 0x7 + field public static final int CAPABILITY_UPDATE_TRIGGER_MOVE_TO_3G = 6; // 0x6 + field public static final int CAPABILITY_UPDATE_TRIGGER_MOVE_TO_EHRPD = 4; // 0x4 + field public static final int CAPABILITY_UPDATE_TRIGGER_MOVE_TO_HSPAPLUS = 5; // 0x5 + field public static final int CAPABILITY_UPDATE_TRIGGER_MOVE_TO_IWLAN = 9; // 0x9 + field public static final int CAPABILITY_UPDATE_TRIGGER_MOVE_TO_LTE_VOPS_DISABLED = 2; // 0x2 + field public static final int CAPABILITY_UPDATE_TRIGGER_MOVE_TO_LTE_VOPS_ENABLED = 3; // 0x3 + field public static final int CAPABILITY_UPDATE_TRIGGER_MOVE_TO_NR5G_VOPS_DISABLED = 10; // 0xa + field public static final int CAPABILITY_UPDATE_TRIGGER_MOVE_TO_NR5G_VOPS_ENABLED = 11; // 0xb + field public static final int CAPABILITY_UPDATE_TRIGGER_MOVE_TO_WLAN = 8; // 0x8 + field public static final int CAPABILITY_UPDATE_TRIGGER_UNKNOWN = 0; // 0x0 + field public static final int PUBLISH_STATE_NOT_PUBLISHED = 2; // 0x2 + field public static final int PUBLISH_STATE_OK = 1; // 0x1 + field public static final int PUBLISH_STATE_OTHER_ERROR = 6; // 0x6 + field public static final int PUBLISH_STATE_RCS_PROVISION_ERROR = 4; // 0x4 + field public static final int PUBLISH_STATE_REQUEST_TIMEOUT = 5; // 0x5 + field public static final int PUBLISH_STATE_VOICE_PROVISION_ERROR = 3; // 0x3 + } + + public static interface RcsUceAdapter.OnPublishStateChangedListener { + method public void onPublishStateChange(int); } public final class RtpHeaderExtension implements android.os.Parcelable { @@ -12793,16 +12880,24 @@ package android.telephony.ims.feature { } public class RcsFeature extends android.telephony.ims.feature.ImsFeature { - ctor public RcsFeature(); + ctor @Deprecated public RcsFeature(); + ctor public RcsFeature(@NonNull java.util.concurrent.Executor); method public void changeEnabledCapabilities(@NonNull android.telephony.ims.feature.CapabilityChangeRequest, @NonNull android.telephony.ims.feature.ImsFeature.CapabilityCallbackProxy); + method @NonNull public android.telephony.ims.stub.RcsCapabilityExchangeImplBase createCapabilityExchangeImpl(@NonNull java.util.concurrent.Executor, @NonNull android.telephony.ims.stub.CapabilityExchangeEventListener); method public void onFeatureReady(); method public void onFeatureRemoved(); + method public void removeCapabilityExchangeImpl(@NonNull android.telephony.ims.stub.RcsCapabilityExchangeImplBase); } } package android.telephony.ims.stub { + public interface CapabilityExchangeEventListener { + method public void onRequestPublishCapabilities(int) throws android.telephony.ims.ImsException; + method public void onUnpublish() throws android.telephony.ims.ImsException; + } + public interface DelegateConnectionMessageCallback { method public void onMessageReceived(@NonNull android.telephony.ims.SipMessage); method public void onMessageSendFailure(@NonNull String, int); @@ -12929,6 +13024,7 @@ package android.telephony.ims.stub { method public void triggerFullNetworkRegistration(@IntRange(from=100, to=699) int, @Nullable String); method public void triggerSipDelegateDeregistration(); method public void updateSipDelegateRegistration(); + field public static final int REGISTRATION_TECH_CROSS_SIM = 2; // 0x2 field public static final int REGISTRATION_TECH_IWLAN = 1; // 0x1 field public static final int REGISTRATION_TECH_LTE = 0; // 0x0 field public static final int REGISTRATION_TECH_NONE = -1; // 0xffffffff @@ -12983,6 +13079,27 @@ package android.telephony.ims.stub { method public int updateColr(int); } + public class RcsCapabilityExchangeImplBase { + ctor public RcsCapabilityExchangeImplBase(@NonNull java.util.concurrent.Executor); + method public void publishCapabilities(@NonNull String, @NonNull android.telephony.ims.stub.RcsCapabilityExchangeImplBase.PublishResponseCallback); + field public static final int COMMAND_CODE_FETCH_ERROR = 3; // 0x3 + field public static final int COMMAND_CODE_GENERIC_FAILURE = 1; // 0x1 + field public static final int COMMAND_CODE_INSUFFICIENT_MEMORY = 5; // 0x5 + field public static final int COMMAND_CODE_INVALID_PARAM = 2; // 0x2 + field public static final int COMMAND_CODE_LOST_NETWORK_CONNECTION = 6; // 0x6 + field public static final int COMMAND_CODE_NOT_FOUND = 8; // 0x8 + field public static final int COMMAND_CODE_NOT_SUPPORTED = 7; // 0x7 + field public static final int COMMAND_CODE_NO_CHANGE = 10; // 0xa + field public static final int COMMAND_CODE_REQUEST_TIMEOUT = 4; // 0x4 + field public static final int COMMAND_CODE_SERVICE_UNAVAILABLE = 9; // 0x9 + field public static final int COMMAND_CODE_SERVICE_UNKNOWN = 0; // 0x0 + } + + public static interface RcsCapabilityExchangeImplBase.PublishResponseCallback { + method public void onCommandError(int) throws android.telephony.ims.ImsException; + method public void onNetworkResponse(@IntRange(from=100, to=699) int, @NonNull String) throws android.telephony.ims.ImsException; + } + public interface SipDelegate { method public void closeDialog(@NonNull String); method public void notifyMessageReceiveError(@NonNull String, int); diff --git a/core/api/test-current.txt b/core/api/test-current.txt index c03461aa7742..1422561e3347 100644 --- a/core/api/test-current.txt +++ b/core/api/test-current.txt @@ -2512,7 +2512,6 @@ package android.window { method @CallSuper @NonNull @RequiresPermission(android.Manifest.permission.MANAGE_ACTIVITY_TASKS) public java.util.List<android.window.TaskAppearedInfo> registerOrganizer(); method @BinderThread public void removeStartingWindow(int); method @RequiresPermission(android.Manifest.permission.MANAGE_ACTIVITY_TASKS) public void setInterceptBackPressedOnTaskRoot(@NonNull android.window.WindowContainerToken, boolean); - method @RequiresPermission(android.Manifest.permission.MANAGE_ACTIVITY_TASKS) public void setLaunchRoot(int, @NonNull android.window.WindowContainerToken); method @CallSuper @RequiresPermission(android.Manifest.permission.MANAGE_ACTIVITY_TASKS) public void unregisterOrganizer(); } @@ -2527,6 +2526,7 @@ package android.window { method public int describeContents(); method @NonNull public android.window.WindowContainerTransaction reorder(@NonNull android.window.WindowContainerToken, boolean); method @NonNull public android.window.WindowContainerTransaction reparent(@NonNull android.window.WindowContainerToken, @Nullable android.window.WindowContainerToken, boolean); + method @NonNull public android.window.WindowContainerTransaction reparentTasks(@Nullable android.window.WindowContainerToken, @Nullable android.window.WindowContainerToken, @Nullable int[], @Nullable int[], boolean); method @NonNull public android.window.WindowContainerTransaction scheduleFinishEnterPip(@NonNull android.window.WindowContainerToken, @NonNull android.graphics.Rect); method @NonNull public android.window.WindowContainerTransaction setActivityWindowingMode(@NonNull android.window.WindowContainerToken, int); method @NonNull public android.window.WindowContainerTransaction setAppBounds(@NonNull android.window.WindowContainerToken, @NonNull android.graphics.Rect); @@ -2534,6 +2534,7 @@ package android.window { method @NonNull public android.window.WindowContainerTransaction setBoundsChangeTransaction(@NonNull android.window.WindowContainerToken, @NonNull android.view.SurfaceControl.Transaction); method @NonNull public android.window.WindowContainerTransaction setFocusable(@NonNull android.window.WindowContainerToken, boolean); method @NonNull public android.window.WindowContainerTransaction setHidden(@NonNull android.window.WindowContainerToken, boolean); + method @NonNull public android.window.WindowContainerTransaction setLaunchRoot(@NonNull android.window.WindowContainerToken, @Nullable int[], @Nullable int[]); method @NonNull public android.window.WindowContainerTransaction setScreenSizeDp(@NonNull android.window.WindowContainerToken, int, int); method @NonNull public android.window.WindowContainerTransaction setSmallestScreenWidthDp(@NonNull android.window.WindowContainerToken, int); method @NonNull public android.window.WindowContainerTransaction setWindowingMode(@NonNull android.window.WindowContainerToken, int); diff --git a/core/java/android/app/ActivityManagerInternal.java b/core/java/android/app/ActivityManagerInternal.java index 6e7bb83b2fcb..b1890b075c59 100644 --- a/core/java/android/app/ActivityManagerInternal.java +++ b/core/java/android/app/ActivityManagerInternal.java @@ -32,10 +32,12 @@ import android.os.Bundle; import android.os.IBinder; import android.os.TransactionTooLargeException; import android.os.WorkSource; +import android.util.ArraySet; import java.util.ArrayList; import java.util.List; import java.util.Map; +import java.util.Set; /** * Activity manager local system service interface. @@ -446,6 +448,32 @@ public abstract class ActivityManagerInternal { */ public abstract void setDeviceOwnerUid(int uid); + /** Is this a profile owner app? */ + public abstract boolean isProfileOwner(int uid); + + /** + * Called by DevicePolicyManagerService to set the uid of the profile owner. + * @param profileOwnerUids The profile owner UIDs. The ownership of the array is + * passed to callee. + */ + public abstract void setProfileOwnerUid(ArraySet<Integer> profileOwnerUids); + + /** + * Set all associated companion app that belongs to a userId. + * @param userId + * @param companionAppUids ActivityManager will take ownership of this Set, the caller + * shouldn't touch this Set after calling this interface. + */ + public abstract void setCompanionAppUids(int userId, Set<Integer> companionAppUids); + + /** + * is the uid an associated companion app of a userId? + * @param userId + * @param uid + * @return + */ + public abstract boolean isAssociatedCompanionApp(int userId, int uid); + /** * Sends a broadcast, assuming the caller to be the system and allowing the inclusion of an * approved whitelist of app Ids >= {@link android.os.Process#FIRST_APPLICATION_UID} that the diff --git a/core/java/android/app/ActivityThread.java b/core/java/android/app/ActivityThread.java index 2fe1711bdbd4..bd437f4ce192 100644 --- a/core/java/android/app/ActivityThread.java +++ b/core/java/android/app/ActivityThread.java @@ -2328,7 +2328,7 @@ public final class ActivityThread extends ClientTransactionHandler { return null; } - @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553) + @UnsupportedAppUsage(trackingBug = 171933273) public final LoadedApk getPackageInfo(ApplicationInfo ai, CompatibilityInfo compatInfo, int flags) { boolean includeCode = (flags&Context.CONTEXT_INCLUDE_CODE) != 0; diff --git a/core/java/android/app/EventLogTags.logtags b/core/java/android/app/EventLogTags.logtags index 6296a689b6dd..d0856f44d9a9 100644 --- a/core/java/android/app/EventLogTags.logtags +++ b/core/java/android/app/EventLogTags.logtags @@ -10,8 +10,6 @@ option java_package android.app # The activity's onResume has been called. 30022 wm_on_resume_called (Token|1|5),(Component Name|3),(Reason|3) -# Attempting to stop an activity -30048 wm_stop_activity (User|1|5),(Token|1|5),(Component Name|3) # The activity's onStop has been called. 30049 wm_on_stop_called (Token|1|5),(Component Name|3),(Reason|3) @@ -31,6 +29,3 @@ option java_package android.app # The activity's onTopResumedActivityChanged(false) has been called. 30065 wm_on_top_resumed_lost_called (Token|1|5),(Component Name|3),(Reason|3) -# An activity been add into stopping list -30066 wm_add_to_stopping (User|1|5),(Token|1|5),(Component Name|3),(Reason|3) - diff --git a/core/java/android/app/OWNERS b/core/java/android/app/OWNERS index c804e6427dce..6d79e2d3c166 100644 --- a/core/java/android/app/OWNERS +++ b/core/java/android/app/OWNERS @@ -39,6 +39,9 @@ per-file *Alarm* = file:/apex/jobscheduler/OWNERS # AppOps per-file *AppOp* = file:/core/java/android/permission/OWNERS +# Multiuser +per-file *User* = file:/MULTIUSER_OWNERS + # Notification per-file *Notification* = file:/packages/SystemUI/OWNERS diff --git a/core/java/android/app/SystemServiceRegistry.java b/core/java/android/app/SystemServiceRegistry.java index 050d194c4094..dd016a2cf78e 100644 --- a/core/java/android/app/SystemServiceRegistry.java +++ b/core/java/android/app/SystemServiceRegistry.java @@ -209,6 +209,8 @@ import android.view.contentcapture.IContentCaptureManager; import android.view.inputmethod.InputMethodManager; import android.view.textclassifier.TextClassificationManager; import android.view.textservice.TextServicesManager; +import android.view.translation.ITranslationManager; +import android.view.translation.TranslationManager; import com.android.internal.app.IAppOpsService; import com.android.internal.app.IBatteryStats; @@ -1172,6 +1174,20 @@ public final class SystemServiceRegistry { return null; }}); + registerService(Context.TRANSLATION_MANAGER_SERVICE, TranslationManager.class, + new CachedServiceFetcher<TranslationManager>() { + @Override + public TranslationManager createService(ContextImpl ctx) + throws ServiceNotFoundException { + IBinder b = ServiceManager.getService(Context.TRANSLATION_MANAGER_SERVICE); + ITranslationManager service = ITranslationManager.Stub.asInterface(b); + // Service is null when not provided by OEM. + if (service != null) { + return new TranslationManager(ctx.getOuterContext(), service); + } + return null; + }}); + registerService(Context.SEARCH_UI_SERVICE, SearchUiManager.class, new CachedServiceFetcher<SearchUiManager>() { @Override diff --git a/core/java/android/app/admin/DevicePolicyManager.java b/core/java/android/app/admin/DevicePolicyManager.java index 97879b80ea06..7bc967fe251d 100644 --- a/core/java/android/app/admin/DevicePolicyManager.java +++ b/core/java/android/app/admin/DevicePolicyManager.java @@ -2729,6 +2729,17 @@ public class DevicePolicyManager { return DebugUtils.constantToString(DevicePolicyManager.class, PREFIX_OPERATION, operation); } + /** @hide */ + public void resetNewUserDisclaimer() { + if (mService != null) { + try { + mService.resetNewUserDisclaimer(); + } catch (RemoteException e) { + throw e.rethrowFromSystemServer(); + } + } + } + /** * Return true if the given administrator component is currently active (enabled) in the system. * @@ -3020,6 +3031,10 @@ public class DevicePolicyManager { * * <p><strong>Note:</strong> Specifying password requirements using this method clears the * password complexity requirements set using {@link #setRequiredPasswordComplexity(int)}. + * If this method is called on the {@link DevicePolicyManager} instance returned by + * {@link #getParentProfileInstance(ComponentName)}, then password complexity requirements + * set on the primary {@link DevicePolicyManager} must be cleared first by calling + * {@link #setRequiredPasswordComplexity} with {@link #PASSWORD_COMPLEXITY_NONE) first. * * @deprecated Prefer using {@link #setRequiredPasswordComplexity(int)}, to require a password * that satisfies a complexity level defined by the platform, rather than specifying custom @@ -3039,6 +3054,9 @@ public class DevicePolicyManager { * calling app is targeting {@link android.os.Build.VERSION_CODES#S} and above, * and is calling the method the {@link DevicePolicyManager} instance returned by * {@link #getParentProfileInstance(ComponentName)}. + * @throws IllegalStateException if the caller is trying to set password quality on the parent + * {@link DevicePolicyManager} instance while password complexity was set on the + * primary {@link DevicePolicyManager} instance. */ @Deprecated public void setPasswordQuality(@NonNull ComponentName admin, int quality) { @@ -4055,10 +4073,18 @@ public class DevicePolicyManager { * <p><strong>Note:</strong> Specifying password requirements using this method clears any * password requirements set using the obsolete {@link #setPasswordQuality(ComponentName, int)} * and any of its associated methods. + * Additionally, if there are password requirements set using the obsolete + * {@link #setPasswordQuality(ComponentName, int)} on the parent {@code DevicePolicyManager} + * instance, they must be cleared by calling {@link #setPasswordQuality(ComponentName, int)} + * with {@link #PASSWORD_QUALITY_UNSPECIFIED} on that instance prior to setting complexity + * requirement for the managed profile. * * @throws SecurityException if the calling application is not a device owner or a profile * owner. * @throws IllegalArgumentException if the complexity level is not one of the four above. + * @throws IllegalStateException if the caller is trying to set password complexity while there + * are password requirements specified using {@link #setPasswordQuality(ComponentName, int)} + * on the parent {@code DevicePolicyManager} instance. */ public void setRequiredPasswordComplexity(@PasswordComplexity int passwordComplexity) { if (mService == null) { @@ -5191,6 +5217,16 @@ public class DevicePolicyManager { "android.app.action.MANAGED_USER_CREATED"; /** + * Broadcast action: notify system that a new (Android) user was added when the device is + * managed by a device owner, so receivers can show the proper disclaimer to the (human) user. + * + * @hide + */ + @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION) + public static final String ACTION_SHOW_NEW_USER_DISCLAIMER = + "android.app.action.ACTION_SHOW_NEW_USER_DISCLAIMER"; + + /** * Widgets are enabled in keyguard */ public static final int KEYGUARD_DISABLE_FEATURES_NONE = 0; diff --git a/core/java/android/app/admin/IDevicePolicyManager.aidl b/core/java/android/app/admin/IDevicePolicyManager.aidl index e81abfe5a409..aaa5f7ce4cb0 100644 --- a/core/java/android/app/admin/IDevicePolicyManager.aidl +++ b/core/java/android/app/admin/IDevicePolicyManager.aidl @@ -252,6 +252,7 @@ interface IDevicePolicyManager { int stopUser(in ComponentName who, in UserHandle userHandle); int logoutUser(in ComponentName who); List<UserHandle> getSecondaryUsers(in ComponentName who); + void resetNewUserDisclaimer(); void enableSystemApp(in ComponentName admin, in String callerPackage, in String packageName); int enableSystemAppWithIntent(in ComponentName admin, in String callerPackage, in Intent intent); diff --git a/core/java/android/bluetooth/BluetoothA2dp.java b/core/java/android/bluetooth/BluetoothA2dp.java index c0cb32346821..15daf1c59d1a 100644 --- a/core/java/android/bluetooth/BluetoothA2dp.java +++ b/core/java/android/bluetooth/BluetoothA2dp.java @@ -118,7 +118,7 @@ public final class BluetoothA2dp implements BluetoothProfile { * @hide */ @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION) - @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553) + @UnsupportedAppUsage(trackingBug = 171933273) public static final String ACTION_ACTIVE_DEVICE_CHANGED = "android.bluetooth.a2dp.profile.action.ACTIVE_DEVICE_CHANGED"; @@ -409,7 +409,7 @@ public final class BluetoothA2dp implements BluetoothProfile { * @hide */ @RequiresPermission(Manifest.permission.BLUETOOTH_ADMIN) - @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553) + @UnsupportedAppUsage(trackingBug = 171933273) public boolean setActiveDevice(@Nullable BluetoothDevice device) { if (DBG) log("setActiveDevice(" + device + ")"); try { @@ -433,7 +433,7 @@ public final class BluetoothA2dp implements BluetoothProfile { * is active * @hide */ - @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553) + @UnsupportedAppUsage(trackingBug = 171933273) @Nullable @RequiresPermission(Manifest.permission.BLUETOOTH) public BluetoothDevice getActiveDevice() { diff --git a/core/java/android/bluetooth/BluetoothAdapter.java b/core/java/android/bluetooth/BluetoothAdapter.java index 406fe8d5023c..7eda50e5c9cb 100644 --- a/core/java/android/bluetooth/BluetoothAdapter.java +++ b/core/java/android/bluetooth/BluetoothAdapter.java @@ -1174,7 +1174,7 @@ public final class BluetoothAdapter { * @return true to indicate adapter shutdown has begun, or false on immediate error * @hide */ - @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553) + @UnsupportedAppUsage(trackingBug = 171933273) public boolean disable(boolean persist) { try { diff --git a/core/java/android/bluetooth/BluetoothHeadset.java b/core/java/android/bluetooth/BluetoothHeadset.java index 41610963c047..d6b38fd32e8f 100644 --- a/core/java/android/bluetooth/BluetoothHeadset.java +++ b/core/java/android/bluetooth/BluetoothHeadset.java @@ -113,7 +113,7 @@ public final class BluetoothHeadset implements BluetoothProfile { * @hide */ @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION) - @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553) + @UnsupportedAppUsage(trackingBug = 171933273) public static final String ACTION_ACTIVE_DEVICE_CHANGED = "android.bluetooth.headset.profile.action.ACTIVE_DEVICE_CHANGED"; @@ -1172,7 +1172,7 @@ public final class BluetoothHeadset implements BluetoothProfile { * @hide */ @RequiresPermission(android.Manifest.permission.BLUETOOTH_ADMIN) - @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553) + @UnsupportedAppUsage(trackingBug = 171933273) public boolean setActiveDevice(@Nullable BluetoothDevice device) { if (DBG) { Log.d(TAG, "setActiveDevice: " + device); @@ -1198,7 +1198,7 @@ public final class BluetoothHeadset implements BluetoothProfile { * is active. * @hide */ - @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553) + @UnsupportedAppUsage(trackingBug = 171933273) @Nullable @RequiresPermission(Manifest.permission.BLUETOOTH) public BluetoothDevice getActiveDevice() { diff --git a/core/java/android/companion/AssociationRequest.java b/core/java/android/companion/AssociationRequest.java index 57b0828b334c..083ce968d66f 100644 --- a/core/java/android/companion/AssociationRequest.java +++ b/core/java/android/companion/AssociationRequest.java @@ -96,11 +96,25 @@ public final class AssociationRequest implements Parcelable { */ private @Nullable String mCallingPackage = null; + /** + * The user-readable description of the device profile's privileges. + * + * Populated by the system. + * + * @hide + */ + private @Nullable String mDeviceProfilePrivilegesDescription = null; + /** @hide */ public void setCallingPackage(@NonNull String pkg) { mCallingPackage = pkg; } + /** @hide */ + public void setDeviceProfilePrivilegesDescription(@NonNull String desc) { + mDeviceProfilePrivilegesDescription = desc; + } + private void onConstructed() { if (mDeviceProfile != null && !Objects.equals(mDeviceProfile, DEVICE_PROFILE_WATCH)) { @@ -178,14 +192,14 @@ public final class AssociationRequest implements Parcelable { markUsed(); return new AssociationRequest( mSingleDevice, emptyIfNull(mDeviceFilters), - mDeviceProfile, null); + mDeviceProfile, null, null); } } - // Code below generated by codegen v1.0.20. + // Code below generated by codegen v1.0.22. // // DO NOT MODIFY! // CHECKSTYLE:OFF Generated code @@ -215,6 +229,10 @@ public final class AssociationRequest implements Parcelable { * The app package making the request. * * Populated by the system. + * @param deviceProfilePrivilegesDescription + * The user-readable description of the device profile's privileges. + * + * Populated by the system. * @hide */ @DataClass.Generated.Member @@ -222,7 +240,8 @@ public final class AssociationRequest implements Parcelable { boolean singleDevice, @NonNull List<DeviceFilter<?>> deviceFilters, @Nullable @DeviceProfile String deviceProfile, - @Nullable String callingPackage) { + @Nullable String callingPackage, + @Nullable String deviceProfilePrivilegesDescription) { this.mSingleDevice = singleDevice; this.mDeviceFilters = deviceFilters; com.android.internal.util.AnnotationValidations.validate( @@ -231,6 +250,7 @@ public final class AssociationRequest implements Parcelable { com.android.internal.util.AnnotationValidations.validate( DeviceProfile.class, null, mDeviceProfile); this.mCallingPackage = callingPackage; + this.mDeviceProfilePrivilegesDescription = deviceProfilePrivilegesDescription; onConstructed(); } @@ -257,6 +277,18 @@ public final class AssociationRequest implements Parcelable { return mCallingPackage; } + /** + * The user-readable description of the device profile's privileges. + * + * Populated by the system. + * + * @hide + */ + @DataClass.Generated.Member + public @Nullable String getDeviceProfilePrivilegesDescription() { + return mDeviceProfilePrivilegesDescription; + } + @Override @DataClass.Generated.Member public String toString() { @@ -267,7 +299,8 @@ public final class AssociationRequest implements Parcelable { "singleDevice = " + mSingleDevice + ", " + "deviceFilters = " + mDeviceFilters + ", " + "deviceProfile = " + mDeviceProfile + ", " + - "callingPackage = " + mCallingPackage + + "callingPackage = " + mCallingPackage + ", " + + "deviceProfilePrivilegesDescription = " + mDeviceProfilePrivilegesDescription + " }"; } @@ -287,7 +320,8 @@ public final class AssociationRequest implements Parcelable { && mSingleDevice == that.mSingleDevice && Objects.equals(mDeviceFilters, that.mDeviceFilters) && Objects.equals(mDeviceProfile, that.mDeviceProfile) - && Objects.equals(mCallingPackage, that.mCallingPackage); + && Objects.equals(mCallingPackage, that.mCallingPackage) + && Objects.equals(mDeviceProfilePrivilegesDescription, that.mDeviceProfilePrivilegesDescription); } @Override @@ -301,6 +335,7 @@ public final class AssociationRequest implements Parcelable { _hash = 31 * _hash + Objects.hashCode(mDeviceFilters); _hash = 31 * _hash + Objects.hashCode(mDeviceProfile); _hash = 31 * _hash + Objects.hashCode(mCallingPackage); + _hash = 31 * _hash + Objects.hashCode(mDeviceProfilePrivilegesDescription); return _hash; } @@ -314,10 +349,12 @@ public final class AssociationRequest implements Parcelable { if (mSingleDevice) flg |= 0x1; if (mDeviceProfile != null) flg |= 0x4; if (mCallingPackage != null) flg |= 0x8; + if (mDeviceProfilePrivilegesDescription != null) flg |= 0x10; dest.writeByte(flg); dest.writeParcelableList(mDeviceFilters, flags); if (mDeviceProfile != null) dest.writeString(mDeviceProfile); if (mCallingPackage != null) dest.writeString(mCallingPackage); + if (mDeviceProfilePrivilegesDescription != null) dest.writeString(mDeviceProfilePrivilegesDescription); } @Override @@ -337,6 +374,7 @@ public final class AssociationRequest implements Parcelable { in.readParcelableList(deviceFilters, DeviceFilter.class.getClassLoader()); String deviceProfile = (flg & 0x4) == 0 ? null : in.readString(); String callingPackage = (flg & 0x8) == 0 ? null : in.readString(); + String deviceProfilePrivilegesDescription = (flg & 0x10) == 0 ? null : in.readString(); this.mSingleDevice = singleDevice; this.mDeviceFilters = deviceFilters; @@ -346,6 +384,7 @@ public final class AssociationRequest implements Parcelable { com.android.internal.util.AnnotationValidations.validate( DeviceProfile.class, null, mDeviceProfile); this.mCallingPackage = callingPackage; + this.mDeviceProfilePrivilegesDescription = deviceProfilePrivilegesDescription; onConstructed(); } @@ -365,10 +404,10 @@ public final class AssociationRequest implements Parcelable { }; @DataClass.Generated( - time = 1604534468409L, - codegenVersion = "1.0.20", + time = 1610132130920L, + codegenVersion = "1.0.22", sourceFile = "frameworks/base/core/java/android/companion/AssociationRequest.java", - inputSignatures = "public static final java.lang.String DEVICE_PROFILE_WATCH\nprivate boolean mSingleDevice\nprivate @com.android.internal.util.DataClass.PluralOf(\"deviceFilter\") @android.annotation.NonNull java.util.List<android.companion.DeviceFilter<?>> mDeviceFilters\nprivate @android.annotation.Nullable @android.companion.AssociationRequest.DeviceProfile java.lang.String mDeviceProfile\nprivate @android.annotation.Nullable java.lang.String mCallingPackage\npublic void setCallingPackage(java.lang.String)\nprivate void onConstructed()\npublic @android.compat.annotation.UnsupportedAppUsage boolean isSingleDevice()\npublic @android.annotation.NonNull @android.compat.annotation.UnsupportedAppUsage java.util.List<android.companion.DeviceFilter<?>> getDeviceFilters()\nclass AssociationRequest extends java.lang.Object implements [android.os.Parcelable]\nprivate boolean mSingleDevice\nprivate @android.annotation.Nullable java.util.ArrayList<android.companion.DeviceFilter<?>> mDeviceFilters\nprivate @android.annotation.Nullable java.lang.String mDeviceProfile\npublic @android.annotation.NonNull android.companion.AssociationRequest.Builder setSingleDevice(boolean)\npublic @android.annotation.NonNull android.companion.AssociationRequest.Builder addDeviceFilter(android.companion.DeviceFilter<?>)\npublic @android.annotation.NonNull android.companion.AssociationRequest.Builder setDeviceProfile(java.lang.String)\npublic @android.annotation.NonNull @java.lang.Override android.companion.AssociationRequest build()\nclass Builder extends android.provider.OneTimeUseBuilder<android.companion.AssociationRequest> implements []\n@com.android.internal.util.DataClass(genToString=true, genEqualsHashCode=true, genHiddenGetters=true, genParcelable=true, genHiddenConstructor=true, genBuilder=false)") + inputSignatures = "public static final java.lang.String DEVICE_PROFILE_WATCH\nprivate boolean mSingleDevice\nprivate @com.android.internal.util.DataClass.PluralOf(\"deviceFilter\") @android.annotation.NonNull java.util.List<android.companion.DeviceFilter<?>> mDeviceFilters\nprivate @android.annotation.Nullable @android.companion.AssociationRequest.DeviceProfile java.lang.String mDeviceProfile\nprivate @android.annotation.Nullable java.lang.String mCallingPackage\nprivate @android.annotation.Nullable java.lang.String mDeviceProfilePrivilegesDescription\npublic void setCallingPackage(java.lang.String)\npublic void setDeviceProfilePrivilegesDescription(java.lang.String)\nprivate void onConstructed()\npublic @android.compat.annotation.UnsupportedAppUsage boolean isSingleDevice()\npublic @android.annotation.NonNull @android.compat.annotation.UnsupportedAppUsage java.util.List<android.companion.DeviceFilter<?>> getDeviceFilters()\nclass AssociationRequest extends java.lang.Object implements [android.os.Parcelable]\nprivate boolean mSingleDevice\nprivate @android.annotation.Nullable java.util.ArrayList<android.companion.DeviceFilter<?>> mDeviceFilters\nprivate @android.annotation.Nullable java.lang.String mDeviceProfile\npublic @android.annotation.NonNull android.companion.AssociationRequest.Builder setSingleDevice(boolean)\npublic @android.annotation.NonNull android.companion.AssociationRequest.Builder addDeviceFilter(android.companion.DeviceFilter<?>)\npublic @android.annotation.NonNull android.companion.AssociationRequest.Builder setDeviceProfile(java.lang.String)\npublic @android.annotation.NonNull @java.lang.Override android.companion.AssociationRequest build()\nclass Builder extends android.provider.OneTimeUseBuilder<android.companion.AssociationRequest> implements []\n@com.android.internal.util.DataClass(genToString=true, genEqualsHashCode=true, genHiddenGetters=true, genParcelable=true, genHiddenConstructor=true, genBuilder=false)") @Deprecated private void __metadata() {} diff --git a/core/java/android/content/Context.java b/core/java/android/content/Context.java index 43011fce2125..5ccceca9a69d 100644 --- a/core/java/android/content/Context.java +++ b/core/java/android/content/Context.java @@ -4509,6 +4509,17 @@ public abstract class Context { public static final String CONTENT_CAPTURE_MANAGER_SERVICE = "content_capture"; /** + * Official published name of the translation service. + * + * @hide + * @see #getSystemService(String) + */ + // TODO(b/176208267): change it back to translation before S release. + @SystemApi + @SuppressLint("ServiceName") + public static final String TRANSLATION_MANAGER_SERVICE = "transformer"; + + /** * Used for getting content selections and classifications for task snapshots. * * @hide diff --git a/core/java/android/content/om/IOverlayManager.aidl b/core/java/android/content/om/IOverlayManager.aidl index 0b950b461285..44b5c4482599 100644 --- a/core/java/android/content/om/IOverlayManager.aidl +++ b/core/java/android/content/om/IOverlayManager.aidl @@ -17,7 +17,6 @@ package android.content.om; import android.content.om.OverlayInfo; -import android.content.om.OverlayManagerTransaction; /** * Api for getting information about overlay packages. @@ -164,18 +163,4 @@ interface IOverlayManager { * @param packageName The name of the overlay package whose idmap should be deleted. */ void invalidateCachesForOverlay(in String packageName, in int userIs); - - /** - * Perform a series of requests related to overlay packages. This is an - * atomic operation: either all requests were performed successfully and - * the changes were propagated to the rest of the system, or at least one - * request could not be performed successfully and nothing is changed and - * nothing is propagated to the rest of the system. - * - * @see OverlayManagerTransaction - * - * @param transaction the series of overlay related requests to perform - * @throws SecurityException if the transaction failed - */ - void commit(in OverlayManagerTransaction transaction); } diff --git a/core/java/android/content/om/OverlayManager.java b/core/java/android/content/om/OverlayManager.java index 7c14c2891d01..217f637cf9e3 100644 --- a/core/java/android/content/om/OverlayManager.java +++ b/core/java/android/content/om/OverlayManager.java @@ -254,29 +254,6 @@ public class OverlayManager { } /** - * Perform a series of requests related to overlay packages. This is an - * atomic operation: either all requests were performed successfully and - * the changes were propagated to the rest of the system, or at least one - * request could not be performed successfully and nothing is changed and - * nothing is propagated to the rest of the system. - * - * @see OverlayManagerTransaction - * - * @param transaction the series of overlay related requests to perform - * @throws Exception if not all the requests could be successfully and - * atomically executed - * - * @hide - */ - public void commit(@NonNull final OverlayManagerTransaction transaction) { - try { - mService.commit(transaction); - } catch (RemoteException e) { - throw e.rethrowFromSystemServer(); - } - } - - /** * Starting on R, actor enforcement and app visibility changes introduce additional failure * cases, but the SecurityException thrown with these checks is unexpected for existing * consumers of the API. diff --git a/core/java/android/content/om/OverlayManagerTransaction.java b/core/java/android/content/om/OverlayManagerTransaction.java deleted file mode 100644 index 1fa8973c35b5..000000000000 --- a/core/java/android/content/om/OverlayManagerTransaction.java +++ /dev/null @@ -1,216 +0,0 @@ -/* - * Copyright (C) 2019 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package android.content.om; - -import static com.android.internal.util.Preconditions.checkNotNull; - -import android.annotation.IntDef; -import android.annotation.NonNull; -import android.os.Parcel; -import android.os.Parcelable; -import android.os.UserHandle; - -import java.lang.annotation.Retention; -import java.lang.annotation.RetentionPolicy; -import java.util.ArrayList; -import java.util.Iterator; -import java.util.List; - -/** - * Container for a batch of requests to the OverlayManagerService. - * - * Transactions are created using a builder interface. Example usage: - * - * final OverlayManager om = ctx.getSystemService(OverlayManager.class); - * final OverlayManagerTransaction t = new OverlayManagerTransaction.Builder() - * .setEnabled(...) - * .setEnabled(...) - * .build(); - * om.commit(t); - * - * @hide - */ -public class OverlayManagerTransaction - implements Iterable<OverlayManagerTransaction.Request>, Parcelable { - // TODO: remove @hide from this class when OverlayManager is added to the - // SDK, but keep OverlayManagerTransaction.Request @hidden - private final List<Request> mRequests; - - OverlayManagerTransaction(@NonNull final List<Request> requests) { - checkNotNull(requests); - if (requests.contains(null)) { - throw new IllegalArgumentException("null request"); - } - mRequests = requests; - } - - private OverlayManagerTransaction(@NonNull final Parcel source) { - final int size = source.readInt(); - mRequests = new ArrayList<Request>(size); - for (int i = 0; i < size; i++) { - final int request = source.readInt(); - final String packageName = source.readString(); - final int userId = source.readInt(); - mRequests.add(new Request(request, packageName, userId)); - } - } - - @Override - public Iterator<Request> iterator() { - return mRequests.iterator(); - } - - @Override - public String toString() { - return String.format("OverlayManagerTransaction { mRequests = %s }", mRequests); - } - - /** - * A single unit of the transaction, such as a request to enable an - * overlay, or to disable an overlay. - * - * @hide - */ - public static class Request { - @IntDef(prefix = "TYPE_", value = { - TYPE_SET_ENABLED, - TYPE_SET_DISABLED, - }) - @Retention(RetentionPolicy.SOURCE) - @interface RequestType {} - - public static final int TYPE_SET_ENABLED = 0; - public static final int TYPE_SET_DISABLED = 1; - - @RequestType public final int type; - public final String packageName; - public final int userId; - - public Request(@RequestType final int type, @NonNull final String packageName, - final int userId) { - this.type = type; - this.packageName = packageName; - this.userId = userId; - } - - @Override - public String toString() { - return String.format("Request{type=0x%02x (%s), packageName=%s, userId=%d}", - type, typeToString(), packageName, userId); - } - - /** - * Translate the request type into a human readable string. Only - * intended for debugging. - * - * @hide - */ - public String typeToString() { - switch (type) { - case TYPE_SET_ENABLED: return "TYPE_SET_ENABLED"; - case TYPE_SET_DISABLED: return "TYPE_SET_DISABLED"; - default: return String.format("TYPE_UNKNOWN (0x%02x)", type); - } - } - } - - /** - * Builder class for OverlayManagerTransaction objects. - * - * @hide - */ - public static class Builder { - private final List<Request> mRequests = new ArrayList<>(); - - /** - * Request that an overlay package be enabled and change its loading - * order to the last package to be loaded, or disabled - * - * If the caller has the correct permissions, it is always possible to - * disable an overlay. Due to technical and security reasons it may not - * always be possible to enable an overlay, for instance if the overlay - * does not successfully overlay any target resources due to - * overlayable policy restrictions. - * - * An enabled overlay is a part of target package's resources, i.e. it will - * be part of any lookups performed via {@link android.content.res.Resources} - * and {@link android.content.res.AssetManager}. A disabled overlay will no - * longer affect the resources of the target package. If the target is - * currently running, its outdated resources will be replaced by new ones. - * - * @param packageName The name of the overlay package. - * @param enable true to enable the overlay, false to disable it. - * @return this Builder object, so you can chain additional requests - */ - public Builder setEnabled(@NonNull String packageName, boolean enable) { - return setEnabled(packageName, enable, UserHandle.myUserId()); - } - - /** - * @hide - */ - public Builder setEnabled(@NonNull String packageName, boolean enable, int userId) { - checkNotNull(packageName); - @Request.RequestType final int type = - enable ? Request.TYPE_SET_ENABLED : Request.TYPE_SET_DISABLED; - mRequests.add(new Request(type, packageName, userId)); - return this; - } - - /** - * Create a new transaction out of the requests added so far. Execute - * the transaction by calling OverlayManager#commit. - * - * @see OverlayManager#commit - * @return a new transaction - */ - public OverlayManagerTransaction build() { - return new OverlayManagerTransaction(mRequests); - } - } - - @Override - public int describeContents() { - return 0; - } - - @Override - public void writeToParcel(Parcel dest, int flags) { - final int size = mRequests.size(); - dest.writeInt(size); - for (int i = 0; i < size; i++) { - final Request req = mRequests.get(i); - dest.writeInt(req.type); - dest.writeString(req.packageName); - dest.writeInt(req.userId); - } - } - - public static final Parcelable.Creator<OverlayManagerTransaction> CREATOR = - new Parcelable.Creator<OverlayManagerTransaction>() { - - @Override - public OverlayManagerTransaction createFromParcel(Parcel source) { - return new OverlayManagerTransaction(source); - } - - @Override - public OverlayManagerTransaction[] newArray(int size) { - return new OverlayManagerTransaction[size]; - } - }; -} diff --git a/core/java/android/content/pm/IPackageManager.aidl b/core/java/android/content/pm/IPackageManager.aidl index f634b8a54a0f..7c0b82161988 100644 --- a/core/java/android/content/pm/IPackageManager.aidl +++ b/core/java/android/content/pm/IPackageManager.aidl @@ -65,7 +65,7 @@ import android.content.IntentSender; */ interface IPackageManager { void checkPackageStartable(String packageName, int userId); - @UnsupportedAppUsage(maxTargetSdk = 30, trackingBug = 170729553) + @UnsupportedAppUsage(trackingBug = 171933273) boolean isPackageAvailable(String packageName, int userId); @UnsupportedAppUsage PackageInfo getPackageInfo(String packageName, int flags, int userId); diff --git a/core/java/android/content/pm/OWNERS b/core/java/android/content/pm/OWNERS index f88df958d827..fd32efccbcec 100644 --- a/core/java/android/content/pm/OWNERS +++ b/core/java/android/content/pm/OWNERS @@ -7,3 +7,4 @@ patb@google.com per-file PackageParser.java = chiuwinson@google.com per-file *Shortcut* = file:/core/java/android/content/pm/SHORTCUT_OWNERS per-file *Launcher* = file:/core/java/android/content/pm/LAUNCHER_OWNERS +per-file UserInfo* = file:/MULTIUSER_OWNERS diff --git a/core/java/android/content/pm/PackageManager.java b/core/java/android/content/pm/PackageManager.java index e074eab99a7c..17c4d25d82d7 100644 --- a/core/java/android/content/pm/PackageManager.java +++ b/core/java/android/content/pm/PackageManager.java @@ -3406,6 +3406,14 @@ public abstract class PackageManager { /** * Feature for {@link #getSystemAvailableFeatures} and {@link #hasSystemFeature}: + * The device supports translation of text-to-text in multiple languages via integration with + * the system {@link android.service.translation.TranslationService translation provider}. + */ + @SdkConstant(SdkConstantType.FEATURE) + public static final String FEATURE_TRANSLATION = "android.software.translation"; + + /** + * Feature for {@link #getSystemAvailableFeatures} and {@link #hasSystemFeature}: * The device implements headtracking suitable for a VR device. */ @SdkConstant(SdkConstantType.FEATURE) diff --git a/core/java/android/hardware/biometrics/BiometricManager.java b/core/java/android/hardware/biometrics/BiometricManager.java index 987d790601b4..4145a7273ed2 100644 --- a/core/java/android/hardware/biometrics/BiometricManager.java +++ b/core/java/android/hardware/biometrics/BiometricManager.java @@ -358,6 +358,27 @@ public class BiometricManager { } /** + * Requests all {@link Authenticators.Types#BIOMETRIC_STRONG} sensors to have their + * authenticatorId invalidated for the specified user. This happens when enrollments have been + * added on devices with multiple biometric sensors. + * + * @param userId userId that the authenticatorId should be invalidated for + * @param fromSensorId sensor that triggered the invalidation request + * @hide + */ + @RequiresPermission(USE_BIOMETRIC_INTERNAL) + public void invalidateAuthenticatorIds(int userId, int fromSensorId, + @NonNull IInvalidationCallback callback) { + if (mService != null) { + try { + mService.invalidateAuthenticatorIds(userId, fromSensorId, callback); + } catch (RemoteException e) { + throw e.rethrowFromSystemServer(); + } + } + } + + /** * Get a list of AuthenticatorIDs for biometric authenticators which have 1) enrolled templates, * and 2) meet the requirements for integrating with Keystore. The AuthenticatorIDs are known * in Keystore land as SIDs, and are used during key generation. diff --git a/core/java/android/hardware/biometrics/IAuthService.aidl b/core/java/android/hardware/biometrics/IAuthService.aidl index 8e7f5ce8a85d..0dfd5dbf300e 100644 --- a/core/java/android/hardware/biometrics/IAuthService.aidl +++ b/core/java/android/hardware/biometrics/IAuthService.aidl @@ -18,6 +18,7 @@ package android.hardware.biometrics; import android.hardware.biometrics.IBiometricEnabledOnKeyguardCallback; import android.hardware.biometrics.IBiometricServiceReceiver; +import android.hardware.biometrics.IInvalidationCallback; import android.hardware.biometrics.ITestSession; import android.hardware.biometrics.PromptInfo; import android.hardware.biometrics.SensorPropertiesInternal; @@ -57,6 +58,11 @@ interface IAuthService { // Register callback for when keyguard biometric eligibility changes. void registerEnabledOnKeyguardCallback(IBiometricEnabledOnKeyguardCallback callback); + // Requests all BIOMETRIC_STRONG sensors to have their authenticatorId invalidated for the + // specified user. This happens when enrollments have been added on devices with multiple + // biometric sensors. + void invalidateAuthenticatorIds(int userId, int fromSensorId, IInvalidationCallback callback); + // Get a list of AuthenticatorIDs for authenticators which have enrolled templates and meet // the requirements for integrating with Keystore. The AuthenticatorID are known in Keystore // land as SIDs, and are used during key generation. diff --git a/core/java/android/hardware/biometrics/IBiometricAuthenticator.aidl b/core/java/android/hardware/biometrics/IBiometricAuthenticator.aidl index cc12125c13f0..fcdf61e99471 100644 --- a/core/java/android/hardware/biometrics/IBiometricAuthenticator.aidl +++ b/core/java/android/hardware/biometrics/IBiometricAuthenticator.aidl @@ -18,6 +18,7 @@ package android.hardware.biometrics; import android.hardware.biometrics.IBiometricSensorReceiver; import android.hardware.biometrics.IBiometricServiceLockoutResetCallback; +import android.hardware.biometrics.IInvalidationCallback; import android.hardware.biometrics.ITestSession; import android.hardware.biometrics.SensorPropertiesInternal; import android.hardware.face.IFaceServiceReceiver; @@ -63,6 +64,9 @@ interface IBiometricAuthenticator { // Return the LockoutTracker status for the specified user int getLockoutModeForUser(int userId); + // Request the authenticatorId to be invalidated for the specified user + void invalidateAuthenticatorId(int userId, IInvalidationCallback callback); + // Gets the authenticator ID representing the current set of enrolled templates long getAuthenticatorId(int callingUserId); } diff --git a/core/java/android/hardware/biometrics/IBiometricService.aidl b/core/java/android/hardware/biometrics/IBiometricService.aidl index 6f7bcb68cc8d..a14a910a9e50 100644 --- a/core/java/android/hardware/biometrics/IBiometricService.aidl +++ b/core/java/android/hardware/biometrics/IBiometricService.aidl @@ -19,6 +19,7 @@ package android.hardware.biometrics; import android.hardware.biometrics.IBiometricEnabledOnKeyguardCallback; import android.hardware.biometrics.IBiometricServiceReceiver; import android.hardware.biometrics.IBiometricAuthenticator; +import android.hardware.biometrics.IInvalidationCallback; import android.hardware.biometrics.ITestSession; import android.hardware.biometrics.PromptInfo; import android.hardware.biometrics.SensorPropertiesInternal; @@ -62,6 +63,11 @@ interface IBiometricService { // Client lifecycle is still managed in <Biometric>Service. void onReadyForAuthentication(int cookie); + // Requests all BIOMETRIC_STRONG sensors to have their authenticatorId invalidated for the + // specified user. This happens when enrollments have been added on devices with multiple + // biometric sensors. + void invalidateAuthenticatorIds(int userId, int fromSensorId, IInvalidationCallback callback); + // Get a list of AuthenticatorIDs for authenticators which have enrolled templates and meet // the requirements for integrating with Keystore. The AuthenticatorID are known in Keystore // land as SIDs, and are used during key generation. diff --git a/core/java/android/hardware/biometrics/IInvalidationCallback.aidl b/core/java/android/hardware/biometrics/IInvalidationCallback.aidl new file mode 100644 index 000000000000..24f7d9d7d42e --- /dev/null +++ b/core/java/android/hardware/biometrics/IInvalidationCallback.aidl @@ -0,0 +1,27 @@ +/* + * 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.hardware.biometrics; + +/** + * Notifies the caller for BiometricManager#invalidateAuthenticatorIds status updates. See + * InvalidationRequesterClient for more info. + * @hide + */ +interface IInvalidationCallback { + // Notifies the caller when all authenticatorId(s) have been invalidated. + void onCompleted(); +}
\ No newline at end of file diff --git a/core/java/android/hardware/face/IFaceService.aidl b/core/java/android/hardware/face/IFaceService.aidl index 468157a19971..3b19f12a41ba 100644 --- a/core/java/android/hardware/face/IFaceService.aidl +++ b/core/java/android/hardware/face/IFaceService.aidl @@ -17,6 +17,7 @@ package android.hardware.face; import android.hardware.biometrics.IBiometricSensorReceiver; import android.hardware.biometrics.IBiometricServiceLockoutResetCallback; +import android.hardware.biometrics.IInvalidationCallback; import android.hardware.biometrics.ITestSession; import android.hardware.face.IFaceServiceReceiver; import android.hardware.face.Face; @@ -104,6 +105,9 @@ interface IFaceService { // Return the LockoutTracker status for the specified user int getLockoutModeForUser(int sensorId, int userId); + // Requests for the specified sensor+userId's authenticatorId to be invalidated + void invalidateAuthenticatorId(int sensorId, int userId, IInvalidationCallback callback); + // Gets the authenticator ID for face long getAuthenticatorId(int sensorId, int callingUserId); diff --git a/core/java/android/hardware/fingerprint/IFingerprintService.aidl b/core/java/android/hardware/fingerprint/IFingerprintService.aidl index ac026c796b51..74c5b5864e87 100644 --- a/core/java/android/hardware/fingerprint/IFingerprintService.aidl +++ b/core/java/android/hardware/fingerprint/IFingerprintService.aidl @@ -17,6 +17,7 @@ package android.hardware.fingerprint; import android.hardware.biometrics.IBiometricSensorReceiver; import android.hardware.biometrics.IBiometricServiceLockoutResetCallback; +import android.hardware.biometrics.IInvalidationCallback; import android.hardware.biometrics.ITestSession; import android.hardware.fingerprint.IFingerprintClientActiveCallback; import android.hardware.fingerprint.IFingerprintServiceReceiver; @@ -116,6 +117,9 @@ interface IFingerprintService { // Return the LockoutTracker status for the specified user int getLockoutModeForUser(int sensorId, int userId); + // Requests for the specified sensor+userId's authenticatorId to be invalidated + void invalidateAuthenticatorId(int sensorId, int userId, IInvalidationCallback callback); + // Gets the authenticator ID for fingerprint long getAuthenticatorId(int sensorId, int callingUserId); diff --git a/core/java/android/net/CaptivePortalData.java b/core/java/android/net/CaptivePortalData.java index 59e62a675abc..9b56b23cc879 100644 --- a/core/java/android/net/CaptivePortalData.java +++ b/core/java/android/net/CaptivePortalData.java @@ -39,9 +39,11 @@ public final class CaptivePortalData implements Parcelable { private final long mByteLimit; private final long mExpiryTimeMillis; private final boolean mCaptive; + private final String mVenueFriendlyName; private CaptivePortalData(long refreshTimeMillis, Uri userPortalUrl, Uri venueInfoUrl, - boolean isSessionExtendable, long byteLimit, long expiryTimeMillis, boolean captive) { + boolean isSessionExtendable, long byteLimit, long expiryTimeMillis, boolean captive, + String venueFriendlyName) { mRefreshTimeMillis = refreshTimeMillis; mUserPortalUrl = userPortalUrl; mVenueInfoUrl = venueInfoUrl; @@ -49,11 +51,12 @@ public final class CaptivePortalData implements Parcelable { mByteLimit = byteLimit; mExpiryTimeMillis = expiryTimeMillis; mCaptive = captive; + mVenueFriendlyName = venueFriendlyName; } private CaptivePortalData(Parcel p) { this(p.readLong(), p.readParcelable(null), p.readParcelable(null), p.readBoolean(), - p.readLong(), p.readLong(), p.readBoolean()); + p.readLong(), p.readLong(), p.readBoolean(), p.readString()); } @Override @@ -70,6 +73,7 @@ public final class CaptivePortalData implements Parcelable { dest.writeLong(mByteLimit); dest.writeLong(mExpiryTimeMillis); dest.writeBoolean(mCaptive); + dest.writeString(mVenueFriendlyName); } /** @@ -83,6 +87,7 @@ public final class CaptivePortalData implements Parcelable { private long mBytesRemaining = -1; private long mExpiryTime = -1; private boolean mCaptive; + private String mVenueFriendlyName; /** * Create an empty builder. @@ -100,7 +105,8 @@ public final class CaptivePortalData implements Parcelable { .setSessionExtendable(data.mIsSessionExtendable) .setBytesRemaining(data.mByteLimit) .setExpiryTime(data.mExpiryTimeMillis) - .setCaptive(data.mCaptive); + .setCaptive(data.mCaptive) + .setVenueFriendlyName(data.mVenueFriendlyName); } /** @@ -167,12 +173,22 @@ public final class CaptivePortalData implements Parcelable { } /** + * Set the venue friendly name. + */ + @NonNull + public Builder setVenueFriendlyName(@Nullable String venueFriendlyName) { + mVenueFriendlyName = venueFriendlyName; + return this; + } + + /** * Create a new {@link CaptivePortalData}. */ @NonNull public CaptivePortalData build() { return new CaptivePortalData(mRefreshTime, mUserPortalUrl, mVenueInfoUrl, - mIsSessionExtendable, mBytesRemaining, mExpiryTime, mCaptive); + mIsSessionExtendable, mBytesRemaining, mExpiryTime, mCaptive, + mVenueFriendlyName); } } @@ -232,6 +248,14 @@ public final class CaptivePortalData implements Parcelable { return mCaptive; } + /** + * Get the venue friendly name + */ + @Nullable + public String getVenueFriendlyName() { + return mVenueFriendlyName; + } + @NonNull public static final Creator<CaptivePortalData> CREATOR = new Creator<CaptivePortalData>() { @Override @@ -248,7 +272,7 @@ public final class CaptivePortalData implements Parcelable { @Override public int hashCode() { return Objects.hash(mRefreshTimeMillis, mUserPortalUrl, mVenueInfoUrl, - mIsSessionExtendable, mByteLimit, mExpiryTimeMillis, mCaptive); + mIsSessionExtendable, mByteLimit, mExpiryTimeMillis, mCaptive, mVenueFriendlyName); } @Override @@ -261,7 +285,8 @@ public final class CaptivePortalData implements Parcelable { && mIsSessionExtendable == other.mIsSessionExtendable && mByteLimit == other.mByteLimit && mExpiryTimeMillis == other.mExpiryTimeMillis - && mCaptive == other.mCaptive; + && mCaptive == other.mCaptive + && Objects.equals(mVenueFriendlyName, other.mVenueFriendlyName); } @Override @@ -274,6 +299,7 @@ public final class CaptivePortalData implements Parcelable { + ", byteLimit: " + mByteLimit + ", expiryTime: " + mExpiryTimeMillis + ", captive: " + mCaptive + + ", venueFriendlyName: " + mVenueFriendlyName + "}"; } } diff --git a/core/java/android/net/IIpConnectivityMetrics.aidl b/core/java/android/net/IIpConnectivityMetrics.aidl index aeaf09d8fafe..aa3682d92105 100644 --- a/core/java/android/net/IIpConnectivityMetrics.aidl +++ b/core/java/android/net/IIpConnectivityMetrics.aidl @@ -19,6 +19,9 @@ package android.net; import android.os.Parcelable; import android.net.ConnectivityMetricsEvent; import android.net.INetdEventCallback; +import android.net.LinkProperties; +import android.net.Network; +import android.net.NetworkCapabilities; /** {@hide} */ interface IIpConnectivityMetrics { @@ -29,6 +32,11 @@ interface IIpConnectivityMetrics { */ int logEvent(in ConnectivityMetricsEvent event); + void logDefaultNetworkValidity(boolean valid); + void logDefaultNetworkEvent(in Network defaultNetwork, int score, boolean validated, + in LinkProperties lp, in NetworkCapabilities nc, in Network previousDefaultNetwork, + int previousScore, in LinkProperties previousLp, in NetworkCapabilities previousNc); + /** * Callback can be registered by DevicePolicyManager or NetworkWatchlistService only. * @return status {@code true} if registering/unregistering of the callback was successful, diff --git a/core/java/android/net/INetworkManagementEventObserver.aidl b/core/java/android/net/INetworkManagementEventObserver.aidl index 37813ce11a5f..0a6be20226b8 100644 --- a/core/java/android/net/INetworkManagementEventObserver.aidl +++ b/core/java/android/net/INetworkManagementEventObserver.aidl @@ -85,14 +85,14 @@ oneway interface INetworkManagementEventObserver { /** * Interface data activity status is changed. * - * @param networkType The legacy network type of the data activity change. + * @param transportType The transport type of the data activity change. * @param active True if the interface is actively transmitting data, false if it is idle. * @param tsNanos Elapsed realtime in nanos when the state of the network interface changed. * @param uid Uid of this event. It represents the uid that was responsible for waking the * radio. For those events that are reported by system itself, not from specific uid, * use -1 for the events which means no uid. */ - void interfaceClassDataActivityChanged(int networkType, boolean active, long tsNanos, int uid); + void interfaceClassDataActivityChanged(int transportType, boolean active, long tsNanos, int uid); /** * Information about available DNS servers has been received. diff --git a/core/java/android/net/NetworkAgent.java b/core/java/android/net/NetworkAgent.java index 4166c2c4f244..4f46736c087d 100644 --- a/core/java/android/net/NetworkAgent.java +++ b/core/java/android/net/NetworkAgent.java @@ -408,7 +408,8 @@ public abstract class NetworkAgent { throw new IllegalArgumentException(); } - mInitialConfiguration = new InitialConfiguration(context, new NetworkCapabilities(nc), + mInitialConfiguration = new InitialConfiguration(context, + new NetworkCapabilities(nc, /* parcelLocationSensitiveFields */ true), new LinkProperties(lp), score, config, ni); } @@ -818,7 +819,9 @@ public abstract class NetworkAgent { Objects.requireNonNull(networkCapabilities); mBandwidthUpdatePending.set(false); mLastBwRefreshTime = System.currentTimeMillis(); - final NetworkCapabilities nc = new NetworkCapabilities(networkCapabilities); + final NetworkCapabilities nc = + new NetworkCapabilities(networkCapabilities, + /* parcelLocationSensitiveFields */ true); queueOrSendMessage(reg -> reg.sendNetworkCapabilities(nc)); } diff --git a/core/java/android/net/NetworkCapabilities.java b/core/java/android/net/NetworkCapabilities.java index 1a37fb9fb690..455d7426a170 100644 --- a/core/java/android/net/NetworkCapabilities.java +++ b/core/java/android/net/NetworkCapabilities.java @@ -76,12 +76,33 @@ public final class NetworkCapabilities implements Parcelable { */ private String mRequestorPackageName; + /** + * Indicates whether parceling should preserve fields that are set based on permissions of + * the process receiving the {@link NetworkCapabilities}. + */ + private final boolean mParcelLocationSensitiveFields; + public NetworkCapabilities() { + mParcelLocationSensitiveFields = false; clearAll(); mNetworkCapabilities = DEFAULT_CAPABILITIES; } public NetworkCapabilities(NetworkCapabilities nc) { + this(nc, false /* parcelLocationSensitiveFields */); + } + + /** + * Make a copy of NetworkCapabilities. + * + * @param nc Original NetworkCapabilities + * @param parcelLocationSensitiveFields Whether to parcel location sensitive data or not. + * @hide + */ + @SystemApi + public NetworkCapabilities( + @Nullable NetworkCapabilities nc, boolean parcelLocationSensitiveFields) { + mParcelLocationSensitiveFields = parcelLocationSensitiveFields; if (nc != null) { set(nc); } @@ -93,6 +114,12 @@ public final class NetworkCapabilities implements Parcelable { * @hide */ public void clearAll() { + // Ensures that the internal copies maintained by the connectivity stack does not set + // this bit. + if (mParcelLocationSensitiveFields) { + throw new UnsupportedOperationException( + "Cannot clear NetworkCapabilities when parcelLocationSensitiveFields is set"); + } mNetworkCapabilities = mTransportTypes = mUnwantedNetworkCapabilities = 0; mLinkUpBandwidthKbps = mLinkDownBandwidthKbps = LINK_BANDWIDTH_UNSPECIFIED; mNetworkSpecifier = null; @@ -109,6 +136,8 @@ public final class NetworkCapabilities implements Parcelable { /** * Set all contents of this object to the contents of a NetworkCapabilities. + * + * @param nc Original NetworkCapabilities * @hide */ public void set(@NonNull NetworkCapabilities nc) { @@ -117,7 +146,11 @@ public final class NetworkCapabilities implements Parcelable { mLinkUpBandwidthKbps = nc.mLinkUpBandwidthKbps; mLinkDownBandwidthKbps = nc.mLinkDownBandwidthKbps; mNetworkSpecifier = nc.mNetworkSpecifier; - mTransportInfo = nc.mTransportInfo; + if (nc.getTransportInfo() != null) { + setTransportInfo(nc.getTransportInfo().makeCopy(mParcelLocationSensitiveFields)); + } else { + setTransportInfo(null); + } mSignalStrength = nc.mSignalStrength; setUids(nc.mUids); // Will make the defensive copy setAdministratorUids(nc.getAdministratorUids()); @@ -171,6 +204,8 @@ public final class NetworkCapabilities implements Parcelable { NET_CAPABILITY_PARTIAL_CONNECTIVITY, NET_CAPABILITY_TEMPORARILY_NOT_METERED, NET_CAPABILITY_OEM_PRIVATE, + NET_CAPABILITY_VEHICLE_INTERNAL, + NET_CAPABILITY_NOT_VCN_MANAGED, }) public @interface NetCapability { } @@ -357,8 +392,26 @@ public final class NetworkCapabilities implements Parcelable { @SystemApi public static final int NET_CAPABILITY_OEM_PRIVATE = 26; + /** + * Indicates this is an internal vehicle network, meant to communicate with other + * automotive systems. + * + * @hide + */ + @SystemApi + public static final int NET_CAPABILITY_VEHICLE_INTERNAL = 27; + + /** + * Indicates that this network is not managed by a Virtual Carrier Network (VCN). + * + * TODO(b/177299683): Add additional clarifying javadoc. + * @hide + */ + @SystemApi + public static final int NET_CAPABILITY_NOT_VCN_MANAGED = 28; + private static final int MIN_NET_CAPABILITY = NET_CAPABILITY_MMS; - private static final int MAX_NET_CAPABILITY = NET_CAPABILITY_OEM_PRIVATE; + private static final int MAX_NET_CAPABILITY = NET_CAPABILITY_NOT_VCN_MANAGED; /** * Network capabilities that are expected to be mutable, i.e., can change while a particular @@ -375,7 +428,8 @@ public final class NetworkCapabilities implements Parcelable { | (1 << NET_CAPABILITY_NOT_CONGESTED) | (1 << NET_CAPABILITY_NOT_SUSPENDED) | (1 << NET_CAPABILITY_PARTIAL_CONNECTIVITY) - | (1 << NET_CAPABILITY_TEMPORARILY_NOT_METERED); + | (1 << NET_CAPABILITY_TEMPORARILY_NOT_METERED) + | (1 << NET_CAPABILITY_NOT_VCN_MANAGED); /** * Network capabilities that are not allowed in NetworkRequests. This exists because the @@ -384,16 +438,22 @@ public final class NetworkCapabilities implements Parcelable { * can get into a cycle where the NetworkFactory endlessly churns out NetworkAgents that then * get immediately torn down because they do not have the requested capability. */ + // Note that as a historical exception, the TRUSTED and NOT_VCN_MANAGED capabilities + // are mutable but requestable. Factories are responsible for not getting + // in an infinite loop about these. private static final long NON_REQUESTABLE_CAPABILITIES = - MUTABLE_CAPABILITIES & ~(1 << NET_CAPABILITY_TRUSTED); + MUTABLE_CAPABILITIES + & ~(1 << NET_CAPABILITY_TRUSTED) + & ~(1 << NET_CAPABILITY_NOT_VCN_MANAGED); /** * Capabilities that are set by default when the object is constructed. */ private static final long DEFAULT_CAPABILITIES = - (1 << NET_CAPABILITY_NOT_RESTRICTED) | - (1 << NET_CAPABILITY_TRUSTED) | - (1 << NET_CAPABILITY_NOT_VPN); + (1 << NET_CAPABILITY_NOT_RESTRICTED) + | (1 << NET_CAPABILITY_TRUSTED) + | (1 << NET_CAPABILITY_NOT_VPN) + | (1 << NET_CAPABILITY_NOT_VCN_MANAGED); /** * Capabilities that suggest that a network is restricted. @@ -401,15 +461,16 @@ public final class NetworkCapabilities implements Parcelable { */ @VisibleForTesting /* package */ static final long RESTRICTED_CAPABILITIES = - (1 << NET_CAPABILITY_CBS) | - (1 << NET_CAPABILITY_DUN) | - (1 << NET_CAPABILITY_EIMS) | - (1 << NET_CAPABILITY_FOTA) | - (1 << NET_CAPABILITY_IA) | - (1 << NET_CAPABILITY_IMS) | - (1 << NET_CAPABILITY_RCS) | - (1 << NET_CAPABILITY_XCAP) | - (1 << NET_CAPABILITY_MCX); + (1 << NET_CAPABILITY_CBS) + | (1 << NET_CAPABILITY_DUN) + | (1 << NET_CAPABILITY_EIMS) + | (1 << NET_CAPABILITY_FOTA) + | (1 << NET_CAPABILITY_IA) + | (1 << NET_CAPABILITY_IMS) + | (1 << NET_CAPABILITY_MCX) + | (1 << NET_CAPABILITY_RCS) + | (1 << NET_CAPABILITY_VEHICLE_INTERNAL) + | (1 << NET_CAPABILITY_XCAP); /** * Capabilities that force network to be restricted. @@ -452,7 +513,8 @@ public final class NetworkCapabilities implements Parcelable { | (1 << NET_CAPABILITY_NOT_VPN) | (1 << NET_CAPABILITY_NOT_ROAMING) | (1 << NET_CAPABILITY_NOT_CONGESTED) - | (1 << NET_CAPABILITY_NOT_SUSPENDED); + | (1 << NET_CAPABILITY_NOT_SUSPENDED) + | (1 << NET_CAPABILITY_NOT_VCN_MANAGED); /** * Adds the given capability to this {@code NetworkCapability} instance. @@ -1939,6 +2001,8 @@ public final class NetworkCapabilities implements Parcelable { case NET_CAPABILITY_PARTIAL_CONNECTIVITY: return "PARTIAL_CONNECTIVITY"; case NET_CAPABILITY_TEMPORARILY_NOT_METERED: return "TEMPORARILY_NOT_METERED"; case NET_CAPABILITY_OEM_PRIVATE: return "OEM_PRIVATE"; + case NET_CAPABILITY_VEHICLE_INTERNAL: return "NET_CAPABILITY_VEHICLE_INTERNAL"; + case NET_CAPABILITY_NOT_VCN_MANAGED: return "NOT_VCN_MANAGED"; default: return Integer.toString(capability); } } diff --git a/core/java/android/net/NetworkPolicyManager.java b/core/java/android/net/NetworkPolicyManager.java index 8bfbad605420..c029deae09df 100644 --- a/core/java/android/net/NetworkPolicyManager.java +++ b/core/java/android/net/NetworkPolicyManager.java @@ -32,8 +32,8 @@ import android.content.pm.Signature; import android.net.wifi.WifiConfiguration; import android.net.wifi.WifiInfo; import android.os.Build; +import android.os.Process; import android.os.RemoteException; -import android.os.UserHandle; import android.telephony.SubscriptionPlan; import android.util.DebugUtils; import android.util.Pair; @@ -500,7 +500,7 @@ public class NetworkPolicyManager { @Deprecated public static boolean isUidValidForPolicy(Context context, int uid) { // first, quick-reject non-applications - if (!UserHandle.isApp(uid)) { + if (!Process.isApplicationUid(uid)) { return false; } diff --git a/core/java/android/net/TransportInfo.java b/core/java/android/net/TransportInfo.java index b78d3feccfa0..aa4bbb051179 100644 --- a/core/java/android/net/TransportInfo.java +++ b/core/java/android/net/TransportInfo.java @@ -16,10 +16,48 @@ package android.net; +import android.annotation.NonNull; +import android.annotation.SystemApi; + /** * A container for transport-specific capabilities which is returned by * {@link NetworkCapabilities#getTransportInfo()}. Specific networks * may provide concrete implementations of this interface. + * @see android.net.wifi.aware.WifiAwareNetworkInfo + * @see android.net.wifi.WifiInfo */ public interface TransportInfo { + + /** + * Create a copy of a {@link TransportInfo} that will preserve location sensitive fields that + * were set based on the permissions of the process that originally received it. + * + * <p>By default {@link TransportInfo} does not preserve such fields during parceling, as + * they should not be shared outside of the process that receives them without appropriate + * checks. + * + * @param parcelLocationSensitiveFields Whether the location sensitive fields should be kept + * when parceling + * @return Copy of this instance. + * @hide + */ + @SystemApi + @NonNull + default TransportInfo makeCopy(boolean parcelLocationSensitiveFields) { + return this; + } + + /** + * Returns whether this TransportInfo type has location sensitive fields or not (helps + * to determine whether to perform a location permission check or not before sending to + * apps). + * + * @return {@code true} if this instance contains location sensitive info, {@code false} + * otherwise. + * @hide + */ + @SystemApi + default boolean hasLocationSensitiveFields() { + return false; + } } diff --git a/core/java/android/net/metrics/IpConnectivityLog.java b/core/java/android/net/metrics/IpConnectivityLog.java index a008d855025a..58ea91573775 100644 --- a/core/java/android/net/metrics/IpConnectivityLog.java +++ b/core/java/android/net/metrics/IpConnectivityLog.java @@ -17,10 +17,13 @@ package android.net.metrics; import android.annotation.NonNull; +import android.annotation.Nullable; import android.annotation.SystemApi; import android.net.ConnectivityMetricsEvent; import android.net.IIpConnectivityMetrics; +import android.net.LinkProperties; import android.net.Network; +import android.net.NetworkCapabilities; import android.os.Parcelable; import android.os.RemoteException; import android.os.ServiceManager; @@ -66,6 +69,9 @@ public class IpConnectivityLog { final IIpConnectivityMetrics service = IIpConnectivityMetrics.Stub.asInterface(ServiceManager.getService(SERVICE_NAME)); if (service == null) { + if (DBG) { + Log.d(TAG, SERVICE_NAME + " service was not ready"); + } return false; } // Two threads racing here will write the same pointer because getService @@ -83,9 +89,6 @@ public class IpConnectivityLog { */ public boolean log(@NonNull ConnectivityMetricsEvent ev) { if (!checkLoggerService()) { - if (DBG) { - Log.d(TAG, SERVICE_NAME + " service was not ready"); - } return false; } if (ev.timestamp == 0) { @@ -161,6 +164,56 @@ public class IpConnectivityLog { return log(makeEv(data)); } + /** + * Logs the validation status of the default network. + * @param valid whether the current default network was validated (i.e., whether it had + * {@link NetworkCapabilities.NET_CAPABILITY_VALIDATED} + * @return true if the event was successfully logged. + * @hide + */ + public boolean logDefaultNetworkValidity(boolean valid) { + if (!checkLoggerService()) { + return false; + } + try { + mService.logDefaultNetworkValidity(valid); + } catch (RemoteException ignored) { + // Only called within the system server. + } + return true; + } + + /** + * Logs a change in the default network. + * + * @param defaultNetwork the current default network + * @param score the current score of {@code defaultNetwork} + * @param lp the {@link LinkProperties} of {@code defaultNetwork} + * @param nc the {@link NetworkCapabilities} of the {@code defaultNetwork} + * @param validated whether {@code defaultNetwork} network is validated + * @param previousDefaultNetwork the previous default network + * @param previousScore the score of {@code previousDefaultNetwork} + * @param previousLp the {@link LinkProperties} of {@code previousDefaultNetwork} + * @param previousNc the {@link NetworkCapabilities} of {@code previousDefaultNetwork} + * @return true if the event was successfully logged. + * @hide + */ + public boolean logDefaultNetworkEvent(@Nullable Network defaultNetwork, int score, + boolean validated, @Nullable LinkProperties lp, @Nullable NetworkCapabilities nc, + @Nullable Network previousDefaultNetwork, int previousScore, + @Nullable LinkProperties previousLp, @Nullable NetworkCapabilities previousNc) { + if (!checkLoggerService()) { + return false; + } + try { + mService.logDefaultNetworkEvent(defaultNetwork, score, validated, lp, nc, + previousDefaultNetwork, previousScore, previousLp, previousNc); + } catch (RemoteException ignored) { + // Only called within the system server. + } + return true; + } + private static ConnectivityMetricsEvent makeEv(Event data) { ConnectivityMetricsEvent ev = new ConnectivityMetricsEvent(); ev.data = data; diff --git a/core/java/android/os/DropBoxManager.java b/core/java/android/os/DropBoxManager.java index 3dce130f5d61..575fc4c8931f 100644 --- a/core/java/android/os/DropBoxManager.java +++ b/core/java/android/os/DropBoxManager.java @@ -19,6 +19,10 @@ package android.os; import static android.Manifest.permission.PACKAGE_USAGE_STATS; import static android.Manifest.permission.READ_LOGS; +import android.annotation.BytesLong; +import android.annotation.CurrentTimeMillisLong; +import android.annotation.IntDef; +import android.annotation.NonNull; import android.annotation.Nullable; import android.annotation.RequiresPermission; import android.annotation.SdkConstant; @@ -35,6 +39,9 @@ import java.io.Closeable; import java.io.File; import java.io.IOException; import java.io.InputStream; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.nio.charset.StandardCharsets; import java.util.zip.GZIPInputStream; /** @@ -54,6 +61,11 @@ public class DropBoxManager { @UnsupportedAppUsage private final IDropBoxManagerService mService; + /** @hide */ + @IntDef(flag = true, prefix = { "IS_" }, value = { IS_EMPTY, IS_TEXT, IS_GZIPPED }) + @Retention(RetentionPolicy.SOURCE) + public @interface Flags {} + /** Flag value: Entry's content was deleted to save space. */ public static final int IS_EMPTY = 1; @@ -105,15 +117,15 @@ public class DropBoxManager { * {@link #close()} when you are done using it. */ public static class Entry implements Parcelable, Closeable { - private final String mTag; - private final long mTimeMillis; + private final @NonNull String mTag; + private final @CurrentTimeMillisLong long mTimeMillis; - private final byte[] mData; - private final ParcelFileDescriptor mFileDescriptor; - private final int mFlags; + private final @Nullable byte[] mData; + private final @Nullable ParcelFileDescriptor mFileDescriptor; + private final @Flags int mFlags; /** Create a new empty Entry with no contents. */ - public Entry(String tag, long millis) { + public Entry(@NonNull String tag, @CurrentTimeMillisLong long millis) { if (tag == null) throw new NullPointerException("tag == null"); mTag = tag; @@ -124,13 +136,14 @@ public class DropBoxManager { } /** Create a new Entry with plain text contents. */ - public Entry(String tag, long millis, String text) { + public Entry(@NonNull String tag, @CurrentTimeMillisLong long millis, + @NonNull String text) { if (tag == null) throw new NullPointerException("tag == null"); if (text == null) throw new NullPointerException("text == null"); mTag = tag; mTimeMillis = millis; - mData = text.getBytes(); + mData = text.getBytes(StandardCharsets.UTF_8); mFileDescriptor = null; mFlags = IS_TEXT; } @@ -139,7 +152,8 @@ public class DropBoxManager { * Create a new Entry with byte array contents. * The data array must not be modified after creating this entry. */ - public Entry(String tag, long millis, byte[] data, int flags) { + public Entry(@NonNull String tag, @CurrentTimeMillisLong long millis, + @Nullable byte[] data, @Flags int flags) { if (tag == null) throw new NullPointerException("tag == null"); if (((flags & IS_EMPTY) != 0) != (data == null)) { throw new IllegalArgumentException("Bad flags: " + flags); @@ -156,7 +170,8 @@ public class DropBoxManager { * Create a new Entry with streaming data contents. * Takes ownership of the ParcelFileDescriptor. */ - public Entry(String tag, long millis, ParcelFileDescriptor data, int flags) { + public Entry(@NonNull String tag, @CurrentTimeMillisLong long millis, + @Nullable ParcelFileDescriptor data, @Flags int flags) { if (tag == null) throw new NullPointerException("tag == null"); if (((flags & IS_EMPTY) != 0) != (data == null)) { throw new IllegalArgumentException("Bad flags: " + flags); @@ -173,7 +188,8 @@ public class DropBoxManager { * Create a new Entry with the contents read from a file. * The file will be read when the entry's contents are requested. */ - public Entry(String tag, long millis, File data, int flags) throws IOException { + public Entry(@NonNull String tag, @CurrentTimeMillisLong long millis, + @NonNull File data, @Flags int flags) throws IOException { if (tag == null) throw new NullPointerException("tag == null"); if ((flags & IS_EMPTY) != 0) throw new IllegalArgumentException("Bad flags: " + flags); @@ -190,19 +206,26 @@ public class DropBoxManager { } /** @return the tag originally attached to the entry. */ - public String getTag() { return mTag; } + public @NonNull String getTag() { + return mTag; + } /** @return time when the entry was originally created. */ - public long getTimeMillis() { return mTimeMillis; } + public @CurrentTimeMillisLong long getTimeMillis() { + return mTimeMillis; + } /** @return flags describing the content returned by {@link #getInputStream()}. */ - public int getFlags() { return mFlags & ~IS_GZIPPED; } // getInputStream() decompresses. + public @Flags int getFlags() { + // getInputStream() decompresses. + return mFlags & ~IS_GZIPPED; + } /** * @param maxBytes of string to return (will truncate at this length). * @return the uncompressed text contents of the entry, null if the entry is not text. */ - public String getText(int maxBytes) { + public @Nullable String getText(@BytesLong int maxBytes) { if ((mFlags & IS_TEXT) == 0) return null; if (mData != null) return new String(mData, 0, Math.min(maxBytes, mData.length)); @@ -225,7 +248,7 @@ public class DropBoxManager { } /** @return the uncompressed contents of the entry, or null if the contents were lost */ - public InputStream getInputStream() throws IOException { + public @Nullable InputStream getInputStream() throws IOException { InputStream is; if (mData != null) { is = new ByteArrayInputStream(mData); @@ -293,17 +316,8 @@ public class DropBoxManager { * @param tag describing the type of entry being stored * @param data value to store */ - public void addText(String tag, String data) { - try { - mService.add(new Entry(tag, 0, data)); - } catch (RemoteException e) { - if (e instanceof TransactionTooLargeException - && mContext.getApplicationInfo().targetSdkVersion < Build.VERSION_CODES.N) { - Log.e(TAG, "App sent too much data, so it was ignored", e); - return; - } - throw e.rethrowFromSystemServer(); - } + public void addText(@NonNull String tag, @NonNull String data) { + addData(tag, data.getBytes(StandardCharsets.UTF_8), IS_TEXT); } /** @@ -313,10 +327,10 @@ public class DropBoxManager { * @param data value to store * @param flags describing the data */ - public void addData(String tag, byte[] data, int flags) { + public void addData(@NonNull String tag, @Nullable byte[] data, @Flags int flags) { if (data == null) throw new NullPointerException("data == null"); try { - mService.add(new Entry(tag, 0, data, flags)); + mService.addData(tag, data, flags); } catch (RemoteException e) { if (e instanceof TransactionTooLargeException && mContext.getApplicationInfo().targetSdkVersion < Build.VERSION_CODES.N) { @@ -336,15 +350,14 @@ public class DropBoxManager { * @param flags describing the data * @throws IOException if the file can't be opened */ - public void addFile(String tag, File file, int flags) throws IOException { + public void addFile(@NonNull String tag, @NonNull File file, @Flags int flags) + throws IOException { if (file == null) throw new NullPointerException("file == null"); - Entry entry = new Entry(tag, 0, file, flags); - try { - mService.add(entry); + try (ParcelFileDescriptor pfd = ParcelFileDescriptor.open(file, + ParcelFileDescriptor.MODE_READ_ONLY)) { + mService.addFile(tag, pfd, flags); } catch (RemoteException e) { throw e.rethrowFromSystemServer(); - } finally { - entry.close(); } } diff --git a/core/java/android/os/OWNERS b/core/java/android/os/OWNERS index e3ad4e665a67..2559a33c1ab2 100644 --- a/core/java/android/os/OWNERS +++ b/core/java/android/os/OWNERS @@ -23,6 +23,10 @@ per-file BatteryStats* = file:/BATTERY_STATS_OWNERS per-file BatteryUsageStats* = file:/BATTERY_STATS_OWNERS per-file PowerComponents.java = file:/BATTERY_STATS_OWNERS +# Multiuser +per-file IUser* = file:/MULTIUSER_OWNERS +per-file User* = file:/MULTIUSER_OWNERS + # Binder per-file BadParcelableException.java = file:platform/frameworks/native:/libs/binder/OWNERS per-file Binder.java = file:platform/frameworks/native:/libs/binder/OWNERS diff --git a/core/java/android/os/Process.java b/core/java/android/os/Process.java index 8048b9df6097..39e3e146f45b 100644 --- a/core/java/android/os/Process.java +++ b/core/java/android/os/Process.java @@ -31,9 +31,12 @@ import dalvik.system.VMRuntime; import libcore.io.IoUtils; +import java.io.BufferedReader; import java.io.FileDescriptor; +import java.io.FileReader; import java.io.IOException; import java.util.Map; +import java.util.StringTokenizer; import java.util.concurrent.TimeoutException; /** @@ -1430,4 +1433,38 @@ public class Process { } private static native int nativePidFdOpen(int pid, int flags) throws ErrnoException; + + /** + * Checks if a process corresponding to a specific pid owns any file locks. + * @param pid The process ID for which we want to know the existence of file locks. + * @return true If the process holds any file locks, false otherwise. + * @throws IOException if /proc/locks can't be accessed. + * + * @hide + */ + public static boolean hasFileLocks(int pid) throws IOException { + BufferedReader br = null; + + try { + br = new BufferedReader(new FileReader("/proc/locks")); + String line; + + while ((line = br.readLine()) != null) { + StringTokenizer st = new StringTokenizer(line); + + for (int i = 0; i < 5 && st.hasMoreTokens(); i++) { + String str = st.nextToken(); + if (i == 4 && Integer.parseInt(str) == pid) { + return true; + } + } + } + + return false; + } finally { + if (br != null) { + br.close(); + } + } + } } diff --git a/core/java/android/os/image/DynamicSystemClient.java b/core/java/android/os/image/DynamicSystemClient.java index 0fe889410685..5aa4e27fc2f3 100644 --- a/core/java/android/os/image/DynamicSystemClient.java +++ b/core/java/android/os/image/DynamicSystemClient.java @@ -35,8 +35,6 @@ import android.os.Message; import android.os.Messenger; import android.os.ParcelableException; import android.os.RemoteException; -import android.os.SystemProperties; -import android.util.FeatureFlagUtils; import android.util.Slog; import java.lang.annotation.Retention; @@ -251,13 +249,7 @@ public class DynamicSystemClient { mService.send(msg); } catch (RemoteException e) { Slog.e(TAG, "Unable to get status from installation service"); - if (mExecutor != null) { - mExecutor.execute(() -> { - mListener.onStatusChanged(STATUS_UNKNOWN, CAUSE_ERROR_IPC, 0, e); - }); - } else { - mListener.onStatusChanged(STATUS_UNKNOWN, CAUSE_ERROR_IPC, 0, e); - } + notifyOnStatusChangedListener(STATUS_UNKNOWN, CAUSE_ERROR_IPC, 0, e); } } @@ -311,6 +303,20 @@ public class DynamicSystemClient { mExecutor = null; } + private void notifyOnStatusChangedListener( + int status, int cause, long progress, Throwable detail) { + if (mListener != null) { + if (mExecutor != null) { + mExecutor.execute( + () -> { + mListener.onStatusChanged(status, cause, progress, detail); + }); + } else { + mListener.onStatusChanged(status, cause, progress, detail); + } + } + } + /** * Bind to {@code DynamicSystem} installation service. Binding to the installation service * allows it to send status updates to {@link #OnStatusChangedListener}. It is recommanded @@ -320,11 +326,6 @@ public class DynamicSystemClient { @RequiresPermission(android.Manifest.permission.INSTALL_DYNAMIC_SYSTEM) @SystemApi public void bind() { - if (!featureFlagEnabled()) { - Slog.w(TAG, FeatureFlagUtils.DYNAMIC_SYSTEM + " not enabled; bind() aborted."); - return; - } - Intent intent = new Intent(); intent.setClassName("com.android.dynsystem", "com.android.dynsystem.DynamicSystemInstallationService"); @@ -395,11 +396,6 @@ public class DynamicSystemClient { @RequiresPermission(android.Manifest.permission.INSTALL_DYNAMIC_SYSTEM) public void start(@NonNull Uri systemUrl, @BytesLong long systemSize, @BytesLong long userdataSize) { - if (!featureFlagEnabled()) { - Slog.w(TAG, FeatureFlagUtils.DYNAMIC_SYSTEM + " not enabled; start() aborted."); - return; - } - Intent intent = new Intent(); intent.setClassName("com.android.dynsystem", @@ -407,6 +403,7 @@ public class DynamicSystemClient { intent.setData(systemUrl); intent.setAction(ACTION_START_INSTALL); + intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK); intent.putExtra(KEY_SYSTEM_SIZE, systemSize); intent.putExtra(KEY_USERDATA_SIZE, userdataSize); @@ -414,11 +411,6 @@ public class DynamicSystemClient { mContext.startActivity(intent); } - private boolean featureFlagEnabled() { - return SystemProperties.getBoolean( - FeatureFlagUtils.PERSIST_PREFIX + FeatureFlagUtils.DYNAMIC_SYSTEM, false); - } - private void handleMessage(Message msg) { switch (msg.what) { case MSG_POST_STATUS: @@ -432,13 +424,7 @@ public class DynamicSystemClient { Throwable detail = t == null ? null : t.getCause(); - if (mExecutor != null) { - mExecutor.execute(() -> { - mListener.onStatusChanged(status, cause, progress, detail); - }); - } else { - mListener.onStatusChanged(status, cause, progress, detail); - } + notifyOnStatusChangedListener(status, cause, progress, detail); break; default: // do nothing diff --git a/core/java/android/os/incremental/IIncrementalService.aidl b/core/java/android/os/incremental/IIncrementalService.aidl index ca92ad5deae6..7db5a80b6cf9 100644 --- a/core/java/android/os/incremental/IIncrementalService.aidl +++ b/core/java/android/os/incremental/IIncrementalService.aidl @@ -21,6 +21,7 @@ import android.content.pm.IDataLoaderStatusListener; import android.os.incremental.IncrementalNewFileParams; import android.os.incremental.IStorageLoadingProgressListener; import android.os.incremental.IStorageHealthListener; +import android.os.incremental.PerUidReadTimeouts; import android.os.incremental.StorageHealthCheckParams; /** @hide */ @@ -40,7 +41,8 @@ interface IIncrementalService { int createStorage(in @utf8InCpp String path, in DataLoaderParamsParcel params, int createMode, in IDataLoaderStatusListener statusListener, in StorageHealthCheckParams healthCheckParams, - in IStorageHealthListener healthListener); + in IStorageHealthListener healthListener, + in PerUidReadTimeouts[] perUidReadTimeouts); int createLinkedStorage(in @utf8InCpp String path, int otherStorageId, int createMode); /** @@ -123,7 +125,7 @@ interface IIncrementalService { /** * Permanently disable readlogs reporting for a storage given its ID. */ - void disableReadLogs(int storageId); + void disallowReadLogs(int storageId); /** * Setting up native library directories and extract native libs onto a storage if needed. diff --git a/core/java/android/os/incremental/IncrementalFileStorages.java b/core/java/android/os/incremental/IncrementalFileStorages.java index 284c2df2ee7b..59292baa110c 100644 --- a/core/java/android/os/incremental/IncrementalFileStorages.java +++ b/core/java/android/os/incremental/IncrementalFileStorages.java @@ -69,7 +69,8 @@ public final class IncrementalFileStorages { @Nullable IDataLoaderStatusListener statusListener, @Nullable StorageHealthCheckParams healthCheckParams, @Nullable IStorageHealthListener healthListener, - List<InstallationFileParcel> addedFiles) throws IOException { + @NonNull List<InstallationFileParcel> addedFiles, + @NonNull PerUidReadTimeouts[] perUidReadTimeouts) throws IOException { // TODO(b/136132412): validity check if session should not be incremental IncrementalManager incrementalManager = (IncrementalManager) context.getSystemService( Context.INCREMENTAL_SERVICE); @@ -80,7 +81,7 @@ public final class IncrementalFileStorages { final IncrementalFileStorages result = new IncrementalFileStorages(stageDir, incrementalManager, dataLoaderParams, statusListener, healthCheckParams, - healthListener); + healthListener, perUidReadTimeouts); for (InstallationFileParcel file : addedFiles) { if (file.location == LOCATION_DATA_APP) { try { @@ -105,7 +106,8 @@ public final class IncrementalFileStorages { @NonNull DataLoaderParams dataLoaderParams, @Nullable IDataLoaderStatusListener statusListener, @Nullable StorageHealthCheckParams healthCheckParams, - @Nullable IStorageHealthListener healthListener) throws IOException { + @Nullable IStorageHealthListener healthListener, + @NonNull PerUidReadTimeouts[] perUidReadTimeouts) throws IOException { try { mStageDir = stageDir; mIncrementalManager = incrementalManager; @@ -124,7 +126,7 @@ public final class IncrementalFileStorages { mDefaultStorage = mIncrementalManager.createStorage(stageDir.getAbsolutePath(), dataLoaderParams, IncrementalManager.CREATE_MODE_CREATE | IncrementalManager.CREATE_MODE_TEMPORARY_BIND, false, - statusListener, healthCheckParams, healthListener); + statusListener, healthCheckParams, healthListener, perUidReadTimeouts); if (mDefaultStorage == null) { throw new IOException( "Couldn't create incremental storage at " + stageDir); @@ -163,8 +165,8 @@ public final class IncrementalFileStorages { /** * Permanently disables readlogs. */ - public void disableReadLogs() { - mDefaultStorage.disableReadLogs(); + public void disallowReadLogs() { + mDefaultStorage.disallowReadLogs(); } /** diff --git a/core/java/android/os/incremental/IncrementalManager.java b/core/java/android/os/incremental/IncrementalManager.java index fb47ef04b231..4b9327021cb0 100644 --- a/core/java/android/os/incremental/IncrementalManager.java +++ b/core/java/android/os/incremental/IncrementalManager.java @@ -40,6 +40,7 @@ import java.nio.file.Path; import java.nio.file.Paths; import java.nio.file.SimpleFileVisitor; import java.nio.file.attribute.BasicFileAttributes; +import java.util.Objects; /** * Provides operations to open or create an IncrementalStorage, using IIncrementalService @@ -104,10 +105,14 @@ public final class IncrementalManager { boolean autoStartDataLoader, @Nullable IDataLoaderStatusListener statusListener, @Nullable StorageHealthCheckParams healthCheckParams, - @Nullable IStorageHealthListener healthListener) { + @Nullable IStorageHealthListener healthListener, + @NonNull PerUidReadTimeouts[] perUidReadTimeouts) { + Objects.requireNonNull(path); + Objects.requireNonNull(params); + Objects.requireNonNull(perUidReadTimeouts); try { final int id = mService.createStorage(path, params.getData(), createMode, - statusListener, healthCheckParams, healthListener); + statusListener, healthCheckParams, healthListener, perUidReadTimeouts); if (id < 0) { return null; } diff --git a/core/java/android/os/incremental/IncrementalStorage.java b/core/java/android/os/incremental/IncrementalStorage.java index b913faf9cc83..5b688bbd0655 100644 --- a/core/java/android/os/incremental/IncrementalStorage.java +++ b/core/java/android/os/incremental/IncrementalStorage.java @@ -432,9 +432,9 @@ public final class IncrementalStorage { /** * Permanently disable readlogs collection. */ - public void disableReadLogs() { + public void disallowReadLogs() { try { - mService.disableReadLogs(mId); + mService.disallowReadLogs(mId); } catch (RemoteException e) { e.rethrowFromSystemServer(); } diff --git a/core/java/android/os/incremental/PerUidReadTimeouts.aidl b/core/java/android/os/incremental/PerUidReadTimeouts.aidl new file mode 100644 index 000000000000..84f30a6c3aaf --- /dev/null +++ b/core/java/android/os/incremental/PerUidReadTimeouts.aidl @@ -0,0 +1,45 @@ +/* + * Copyright (C) 2020 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.os.incremental; + +/** + * Max value is ~1hr = 3600s = 3600000ms = 3600000000us + * @hide + */ +parcelable PerUidReadTimeouts { + /** UID to apply these timeouts to */ + int uid; + + /** + * Min time to read any block. Note that this doesn't apply to reads + * which are satisfied from the page cache. + */ + long minTimeUs; + + /** + * Min time to satisfy a pending read. Must be >= min_time_us. Any + * pending read which is filled before this time will be delayed so + * that the total read time >= this value. + */ + long minPendingTimeUs; + + /** + * Max time to satisfy a pending read before the read times out. + * Must be >= min_pending_time_us + */ + long maxPendingTimeUs; +} diff --git a/core/java/android/permission/PermissionControllerManager.java b/core/java/android/permission/PermissionControllerManager.java index 0ba09fdab808..f306805ac3a4 100644 --- a/core/java/android/permission/PermissionControllerManager.java +++ b/core/java/android/permission/PermissionControllerManager.java @@ -663,6 +663,34 @@ public final class PermissionControllerManager { } /** + * Gets the description of the privileges associated with the given device profiles + * + * @param profileName Name of the device profile + * @param executor Executor on which to invoke the callback + * @param callback Callback to receive the result + * + * @hide + */ + @RequiresPermission(Manifest.permission.MANAGE_COMPANION_DEVICES) + public void getPrivilegesDescriptionStringForProfile( + @NonNull String profileName, + @NonNull @CallbackExecutor Executor executor, + @NonNull Consumer<String> callback) { + mRemoteService.postAsync(service -> { + AndroidFuture<String> future = new AndroidFuture<>(); + service.getPrivilegesDescriptionStringForProfile(profileName, future); + return future; + }).whenCompleteAsync((description, err) -> { + if (err != null) { + Log.e(TAG, "Error from getPrivilegesDescriptionStringForProfile", err); + callback.accept(null); + } else { + callback.accept(description); + } + }, executor); + } + + /** * @see PermissionControllerManager#updateUserSensitiveForApp * @hide */ diff --git a/core/java/android/permission/PermissionControllerService.java b/core/java/android/permission/PermissionControllerService.java index 8441fea1095a..8105b6517015 100644 --- a/core/java/android/permission/PermissionControllerService.java +++ b/core/java/android/permission/PermissionControllerService.java @@ -540,12 +540,13 @@ public abstract class PermissionControllerService extends Service { public void getPrivilegesDescriptionStringForProfile( @NonNull String deviceProfileName, @NonNull AndroidFuture<String> callback) { - checkStringNotEmpty(deviceProfileName); - Objects.requireNonNull(callback); + try { + checkStringNotEmpty(deviceProfileName); + Objects.requireNonNull(callback); - enforceSomePermissionsGrantedToCaller(Manifest.permission.MANAGE_COMPANION_DEVICES); + enforceSomePermissionsGrantedToCaller( + Manifest.permission.MANAGE_COMPANION_DEVICES); - try { callback.complete(PermissionControllerService .this .getPrivilegesDescriptionStringForProfile(deviceProfileName)); diff --git a/core/java/android/provider/CallLog.java b/core/java/android/provider/CallLog.java index d1aa48914e70..0bdd574d77dd 100644 --- a/core/java/android/provider/CallLog.java +++ b/core/java/android/provider/CallLog.java @@ -54,6 +54,7 @@ import android.telephony.PhoneNumberUtils; import android.text.TextUtils; import android.util.Log; +import java.io.ByteArrayOutputStream; import java.io.FileNotFoundException; import java.io.FileOutputStream; import java.io.IOException; @@ -96,6 +97,10 @@ public class CallLog { */ public static final String SHADOW_AUTHORITY = "call_log_shadow"; + /** @hide */ + public static final Uri SHADOW_CALL_COMPOSER_PICTURE_URI = CALL_COMPOSER_PICTURE_URI.buildUpon() + .authority(SHADOW_AUTHORITY).build(); + /** * Describes an error encountered while storing a call composer picture in the call log. * @hide @@ -143,7 +148,6 @@ public class CallLog { private final int mErrorCode; - /** @hide */ public CallComposerLoggingException(@CallComposerLoggingError int errorCode) { mErrorCode = errorCode; } @@ -154,6 +158,29 @@ public class CallLog { public @CallComposerLoggingError int getErrorCode() { return mErrorCode; } + + @Override + public String toString() { + String errorString; + switch (mErrorCode) { + case ERROR_UNKNOWN: + errorString = "UNKNOWN"; + break; + case ERROR_REMOTE_END_CLOSED: + errorString = "REMOTE_END_CLOSED"; + break; + case ERROR_STORAGE_FULL: + errorString = "STORAGE_FULL"; + break; + case ERROR_INPUT_CLOSED: + errorString = "INPUT_CLOSED"; + break; + default: + errorString = "[[" + mErrorCode + "]]"; + break; + } + return "CallComposerLoggingException: " + errorString; + } } /** @@ -179,7 +206,10 @@ public class CallLog { * @hide */ @SystemApi - @RequiresPermission(Manifest.permission.WRITE_CALL_LOG) + @RequiresPermission(allOf = { + Manifest.permission.WRITE_CALL_LOG, + Manifest.permission.INTERACT_ACROSS_USERS + }) public static void storeCallComposerPictureAsUser(@NonNull Context context, @Nullable UserHandle user, @NonNull InputStream input, @@ -191,69 +221,164 @@ public class CallLog { Objects.requireNonNull(callback); executor.execute(() -> { - Uri pictureFileUri; - Uri pictureInsertionUri = context.getSystemService(UserManager.class) - .isUserUnlocked() ? CALL_COMPOSER_PICTURE_URI - : CALL_COMPOSER_PICTURE_URI.buildUpon().authority(SHADOW_AUTHORITY).build(); - try { - // ContentResolver#insert says that the second argument is nullable. It is in fact - // not nullable. - ContentValues cv = new ContentValues(); - pictureFileUri = context.getContentResolver().insert(pictureInsertionUri, cv); - } catch (ParcelableException e) { - // Most likely an IOException. We don't have a good way of distinguishing them so - // just return an unknown error. - sendCallComposerError(callback, CallComposerLoggingException.ERROR_UNKNOWN); + ByteArrayOutputStream tmpOut = new ByteArrayOutputStream(); + + // Read the entire input into memory first in case we have to write multiple times and + // the input isn't resettable. + byte[] buffer = new byte[1024]; + int bytesRead; + while (true) { + try { + bytesRead = input.read(buffer); + } catch (IOException e) { + Log.e(LOG_TAG, "IOException while reading call composer pic from input: " + + e); + callback.onError(new CallComposerLoggingException( + CallComposerLoggingException.ERROR_INPUT_CLOSED)); + return; + } + if (bytesRead < 0) { + break; + } + tmpOut.write(buffer, 0, bytesRead); + } + byte[] picData = tmpOut.toByteArray(); + + UserManager userManager = context.getSystemService(UserManager.class); + // Nasty casework for the shadow calllog begins... + // First see if we're just inserting for one user. If so, insert into the shadow + // based on whether that user is unlocked. + if (user != null) { + Uri baseUri = userManager.isUserUnlocked(user) ? CALL_COMPOSER_PICTURE_URI + : SHADOW_CALL_COMPOSER_PICTURE_URI; + Uri pictureInsertionUri = ContentProvider.maybeAddUserId(baseUri, + user.getIdentifier()); + Log.i(LOG_TAG, "Inserting call composer for single user at " + + pictureInsertionUri); + + try { + Uri result = storeCallComposerPictureAtUri( + context, pictureInsertionUri, false, picData); + callback.onResult(result); + } catch (CallComposerLoggingException e) { + callback.onError(e); + } return; } - if (pictureFileUri == null) { - // If the call log provider returns null, it means that there's not enough space - // left to store the maximum-sized call composer image. - sendCallComposerError(callback, CallComposerLoggingException.ERROR_STORAGE_FULL); + + // Next, see if the system user is locked. If so, only insert to the system shadow + if (!userManager.isUserUnlocked(UserHandle.SYSTEM)) { + Uri pictureInsertionUri = ContentProvider.maybeAddUserId( + SHADOW_CALL_COMPOSER_PICTURE_URI, + UserHandle.SYSTEM.getIdentifier()); + Log.i(LOG_TAG, "Inserting call composer for all users, but system locked at " + + pictureInsertionUri); + try { + Uri result = + storeCallComposerPictureAtUri(context, pictureInsertionUri, + true, picData); + callback.onResult(result); + } catch (CallComposerLoggingException e) { + callback.onError(e); + } return; } - boolean wroteSuccessfully = false; - try (ParcelFileDescriptor pfd = - context.getContentResolver().openFileDescriptor(pictureFileUri, "w")) { - FileOutputStream output = new FileOutputStream(pfd.getFileDescriptor()); - byte[] buffer = new byte[1024]; - int bytesRead; - while (true) { - try { - bytesRead = input.read(buffer); - } catch (IOException e) { - sendCallComposerError(callback, - CallComposerLoggingException.ERROR_INPUT_CLOSED); - throw e; - } - if (bytesRead < 0) { - break; - } + // If we're inserting to all users and the system user is unlocked, then insert to all + // running users. Non running/still locked users will copy from the system when they + // start. + // First, insert to the system calllog to get the basename to use for the rest of the + // users. + Uri systemPictureInsertionUri = ContentProvider.maybeAddUserId( + CALL_COMPOSER_PICTURE_URI, + UserHandle.SYSTEM.getIdentifier()); + Uri systemInsertedPicture; + try { + systemInsertedPicture = + storeCallComposerPictureAtUri(context, systemPictureInsertionUri, + true, picData); + Log.i(LOG_TAG, "Inserting call composer for all users, succeeded with system," + + " result is " + systemInsertedPicture); + } catch (CallComposerLoggingException e) { + callback.onError(e); + return; + } + + // Next, insert into all users that have call log access AND are running AND are + // decrypted. + Uri strippedInsertionUri = ContentProvider.getUriWithoutUserId(systemInsertedPicture); + for (UserInfo u : userManager.getAliveUsers()) { + UserHandle userHandle = u.getUserHandle(); + if (userHandle.isSystem()) { + // Already written. + continue; + } + + if (!Calls.shouldHaveSharedCallLogEntries( + context, userManager, userHandle.getIdentifier())) { + // Shouldn't have calllog entries. + continue; + } + + if (userManager.isUserRunning(userHandle) + && userManager.isUserUnlocked(userHandle)) { + Uri insertionUri = ContentProvider.maybeAddUserId(strippedInsertionUri, + userHandle.getIdentifier()); + Log.i(LOG_TAG, "Inserting call composer for all users, now on user " + + userHandle + " inserting at " + insertionUri); try { - output.write(buffer, 0, bytesRead); - } catch (IOException e) { - sendCallComposerError(callback, - CallComposerLoggingException.ERROR_REMOTE_END_CLOSED); - throw e; + storeCallComposerPictureAtUri(context, insertionUri, false, picData); + } catch (CallComposerLoggingException e) { + Log.e(LOG_TAG, "Error writing for user " + userHandle.getIdentifier() + + ": " + e); + // If one or more users failed but the system user succeeded, don't return + // an error -- the image is still around somewhere, and we'll be able to + // find it in the system user's call log if needed. } } - wroteSuccessfully = true; - } catch (FileNotFoundException e) { - callback.onError(new CallComposerLoggingException( - CallComposerLoggingException.ERROR_UNKNOWN)); - } catch (IOException e) { - Log.e(LOG_TAG, "IOException while writing call composer pic to call log: " - + e); } + callback.onResult(strippedInsertionUri); + }); + } - if (wroteSuccessfully) { - callback.onResult(pictureFileUri); - } else { + private static Uri storeCallComposerPictureAtUri( + Context context, Uri insertionUri, + boolean forAllUsers, byte[] picData) throws CallComposerLoggingException { + Uri pictureFileUri; + try { + ContentValues cv = new ContentValues(); + cv.put(Calls.ADD_FOR_ALL_USERS, forAllUsers ? 1 : 0); + pictureFileUri = context.getContentResolver().insert(insertionUri, cv); + } catch (ParcelableException e) { + // Most likely an IOException. We don't have a good way of distinguishing them so + // just return an unknown error. + throw new CallComposerLoggingException(CallComposerLoggingException.ERROR_UNKNOWN); + } + if (pictureFileUri == null) { + // If the call log provider returns null, it means that there's not enough space + // left to store the maximum-sized call composer image. + throw new CallComposerLoggingException(CallComposerLoggingException.ERROR_STORAGE_FULL); + } + + try (ParcelFileDescriptor pfd = + context.getContentResolver().openFileDescriptor(pictureFileUri, "w")) { + FileOutputStream output = new FileOutputStream(pfd.getFileDescriptor()); + try { + output.write(picData); + } catch (IOException e) { + Log.e(LOG_TAG, "Got IOException writing to remote end: " + e); // Clean up our mess if we didn't successfully write the file. context.getContentResolver().delete(pictureFileUri, null); + throw new CallComposerLoggingException( + CallComposerLoggingException.ERROR_REMOTE_END_CLOSED); } - }); + } catch (FileNotFoundException e) { + throw new CallComposerLoggingException(CallComposerLoggingException.ERROR_UNKNOWN); + } catch (IOException e) { + // Ignore, this is only thrown upon closing. + Log.e(LOG_TAG, "Got IOException closing remote descriptor: " + e); + } + return pictureFileUri; } // Only call on the correct executor. diff --git a/core/java/android/service/translation/ITranslationCallback.aidl b/core/java/android/service/translation/ITranslationCallback.aidl new file mode 100644 index 000000000000..333cb577f790 --- /dev/null +++ b/core/java/android/service/translation/ITranslationCallback.aidl @@ -0,0 +1,29 @@ +/* + * Copyright (C) 2020 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.service.translation; + +import android.view.translation.TranslationResponse; + +/** + * Interface to receive the result of a {@code TranslationRequest}. + * + * @hide + */ +oneway interface ITranslationCallback { + void onTranslationComplete(in TranslationResponse translationResponse); + void onError(); +} diff --git a/core/java/android/service/translation/ITranslationService.aidl b/core/java/android/service/translation/ITranslationService.aidl new file mode 100644 index 000000000000..6d6f2782ef4b --- /dev/null +++ b/core/java/android/service/translation/ITranslationService.aidl @@ -0,0 +1,38 @@ +/* + * Copyright (C) 2020 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.service.translation; + +import android.service.translation.TranslationRequest; +import android.service.translation.ITranslationCallback; +import android.view.translation.TranslationSpec; +import com.android.internal.os.IResultReceiver; + +/** + * System-wide on-device translation service. + * + * <p>Services requests to translate text between different languages. The primary use case for this + * service is automatic translation of text and web views, when the auto Translate feature is + * enabled. + * + * @hide + */ +oneway interface ITranslationService { + void onConnected(); + void onDisconnected(); + void onCreateTranslationSession(in TranslationSpec sourceSpec, in TranslationSpec destSpec, + int sessionId, in IResultReceiver receiver); +} diff --git a/core/java/android/service/translation/OWNERS b/core/java/android/service/translation/OWNERS new file mode 100644 index 000000000000..a1e663aa8ff7 --- /dev/null +++ b/core/java/android/service/translation/OWNERS @@ -0,0 +1,8 @@ +# Bug component: 994311 + +adamhe@google.com +augale@google.com +joannechung@google.com +lpeter@google.com +svetoslavganov@google.com +tymtsai@google.com diff --git a/core/java/android/service/translation/OnTranslationResultCallbackWrapper.java b/core/java/android/service/translation/OnTranslationResultCallbackWrapper.java new file mode 100644 index 000000000000..345c69c0935d --- /dev/null +++ b/core/java/android/service/translation/OnTranslationResultCallbackWrapper.java @@ -0,0 +1,92 @@ +/* + * Copyright (C) 2020 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.service.translation; + +import android.annotation.NonNull; +import android.annotation.Nullable; +import android.os.DeadObjectException; +import android.os.RemoteException; +import android.util.Log; +import android.view.translation.TranslationResponse; + +import java.util.Objects; +import java.util.concurrent.atomic.AtomicBoolean; + +/** + * Callback to receive the {@link TranslationResponse} on successful translation. + * + * @hide + */ +final class OnTranslationResultCallbackWrapper implements + TranslationService.OnTranslationResultCallback { + + private static final String TAG = "OnTranslationResultCallback"; + + private final @NonNull ITranslationCallback mCallback; + + private AtomicBoolean mCalled; + + /** + * @hide + */ + public OnTranslationResultCallbackWrapper(@NonNull ITranslationCallback callback) { + mCallback = Objects.requireNonNull(callback); + mCalled = new AtomicBoolean(); + } + + @Override + public void onTranslationSuccess(@Nullable TranslationResponse response) { + assertNotCalled(); + if (mCalled.getAndSet(true)) { + throw new IllegalStateException("Already called"); + } + + try { + mCallback.onTranslationComplete(response); + } catch (RemoteException e) { + if (e instanceof DeadObjectException) { + Log.w(TAG, "Process is dead, ignore."); + return; + } + throw e.rethrowAsRuntimeException(); + } + } + + @Override + public void onError() { + assertNotCalled(); + if (mCalled.getAndSet(true)) { + throw new IllegalStateException("Already called"); + } + + try { + mCallback.onError(); + } catch (RemoteException e) { + if (e instanceof DeadObjectException) { + Log.w(TAG, "Process is dead, ignore."); + return; + } + throw e.rethrowAsRuntimeException(); + } + } + + private void assertNotCalled() { + if (mCalled.get()) { + throw new IllegalStateException("Already called"); + } + } +} diff --git a/core/java/android/service/translation/TranslationRequest.aidl b/core/java/android/service/translation/TranslationRequest.aidl new file mode 100644 index 000000000000..9a2d4157696e --- /dev/null +++ b/core/java/android/service/translation/TranslationRequest.aidl @@ -0,0 +1,19 @@ +/* + * Copyright (C) 2020 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.service.translation; + +parcelable TranslationRequest; diff --git a/core/java/android/service/translation/TranslationRequest.java b/core/java/android/service/translation/TranslationRequest.java new file mode 100644 index 000000000000..b8afd7049a82 --- /dev/null +++ b/core/java/android/service/translation/TranslationRequest.java @@ -0,0 +1,281 @@ +/* + * Copyright (C) 2020 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.service.translation; + +import android.annotation.NonNull; +import android.annotation.SystemApi; +import android.os.Parcel; +import android.os.Parcelable; +import android.view.translation.TranslationSpec; + +import com.android.internal.util.DataClass; + +import java.util.ArrayList; +import java.util.List; + +/** + * Internal translation request sent to the {@link android.service.translation.TranslationService} + * which contains the text to be translated. + * + * @hide + */ +@SystemApi +@DataClass(genConstructor = true, genBuilder = true, genToString = true) +public final class TranslationRequest implements Parcelable { + + private final int mRequestId; + @NonNull + private final TranslationSpec mSourceSpec; + @NonNull + private final TranslationSpec mDestSpec; + @NonNull + private final List<android.view.translation.TranslationRequest> mTranslationRequests; + + + + // Code below generated by codegen v1.0.22. + // + // DO NOT MODIFY! + // CHECKSTYLE:OFF Generated code + // + // To regenerate run: + // $ codegen $ANDROID_BUILD_TOP/frameworks/base/core/java/android/service/translation/TranslationRequest.java + // + // To exclude the generated code from IntelliJ auto-formatting enable (one-time): + // Settings > Editor > Code Style > Formatter Control + //@formatter:off + + + @DataClass.Generated.Member + public TranslationRequest( + int requestId, + @NonNull TranslationSpec sourceSpec, + @NonNull TranslationSpec destSpec, + @NonNull List<android.view.translation.TranslationRequest> translationRequests) { + this.mRequestId = requestId; + this.mSourceSpec = sourceSpec; + com.android.internal.util.AnnotationValidations.validate( + NonNull.class, null, mSourceSpec); + this.mDestSpec = destSpec; + com.android.internal.util.AnnotationValidations.validate( + NonNull.class, null, mDestSpec); + this.mTranslationRequests = translationRequests; + com.android.internal.util.AnnotationValidations.validate( + NonNull.class, null, mTranslationRequests); + + // onConstructed(); // You can define this method to get a callback + } + + @DataClass.Generated.Member + public int getRequestId() { + return mRequestId; + } + + @DataClass.Generated.Member + public @NonNull TranslationSpec getSourceSpec() { + return mSourceSpec; + } + + @DataClass.Generated.Member + public @NonNull TranslationSpec getDestSpec() { + return mDestSpec; + } + + @DataClass.Generated.Member + public @NonNull List<android.view.translation.TranslationRequest> getTranslationRequests() { + return mTranslationRequests; + } + + @Override + @DataClass.Generated.Member + public String toString() { + // You can override field toString logic by defining methods like: + // String fieldNameToString() { ... } + + return "TranslationRequest { " + + "requestId = " + mRequestId + ", " + + "sourceSpec = " + mSourceSpec + ", " + + "destSpec = " + mDestSpec + ", " + + "translationRequests = " + mTranslationRequests + + " }"; + } + + @Override + @DataClass.Generated.Member + public void writeToParcel(@NonNull Parcel dest, int flags) { + // You can override field parcelling by defining methods like: + // void parcelFieldName(Parcel dest, int flags) { ... } + + dest.writeInt(mRequestId); + dest.writeTypedObject(mSourceSpec, flags); + dest.writeTypedObject(mDestSpec, flags); + dest.writeParcelableList(mTranslationRequests, flags); + } + + @Override + @DataClass.Generated.Member + public int describeContents() { return 0; } + + /** @hide */ + @SuppressWarnings({"unchecked", "RedundantCast"}) + @DataClass.Generated.Member + /* package-private */ TranslationRequest(@NonNull Parcel in) { + // You can override field unparcelling by defining methods like: + // static FieldType unparcelFieldName(Parcel in) { ... } + + int requestId = in.readInt(); + TranslationSpec sourceSpec = (TranslationSpec) in.readTypedObject(TranslationSpec.CREATOR); + TranslationSpec destSpec = (TranslationSpec) in.readTypedObject(TranslationSpec.CREATOR); + List<android.view.translation.TranslationRequest> translationRequests = new ArrayList<>(); + in.readParcelableList(translationRequests, android.view.translation.TranslationRequest.class.getClassLoader()); + + this.mRequestId = requestId; + this.mSourceSpec = sourceSpec; + com.android.internal.util.AnnotationValidations.validate( + NonNull.class, null, mSourceSpec); + this.mDestSpec = destSpec; + com.android.internal.util.AnnotationValidations.validate( + NonNull.class, null, mDestSpec); + this.mTranslationRequests = translationRequests; + com.android.internal.util.AnnotationValidations.validate( + NonNull.class, null, mTranslationRequests); + + // onConstructed(); // You can define this method to get a callback + } + + @DataClass.Generated.Member + public static final @NonNull Parcelable.Creator<TranslationRequest> CREATOR + = new Parcelable.Creator<TranslationRequest>() { + @Override + public TranslationRequest[] newArray(int size) { + return new TranslationRequest[size]; + } + + @Override + public TranslationRequest createFromParcel(@NonNull Parcel in) { + return new TranslationRequest(in); + } + }; + + /** + * A builder for {@link TranslationRequest} + */ + @SuppressWarnings("WeakerAccess") + @DataClass.Generated.Member + public static final class Builder { + + private int mRequestId; + private @NonNull TranslationSpec mSourceSpec; + private @NonNull TranslationSpec mDestSpec; + private @NonNull List<android.view.translation.TranslationRequest> mTranslationRequests; + + private long mBuilderFieldsSet = 0L; + + public Builder( + int requestId, + @NonNull TranslationSpec sourceSpec, + @NonNull TranslationSpec destSpec, + @NonNull List<android.view.translation.TranslationRequest> translationRequests) { + mRequestId = requestId; + mSourceSpec = sourceSpec; + com.android.internal.util.AnnotationValidations.validate( + NonNull.class, null, mSourceSpec); + mDestSpec = destSpec; + com.android.internal.util.AnnotationValidations.validate( + NonNull.class, null, mDestSpec); + mTranslationRequests = translationRequests; + com.android.internal.util.AnnotationValidations.validate( + NonNull.class, null, mTranslationRequests); + } + + @DataClass.Generated.Member + public @NonNull Builder setRequestId(int value) { + checkNotUsed(); + mBuilderFieldsSet |= 0x1; + mRequestId = value; + return this; + } + + @DataClass.Generated.Member + public @NonNull Builder setSourceSpec(@NonNull TranslationSpec value) { + checkNotUsed(); + mBuilderFieldsSet |= 0x2; + mSourceSpec = value; + return this; + } + + @DataClass.Generated.Member + public @NonNull Builder setDestSpec(@NonNull TranslationSpec value) { + checkNotUsed(); + mBuilderFieldsSet |= 0x4; + mDestSpec = value; + return this; + } + + @DataClass.Generated.Member + public @NonNull Builder setTranslationRequests(@NonNull List<android.view.translation.TranslationRequest> value) { + checkNotUsed(); + mBuilderFieldsSet |= 0x8; + mTranslationRequests = value; + return this; + } + + /** @see #setTranslationRequests */ + @DataClass.Generated.Member + public @NonNull Builder addTranslationRequests(@NonNull android.view.translation.TranslationRequest value) { + // You can refine this method's name by providing item's singular name, e.g.: + // @DataClass.PluralOf("item")) mItems = ... + + if (mTranslationRequests == null) setTranslationRequests(new ArrayList<>()); + mTranslationRequests.add(value); + return this; + } + + /** Builds the instance. This builder should not be touched after calling this! */ + public @NonNull TranslationRequest build() { + checkNotUsed(); + mBuilderFieldsSet |= 0x10; // Mark builder used + + TranslationRequest o = new TranslationRequest( + mRequestId, + mSourceSpec, + mDestSpec, + mTranslationRequests); + return o; + } + + private void checkNotUsed() { + if ((mBuilderFieldsSet & 0x10) != 0) { + throw new IllegalStateException( + "This Builder should not be reused. Use a new Builder instance instead"); + } + } + } + + @DataClass.Generated( + time = 1609966181888L, + codegenVersion = "1.0.22", + sourceFile = "frameworks/base/core/java/android/service/translation/TranslationRequest.java", + inputSignatures = "private final int mRequestId\nprivate final @android.annotation.NonNull android.view.translation.TranslationSpec mSourceSpec\nprivate final @android.annotation.NonNull android.view.translation.TranslationSpec mDestSpec\nprivate final @android.annotation.NonNull java.util.List<android.view.translation.TranslationRequest> mTranslationRequests\nclass TranslationRequest extends java.lang.Object implements [android.os.Parcelable]\n@com.android.internal.util.DataClass(genConstructor=true, genBuilder=true, genToString=true)") + @Deprecated + private void __metadata() {} + + + //@formatter:on + // End of generated code + +} diff --git a/core/java/android/service/translation/TranslationService.java b/core/java/android/service/translation/TranslationService.java new file mode 100644 index 000000000000..b0288076376c --- /dev/null +++ b/core/java/android/service/translation/TranslationService.java @@ -0,0 +1,261 @@ +/* + * Copyright (C) 2020 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.service.translation; + +import static android.view.translation.Translator.EXTRA_SERVICE_BINDER; +import static android.view.translation.Translator.EXTRA_SESSION_ID; + +import static com.android.internal.util.function.pooled.PooledLambda.obtainMessage; + +import android.annotation.CallSuper; +import android.annotation.NonNull; +import android.annotation.Nullable; +import android.annotation.SystemApi; +import android.app.Service; +import android.content.Intent; +import android.os.BaseBundle; +import android.os.Bundle; +import android.os.CancellationSignal; +import android.os.Handler; +import android.os.IBinder; +import android.os.Looper; +import android.os.RemoteException; +import android.util.Log; +import android.view.translation.ITranslationDirectManager; +import android.view.translation.TranslationManager; +import android.view.translation.TranslationResponse; +import android.view.translation.TranslationSpec; + +import com.android.internal.os.IResultReceiver; +import com.android.internal.util.SyncResultReceiver; + +/** + * Service for translating text. + * @hide + */ +@SystemApi +public abstract class TranslationService extends Service { + private static final String TAG = "TranslationService"; + + /** + * The {@link Intent} that must be declared as handled by the service. + * + * <p>To be supported, the service must also require the + * {@link android.Manifest.permission#BIND_TRANSLATION_SERVICE} permission so + * that other applications can not abuse it. + */ + public static final String SERVICE_INTERFACE = + "android.service.translation.TranslationService"; + + /** + * Name under which a TranslationService component publishes information about itself. + * + * <p>This meta-data should reference an XML resource containing a + * <code><{@link + * android.R.styleable#TranslationService translation-service}></code> tag. + * + * <p>Here's an example of how to use it on {@code AndroidManifest.xml}: + * TODO: fill in doc example (check CCService/AFService). + */ + public static final String SERVICE_META_DATA = "android.translation_service"; + + private Handler mHandler; + + /** + * Binder to receive calls from system server. + */ + private final ITranslationService mInterface = new ITranslationService.Stub() { + @Override + public void onConnected() { + mHandler.sendMessage(obtainMessage(TranslationService::onConnected, + TranslationService.this)); + } + + @Override + public void onDisconnected() { + mHandler.sendMessage(obtainMessage(TranslationService::onDisconnected, + TranslationService.this)); + } + + @Override + public void onCreateTranslationSession(TranslationSpec sourceSpec, TranslationSpec destSpec, + int sessionId, IResultReceiver receiver) throws RemoteException { + mHandler.sendMessage(obtainMessage(TranslationService::handleOnCreateTranslationSession, + TranslationService.this, sourceSpec, destSpec, sessionId, receiver)); + } + }; + + /** + * Interface definition for a callback to be invoked when the translation is compleled. + */ + public interface OnTranslationResultCallback { + /** + * Notifies the Android System that a translation request + * {@link TranslationService#onTranslationRequest(TranslationRequest, int, + * CancellationSignal, OnTranslationResultCallback)} was successfully fulfilled by the + * service. + * + * <p>This method should always be called, even if the service cannot fulfill the request + * (in which case it should be called with a TranslationResponse with + * {@link android.view.translation.TranslationResponse#TRANSLATION_STATUS_UNKNOWN_ERROR}, + * or {@link android.view.translation.TranslationResponse + * #TRANSLATION_STATUS_LANGUAGE_UNAVAILABLE}). + * + * @param response translation response for the provided request infos. + * + * @throws IllegalStateException if this method was already called. + */ + void onTranslationSuccess(@NonNull TranslationResponse response); + + /** + * TODO: implement javadoc + */ + void onError(); + } + + /** + * Binder that receives calls from the app. + */ + private final ITranslationDirectManager mClientInterface = + new ITranslationDirectManager.Stub() { + // TODO: Implement cancellation signal + @NonNull + private final CancellationSignal mCancellationSignal = new CancellationSignal(); + + @Override + public void onTranslationRequest(TranslationRequest request, int sessionId, + ITranslationCallback callback, IResultReceiver receiver) + throws RemoteException { + // TODO(b/176464808): Currently, the API is used for both sync and async case. + // It may work now, but maybe two methods is more cleaner. To think how to + // define the APIs for these two cases. + final ITranslationCallback cb = callback != null + ? callback + : new ITranslationCallback.Stub() { + @Override + public void onTranslationComplete( + TranslationResponse translationResponse) + throws RemoteException { + receiver.send(0, + SyncResultReceiver.bundleFor(translationResponse)); + } + + @Override + public void onError() throws RemoteException { + //TODO: implement default error callback + } + }; + // TODO(b/176464808): make it a private member of client + final OnTranslationResultCallback translationResultCallback = + new OnTranslationResultCallbackWrapper(cb); + mHandler.sendMessage(obtainMessage(TranslationService::onTranslationRequest, + TranslationService.this, request, sessionId, mCancellationSignal, + translationResultCallback)); + } + + @Override + public void onFinishTranslationSession(int sessionId) throws RemoteException { + mHandler.sendMessage(obtainMessage( + TranslationService::onFinishTranslationSession, + TranslationService.this, sessionId)); + } + }; + + @CallSuper + @Override + public void onCreate() { + super.onCreate(); + mHandler = new Handler(Looper.getMainLooper(), null, true); + BaseBundle.setShouldDefuse(true); + } + + @Override + @Nullable + public final IBinder onBind(@NonNull Intent intent) { + if (SERVICE_INTERFACE.equals(intent.getAction())) { + return mInterface.asBinder(); + } + Log.w(TAG, "Tried to bind to wrong intent (should be " + SERVICE_INTERFACE + ": " + intent); + return null; + } + + /** + * Called when the Android system connects to service. + * + * <p>You should generally do initialization here rather than in {@link #onCreate}. + */ + public void onConnected() { + } + + /** + * Called when the Android system disconnects from the service. + * + * <p> At this point this service may no longer be an active {@link TranslationService}. + * It should not make calls on {@link TranslationManager} that requires the caller to be + * the current service. + */ + public void onDisconnected() { + } + + /** + * TODO: fill in javadoc. + * + * @param sourceSpec + * @param destSpec + * @param sessionId + */ + // TODO(b/176464808): the session id won't be unique cross client/server process. Need to find + // solution to make it's safe. + public abstract void onCreateTranslationSession(@NonNull TranslationSpec sourceSpec, + @NonNull TranslationSpec destSpec, int sessionId); + + /** + * TODO: fill in javadoc. + * + * @param sessionId + */ + public abstract void onFinishTranslationSession(int sessionId); + + /** + * TODO: fill in javadoc. + * + * @param request + * @param sessionId + * @param callback + * @param cancellationSignal + */ + public abstract void onTranslationRequest(@NonNull TranslationRequest request, int sessionId, + @NonNull CancellationSignal cancellationSignal, + @NonNull OnTranslationResultCallback callback); + + // TODO(b/176464808): Need to handle client dying case + + // TODO(b/176464808): Need to handle the failure case. e.g. if the specs does not support + + private void handleOnCreateTranslationSession(@NonNull TranslationSpec sourceSpec, + @NonNull TranslationSpec destSpec, int sessionId, IResultReceiver resultReceiver) { + try { + final Bundle extras = new Bundle(); + extras.putBinder(EXTRA_SERVICE_BINDER, mClientInterface.asBinder()); + extras.putInt(EXTRA_SESSION_ID, sessionId); + resultReceiver.send(0, extras); + } catch (RemoteException e) { + Log.w(TAG, "RemoteException sending client interface: " + e); + } + onCreateTranslationSession(sourceSpec, destSpec, sessionId); + } +} diff --git a/core/java/android/service/translation/TranslationServiceInfo.java b/core/java/android/service/translation/TranslationServiceInfo.java new file mode 100644 index 000000000000..18cc29d12b5f --- /dev/null +++ b/core/java/android/service/translation/TranslationServiceInfo.java @@ -0,0 +1,173 @@ +/* + * Copyright (C) 2020 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.service.translation; + +import android.Manifest; +import android.annotation.NonNull; +import android.annotation.Nullable; +import android.annotation.UserIdInt; +import android.app.AppGlobals; +import android.content.ComponentName; +import android.content.Context; +import android.content.pm.PackageManager; +import android.content.pm.PackageManager.NameNotFoundException; +import android.content.pm.ServiceInfo; +import android.content.res.Resources; +import android.content.res.TypedArray; +import android.content.res.XmlResourceParser; +import android.os.RemoteException; +import android.util.AttributeSet; +import android.util.Log; +import android.util.Slog; +import android.util.Xml; + +import com.android.internal.R; + +import org.xmlpull.v1.XmlPullParser; +import org.xmlpull.v1.XmlPullParserException; + +import java.io.IOException; +import java.io.PrintWriter; + +/** + * {@link ServiceInfo} and meta-data about an {@link TranslationService}. + * + * @hide + */ +public final class TranslationServiceInfo { + + private static final String TAG = "TranslationServiceInfo"; + private static final String XML_TAG_SERVICE = "translation-service"; + + @NonNull + private final ServiceInfo mServiceInfo; + + @Nullable + private final String mSettingsActivity; + + private static ServiceInfo getServiceInfoOrThrow(ComponentName comp, boolean isTemp, + @UserIdInt int userId) throws PackageManager.NameNotFoundException { + int flags = PackageManager.GET_META_DATA; + if (!isTemp) { + flags |= PackageManager.MATCH_SYSTEM_ONLY; + } + + ServiceInfo si = null; + try { + si = AppGlobals.getPackageManager().getServiceInfo(comp, flags, userId); + } catch (RemoteException e) { + } + if (si == null) { + throw new NameNotFoundException("Could not get serviceInfo for " + + (isTemp ? " (temp)" : "(default system)") + + " " + comp.flattenToShortString()); + } + return si; + } + + @NonNull + public ServiceInfo getServiceInfo() { + return mServiceInfo; + } + + @Nullable + public String getSettingsActivity() { + return mSettingsActivity; + } + + public TranslationServiceInfo(@NonNull Context context, @NonNull ComponentName comp, + boolean isTemporaryService, @UserIdInt int userId) + throws PackageManager.NameNotFoundException { + this(context, getServiceInfoOrThrow(comp, isTemporaryService, userId)); + } + + private TranslationServiceInfo(@NonNull Context context, @NonNull ServiceInfo si) { + // Check for permission. + if (!Manifest.permission.BIND_TRANSLATION_SERVICE.equals(si.permission)) { + Slog.w(TAG, "TranslationServiceInfo from '" + si.packageName + + "' does not require permission " + + Manifest.permission.BIND_CONTENT_CAPTURE_SERVICE); + throw new SecurityException("Service does not require permission " + + Manifest.permission.BIND_CONTENT_CAPTURE_SERVICE); + } + + mServiceInfo = si; + + // Get the metadata, if declared. + // TODO: Try to find more easier way to do this. + final XmlResourceParser parser = si.loadXmlMetaData(context.getPackageManager(), + TranslationService.SERVICE_META_DATA); + if (parser == null) { + mSettingsActivity = null; + return; + } + + String settingsActivity = null; + + try { + final Resources resources = context.getPackageManager().getResourcesForApplication( + si.applicationInfo); + + int type = 0; + while (type != XmlPullParser.END_DOCUMENT && type != XmlPullParser.START_TAG) { + type = parser.next(); + } + + if (XML_TAG_SERVICE.equals(parser.getName())) { + final AttributeSet allAttributes = Xml.asAttributeSet(parser); + TypedArray afsAttributes = null; + try { + afsAttributes = resources.obtainAttributes(allAttributes, + com.android.internal.R.styleable.TranslationService); + settingsActivity = afsAttributes.getString( + R.styleable.ContentCaptureService_settingsActivity); + } finally { + if (afsAttributes != null) { + afsAttributes.recycle(); + } + } + } else { + Log.e(TAG, "Meta-data does not start with translation-service tag"); + } + } catch (PackageManager.NameNotFoundException | IOException | XmlPullParserException e) { + Log.e(TAG, "Error parsing auto fill service meta-data", e); + } + + mSettingsActivity = settingsActivity; + } + + @Override + public String toString() { + final StringBuilder builder = new StringBuilder(); + builder.append(getClass().getSimpleName()); + builder.append("[").append(mServiceInfo); + builder.append(", settings:").append(mSettingsActivity); + return builder.toString(); + } + + /** + * Dumps the service information. + */ + public void dump(@NonNull String prefix, @NonNull PrintWriter pw) { + pw.print(prefix); + pw.print("Component: "); + pw.println(getServiceInfo().getComponentName()); + pw.print(prefix); + pw.print("Settings: "); + pw.println(mSettingsActivity); + } +} diff --git a/core/java/android/util/SparseArray.java b/core/java/android/util/SparseArray.java index dae760f989f6..86120d1e650c 100644 --- a/core/java/android/util/SparseArray.java +++ b/core/java/android/util/SparseArray.java @@ -241,6 +241,14 @@ public class SparseArray<E> implements Cloneable { } /** + * Alias for {@link #put(int, Object)} to support Kotlin [index]= operator. + * @see #put(int, Object) + */ + public void set(int key, E value) { + put(key, value); + } + + /** * Adds a mapping from the specified key to the specified value, * replacing the previous mapping from the specified key if there * was one. diff --git a/core/java/android/view/View.java b/core/java/android/view/View.java index e8584dac90c8..4842aae1af73 100644 --- a/core/java/android/view/View.java +++ b/core/java/android/view/View.java @@ -10448,7 +10448,7 @@ public class View implements Drawable.Callback, KeyEvent.Callback, * * @hide */ - @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553) + @UnsupportedAppUsage(trackingBug = 171933273) protected boolean isVisibleToUser(Rect boundInView) { if (mAttachInfo != null) { // Attached to invisible window means this view is not visible. diff --git a/core/java/android/view/translation/ITranslationDirectManager.aidl b/core/java/android/view/translation/ITranslationDirectManager.aidl new file mode 100644 index 000000000000..358f99a5104b --- /dev/null +++ b/core/java/android/view/translation/ITranslationDirectManager.aidl @@ -0,0 +1,33 @@ +/* + * Copyright (C) 2020 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.translation; + +import android.service.translation.TranslationRequest; +import android.service.translation.ITranslationCallback; +import com.android.internal.os.IResultReceiver; + +/** + * Interface between an app (TranslationManager / Translator) and the remote TranslationService + * providing the TranslationService implementation. + * + * @hide + */ +oneway interface ITranslationDirectManager { + void onTranslationRequest(in TranslationRequest request, int sessionId, + in ITranslationCallback callback, in IResultReceiver receiver); + void onFinishTranslationSession(int sessionId); +} diff --git a/core/java/android/view/translation/ITranslationManager.aidl b/core/java/android/view/translation/ITranslationManager.aidl new file mode 100644 index 000000000000..73addf4d1894 --- /dev/null +++ b/core/java/android/view/translation/ITranslationManager.aidl @@ -0,0 +1,32 @@ +/* + * Copyright (C) 2020 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.translation; + +import android.service.translation.TranslationRequest; +import android.view.translation.TranslationSpec; +import com.android.internal.os.IResultReceiver; + +/** + * Mediator between apps being translated and translation service implementation. + * + * {@hide} + */ +oneway interface ITranslationManager { + void getSupportedLocales(in IResultReceiver receiver, int userId); + void onSessionCreated(in TranslationSpec sourceSpec, in TranslationSpec destSpec, + int sessionId, in IResultReceiver receiver, int userId); +} diff --git a/core/java/android/view/translation/OWNERS b/core/java/android/view/translation/OWNERS new file mode 100644 index 000000000000..a1e663aa8ff7 --- /dev/null +++ b/core/java/android/view/translation/OWNERS @@ -0,0 +1,8 @@ +# Bug component: 994311 + +adamhe@google.com +augale@google.com +joannechung@google.com +lpeter@google.com +svetoslavganov@google.com +tymtsai@google.com diff --git a/core/java/android/content/om/OverlayManagerTransaction.aidl b/core/java/android/view/translation/TranslationData.aidl index 6715c82d4e6f..40f21a6b3d4e 100644 --- a/core/java/android/content/om/OverlayManagerTransaction.aidl +++ b/core/java/android/view/translation/TranslationData.aidl @@ -1,5 +1,5 @@ /* - * Copyright (C) 2019 The Android Open Source Project + * Copyright (C) 2020 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. @@ -14,6 +14,6 @@ * limitations under the License. */ -package android.content.om; +package android.view.translation; -parcelable OverlayManagerTransaction; +parcelable TranslationData; diff --git a/core/java/android/view/translation/TranslationManager.java b/core/java/android/view/translation/TranslationManager.java new file mode 100644 index 000000000000..6554e1a1db54 --- /dev/null +++ b/core/java/android/view/translation/TranslationManager.java @@ -0,0 +1,201 @@ +/* + * Copyright (C) 2020 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.translation; + +import android.annotation.NonNull; +import android.annotation.Nullable; +import android.annotation.RequiresFeature; +import android.annotation.SystemService; +import android.annotation.WorkerThread; +import android.content.Context; +import android.content.pm.PackageManager; +import android.os.Handler; +import android.os.Looper; +import android.os.RemoteException; +import android.service.translation.TranslationService; +import android.util.ArrayMap; +import android.util.Log; +import android.util.Pair; +import android.util.SparseArray; + +import com.android.internal.annotations.GuardedBy; +import com.android.internal.util.SyncResultReceiver; + +import java.util.Collections; +import java.util.List; +import java.util.Objects; +import java.util.Random; +import java.util.concurrent.atomic.AtomicInteger; + +/** + * The {@link TranslationManager} class provides ways for apps to integrate and use the + * translation framework. + * + * <p>The TranslationManager manages {@link Translator}s and help bridge client calls to + * the server {@link android.service.translation.TranslationService} </p> + */ +@SystemService(Context.TRANSLATION_MANAGER_SERVICE) +@RequiresFeature(PackageManager.FEATURE_TRANSLATION) +public final class TranslationManager { + + private static final String TAG = "TranslationManager"; + + /** + * Timeout for calls to system_server. + */ + static final int SYNC_CALLS_TIMEOUT_MS = 5000; + /** + * The result code from result receiver success. + * @hide + */ + public static final int STATUS_SYNC_CALL_SUCCESS = 1; + /** + * The result code from result receiver fail. + * @hide + */ + public static final int STATUS_SYNC_CALL_FAIL = 2; + + private static final Random ID_GENERATOR = new Random(); + private final Object mLock = new Object(); + + @NonNull + private final Context mContext; + + private final ITranslationManager mService; + + @Nullable + @GuardedBy("mLock") + private ITranslationDirectManager mDirectServiceBinder; + + @NonNull + @GuardedBy("mLock") + private final SparseArray<Translator> mTranslators = new SparseArray<>(); + + @NonNull + @GuardedBy("mLock") + private final ArrayMap<Pair<TranslationSpec, TranslationSpec>, Integer> mTranslatorIds = + new ArrayMap<>(); + + @NonNull + private final Handler mHandler; + + private static final AtomicInteger sAvailableRequestId = new AtomicInteger(1); + + /** + * @hide + */ + public TranslationManager(@NonNull Context context, ITranslationManager service) { + mContext = Objects.requireNonNull(context, "context cannot be null"); + mService = service; + + mHandler = Handler.createAsync(Looper.getMainLooper()); + } + + /** + * Create a Translator for translation. + * + * <p><strong>NOTE: </strong>Call on a worker thread. + * + * @param sourceSpec {@link TranslationSpec} for the data to be translated. + * @param destSpec {@link TranslationSpec} for the translated data. + * @return a {@link Translator} to be used for calling translation APIs. + */ + @Nullable + @WorkerThread + public Translator createTranslator(@NonNull TranslationSpec sourceSpec, + @NonNull TranslationSpec destSpec) { + Objects.requireNonNull(sourceSpec, "sourceSpec cannot be null"); + Objects.requireNonNull(sourceSpec, "destSpec cannot be null"); + + synchronized (mLock) { + // TODO(b/176464808): Disallow multiple Translator now, it will throw + // IllegalStateException. Need to discuss if we can allow multiple Translators. + final Pair<TranslationSpec, TranslationSpec> specs = new Pair<>(sourceSpec, destSpec); + if (mTranslatorIds.containsKey(specs)) { + return mTranslators.get(mTranslatorIds.get(specs)); + } + + int translatorId; + do { + translatorId = Math.abs(ID_GENERATOR.nextInt()); + } while (translatorId == 0 || mTranslators.indexOfKey(translatorId) >= 0); + + final Translator newTranslator = new Translator(mContext, sourceSpec, destSpec, + translatorId, this, mHandler, mService); + // Start the Translator session and wait for the result + newTranslator.start(); + try { + if (!newTranslator.isSessionCreated()) { + return null; + } + mTranslators.put(translatorId, newTranslator); + mTranslatorIds.put(specs, translatorId); + return newTranslator; + } catch (Translator.ServiceBinderReceiver.TimeoutException e) { + // TODO(b/176464808): maybe make SyncResultReceiver.TimeoutException constructor + // public and use it. + Log.e(TAG, "Timed out getting create session: " + e); + return null; + } + } + } + + /** + * Returns a list of locales supported by the {@link TranslationService}. + * + * <p><strong>NOTE: </strong>Call on a worker thread. + * + * TODO: Change to correct language/locale format + */ + @NonNull + @WorkerThread + public List<String> getSupportedLocales() { + try { + // TODO: implement it + final SyncResultReceiver receiver = new SyncResultReceiver(SYNC_CALLS_TIMEOUT_MS); + mService.getSupportedLocales(receiver, mContext.getUserId()); + int resutCode = receiver.getIntResult(); + if (resutCode != STATUS_SYNC_CALL_SUCCESS) { + return Collections.emptyList(); + } + return receiver.getParcelableResult(); + } catch (RemoteException e) { + throw e.rethrowFromSystemServer(); + } catch (SyncResultReceiver.TimeoutException e) { + Log.e(TAG, "Timed out getting supported locales: " + e); + return Collections.emptyList(); + } + } + + void removeTranslator(int id) { + synchronized (mLock) { + mTranslators.remove(id); + for (int i = 0; i < mTranslatorIds.size(); i++) { + if (mTranslatorIds.valueAt(i) == id) { + mTranslatorIds.removeAt(i); + break; + } + } + } + } + + AtomicInteger getAvailableRequestId() { + synchronized (mLock) { + return sAvailableRequestId; + } + } +} diff --git a/core/java/android/view/translation/TranslationRequest.aidl b/core/java/android/view/translation/TranslationRequest.aidl new file mode 100644 index 000000000000..c34bf3011462 --- /dev/null +++ b/core/java/android/view/translation/TranslationRequest.aidl @@ -0,0 +1,19 @@ +/* + * Copyright (C) 2020 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.translation; + +parcelable TranslationRequest; diff --git a/core/java/android/view/translation/TranslationRequest.java b/core/java/android/view/translation/TranslationRequest.java new file mode 100644 index 000000000000..a5e3f758ba9f --- /dev/null +++ b/core/java/android/view/translation/TranslationRequest.java @@ -0,0 +1,215 @@ +/* + * Copyright (C) 2020 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.translation; + +import android.annotation.NonNull; +import android.annotation.Nullable; +import android.os.Parcelable; +import android.view.autofill.AutofillId; + +import com.android.internal.util.DataClass; + +/** + * Wrapper class for data to be translated by {@link android.service.translation.TranslationService} + */ +@DataClass(genToString = true, genBuilder = true) +public final class TranslationRequest implements Parcelable { + + @Nullable + private final AutofillId mAutofillId; + + @Nullable + private final CharSequence mTranslationText; + + public TranslationRequest(@Nullable CharSequence text) { + mAutofillId = null; + mTranslationText = text; + } + + private static CharSequence defaultTranslationText() { + return null; + } + + private static AutofillId defaultAutofillId() { + return null; + } + + + + // Code below generated by codegen v1.0.22. + // + // DO NOT MODIFY! + // CHECKSTYLE:OFF Generated code + // + // To regenerate run: + // $ codegen $ANDROID_BUILD_TOP/frameworks/base/core/java/android/view/translation/TranslationRequest.java + // + // To exclude the generated code from IntelliJ auto-formatting enable (one-time): + // Settings > Editor > Code Style > Formatter Control + //@formatter:off + + + @DataClass.Generated.Member + /* package-private */ TranslationRequest( + @Nullable AutofillId autofillId, + @Nullable CharSequence translationText) { + this.mAutofillId = autofillId; + this.mTranslationText = translationText; + + // onConstructed(); // You can define this method to get a callback + } + + @DataClass.Generated.Member + public @Nullable AutofillId getAutofillId() { + return mAutofillId; + } + + @DataClass.Generated.Member + public @Nullable CharSequence getTranslationText() { + return mTranslationText; + } + + @Override + @DataClass.Generated.Member + public String toString() { + // You can override field toString logic by defining methods like: + // String fieldNameToString() { ... } + + return "TranslationRequest { " + + "autofillId = " + mAutofillId + ", " + + "translationText = " + mTranslationText + + " }"; + } + + @Override + @DataClass.Generated.Member + public void writeToParcel(@NonNull android.os.Parcel dest, int flags) { + // You can override field parcelling by defining methods like: + // void parcelFieldName(Parcel dest, int flags) { ... } + + byte flg = 0; + if (mAutofillId != null) flg |= 0x1; + if (mTranslationText != null) flg |= 0x2; + dest.writeByte(flg); + if (mAutofillId != null) dest.writeTypedObject(mAutofillId, flags); + if (mTranslationText != null) dest.writeCharSequence(mTranslationText); + } + + @Override + @DataClass.Generated.Member + public int describeContents() { return 0; } + + /** @hide */ + @SuppressWarnings({"unchecked", "RedundantCast"}) + @DataClass.Generated.Member + /* package-private */ TranslationRequest(@NonNull android.os.Parcel in) { + // You can override field unparcelling by defining methods like: + // static FieldType unparcelFieldName(Parcel in) { ... } + + byte flg = in.readByte(); + AutofillId autofillId = (flg & 0x1) == 0 ? null : (AutofillId) in.readTypedObject(AutofillId.CREATOR); + CharSequence translationText = (flg & 0x2) == 0 ? null : (CharSequence) in.readCharSequence(); + + this.mAutofillId = autofillId; + this.mTranslationText = translationText; + + // onConstructed(); // You can define this method to get a callback + } + + @DataClass.Generated.Member + public static final @NonNull Parcelable.Creator<TranslationRequest> CREATOR + = new Parcelable.Creator<TranslationRequest>() { + @Override + public TranslationRequest[] newArray(int size) { + return new TranslationRequest[size]; + } + + @Override + public TranslationRequest createFromParcel(@NonNull android.os.Parcel in) { + return new TranslationRequest(in); + } + }; + + /** + * A builder for {@link TranslationRequest} + */ + @SuppressWarnings("WeakerAccess") + @DataClass.Generated.Member + public static final class Builder { + + private @Nullable AutofillId mAutofillId; + private @Nullable CharSequence mTranslationText; + + private long mBuilderFieldsSet = 0L; + + public Builder() { + } + + @DataClass.Generated.Member + public @NonNull Builder setAutofillId(@NonNull AutofillId value) { + checkNotUsed(); + mBuilderFieldsSet |= 0x1; + mAutofillId = value; + return this; + } + + @DataClass.Generated.Member + public @NonNull Builder setTranslationText(@NonNull CharSequence value) { + checkNotUsed(); + mBuilderFieldsSet |= 0x2; + mTranslationText = value; + return this; + } + + /** Builds the instance. This builder should not be touched after calling this! */ + public @NonNull TranslationRequest build() { + checkNotUsed(); + mBuilderFieldsSet |= 0x4; // Mark builder used + + if ((mBuilderFieldsSet & 0x1) == 0) { + mAutofillId = defaultAutofillId(); + } + if ((mBuilderFieldsSet & 0x2) == 0) { + mTranslationText = defaultTranslationText(); + } + TranslationRequest o = new TranslationRequest( + mAutofillId, + mTranslationText); + return o; + } + + private void checkNotUsed() { + if ((mBuilderFieldsSet & 0x4) != 0) { + throw new IllegalStateException( + "This Builder should not be reused. Use a new Builder instance instead"); + } + } + } + + @DataClass.Generated( + time = 1610060189421L, + codegenVersion = "1.0.22", + sourceFile = "frameworks/base/core/java/android/view/translation/TranslationRequest.java", + inputSignatures = "private final @android.annotation.Nullable android.view.autofill.AutofillId mAutofillId\nprivate final @android.annotation.Nullable java.lang.CharSequence mTranslationText\nprivate static java.lang.CharSequence defaultTranslationText()\nprivate static android.view.autofill.AutofillId defaultAutofillId()\nclass TranslationRequest extends java.lang.Object implements [android.os.Parcelable]\n@com.android.internal.util.DataClass(genToString=true, genBuilder=true)") + @Deprecated + private void __metadata() {} + + + //@formatter:on + // End of generated code + +} diff --git a/core/java/android/view/translation/TranslationResponse.aidl b/core/java/android/view/translation/TranslationResponse.aidl new file mode 100644 index 000000000000..e5350bb54dc2 --- /dev/null +++ b/core/java/android/view/translation/TranslationResponse.aidl @@ -0,0 +1,19 @@ +/* + * Copyright (C) 2020 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.translation; + +parcelable TranslationResponse; diff --git a/core/java/android/view/translation/TranslationResponse.java b/core/java/android/view/translation/TranslationResponse.java new file mode 100644 index 000000000000..d29063fbd914 --- /dev/null +++ b/core/java/android/view/translation/TranslationResponse.java @@ -0,0 +1,311 @@ +/* + * Copyright (C) 2020 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.translation; + +import android.annotation.IntDef; +import android.annotation.NonNull; +import android.os.Parcel; +import android.os.Parcelable; +import android.service.translation.TranslationService; + +import com.android.internal.util.DataClass; + +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.util.ArrayList; +import java.util.List; + +/** + * Response from the {@link TranslationService}, which contains the translated result. + */ +@DataClass(genBuilder = true, genToString = true, genHiddenConstDefs = true) +public final class TranslationResponse implements Parcelable { + + /** + * The {@link TranslationService} was successful in translating. + */ + public static final int TRANSLATION_STATUS_SUCCESS = 0; + /** + * The {@link TranslationService} returned unknown translation result. + */ + public static final int TRANSLATION_STATUS_UNKNOWN_ERROR = 1; + /** + * The language of the request is not available to be translated. + */ + public static final int TRANSLATION_STATUS_LANGUAGE_UNAVAILABLE = 2; + + /** + * The translation result status code. + */ + private final @TranslationStatus int mTranslationStatus; + /** + * The translation results. If there is no translation result, set it with an empty list. + */ + @NonNull + private List<TranslationRequest> mTranslations = new ArrayList(); + + + + + // Code below generated by codegen v1.0.22. + // + // DO NOT MODIFY! + // CHECKSTYLE:OFF Generated code + // + // To regenerate run: + // $ codegen $ANDROID_BUILD_TOP/frameworks/base/core/java/android/view/translation/TranslationResponse.java + // + // To exclude the generated code from IntelliJ auto-formatting enable (one-time): + // Settings > Editor > Code Style > Formatter Control + //@formatter:off + + + /** @hide */ + @IntDef(prefix = "TRANSLATION_STATUS_", value = { + TRANSLATION_STATUS_SUCCESS, + TRANSLATION_STATUS_UNKNOWN_ERROR, + TRANSLATION_STATUS_LANGUAGE_UNAVAILABLE + }) + @Retention(RetentionPolicy.SOURCE) + @DataClass.Generated.Member + public @interface TranslationStatus {} + + /** @hide */ + @DataClass.Generated.Member + public static String translationStatusToString(@TranslationStatus int value) { + switch (value) { + case TRANSLATION_STATUS_SUCCESS: + return "TRANSLATION_STATUS_SUCCESS"; + case TRANSLATION_STATUS_UNKNOWN_ERROR: + return "TRANSLATION_STATUS_UNKNOWN_ERROR"; + case TRANSLATION_STATUS_LANGUAGE_UNAVAILABLE: + return "TRANSLATION_STATUS_LANGUAGE_UNAVAILABLE"; + default: return Integer.toHexString(value); + } + } + + @DataClass.Generated.Member + /* package-private */ TranslationResponse( + @TranslationStatus int translationStatus, + @NonNull List<TranslationRequest> translations) { + this.mTranslationStatus = translationStatus; + + if (!(mTranslationStatus == TRANSLATION_STATUS_SUCCESS) + && !(mTranslationStatus == TRANSLATION_STATUS_UNKNOWN_ERROR) + && !(mTranslationStatus == TRANSLATION_STATUS_LANGUAGE_UNAVAILABLE)) { + throw new java.lang.IllegalArgumentException( + "translationStatus was " + mTranslationStatus + " but must be one of: " + + "TRANSLATION_STATUS_SUCCESS(" + TRANSLATION_STATUS_SUCCESS + "), " + + "TRANSLATION_STATUS_UNKNOWN_ERROR(" + TRANSLATION_STATUS_UNKNOWN_ERROR + "), " + + "TRANSLATION_STATUS_LANGUAGE_UNAVAILABLE(" + TRANSLATION_STATUS_LANGUAGE_UNAVAILABLE + ")"); + } + + this.mTranslations = translations; + com.android.internal.util.AnnotationValidations.validate( + NonNull.class, null, mTranslations); + + // onConstructed(); // You can define this method to get a callback + } + + /** + * The translation result status code. + */ + @DataClass.Generated.Member + public @TranslationStatus int getTranslationStatus() { + return mTranslationStatus; + } + + /** + * The translation results. If there is no translation result, set it with an empty list. + */ + @DataClass.Generated.Member + public @NonNull List<TranslationRequest> getTranslations() { + return mTranslations; + } + + @Override + @DataClass.Generated.Member + public String toString() { + // You can override field toString logic by defining methods like: + // String fieldNameToString() { ... } + + return "TranslationResponse { " + + "translationStatus = " + translationStatusToString(mTranslationStatus) + ", " + + "translations = " + mTranslations + + " }"; + } + + @Override + @DataClass.Generated.Member + public void writeToParcel(@NonNull Parcel dest, int flags) { + // You can override field parcelling by defining methods like: + // void parcelFieldName(Parcel dest, int flags) { ... } + + dest.writeInt(mTranslationStatus); + dest.writeParcelableList(mTranslations, flags); + } + + @Override + @DataClass.Generated.Member + public int describeContents() { return 0; } + + /** @hide */ + @SuppressWarnings({"unchecked", "RedundantCast"}) + @DataClass.Generated.Member + /* package-private */ TranslationResponse(@NonNull Parcel in) { + // You can override field unparcelling by defining methods like: + // static FieldType unparcelFieldName(Parcel in) { ... } + + int translationStatus = in.readInt(); + List<TranslationRequest> translations = new ArrayList<>(); + in.readParcelableList(translations, TranslationRequest.class.getClassLoader()); + + this.mTranslationStatus = translationStatus; + + if (!(mTranslationStatus == TRANSLATION_STATUS_SUCCESS) + && !(mTranslationStatus == TRANSLATION_STATUS_UNKNOWN_ERROR) + && !(mTranslationStatus == TRANSLATION_STATUS_LANGUAGE_UNAVAILABLE)) { + throw new java.lang.IllegalArgumentException( + "translationStatus was " + mTranslationStatus + " but must be one of: " + + "TRANSLATION_STATUS_SUCCESS(" + TRANSLATION_STATUS_SUCCESS + "), " + + "TRANSLATION_STATUS_UNKNOWN_ERROR(" + TRANSLATION_STATUS_UNKNOWN_ERROR + "), " + + "TRANSLATION_STATUS_LANGUAGE_UNAVAILABLE(" + TRANSLATION_STATUS_LANGUAGE_UNAVAILABLE + ")"); + } + + this.mTranslations = translations; + com.android.internal.util.AnnotationValidations.validate( + NonNull.class, null, mTranslations); + + // onConstructed(); // You can define this method to get a callback + } + + @DataClass.Generated.Member + public static final @NonNull Parcelable.Creator<TranslationResponse> CREATOR + = new Parcelable.Creator<TranslationResponse>() { + @Override + public TranslationResponse[] newArray(int size) { + return new TranslationResponse[size]; + } + + @Override + public TranslationResponse createFromParcel(@NonNull Parcel in) { + return new TranslationResponse(in); + } + }; + + /** + * A builder for {@link TranslationResponse} + */ + @SuppressWarnings("WeakerAccess") + @DataClass.Generated.Member + public static final class Builder { + + private @TranslationStatus int mTranslationStatus; + private @NonNull List<TranslationRequest> mTranslations; + + private long mBuilderFieldsSet = 0L; + + /** + * Creates a new Builder. + * + * @param translationStatus + * The translation result status code. + */ + public Builder( + @TranslationStatus int translationStatus) { + mTranslationStatus = translationStatus; + + if (!(mTranslationStatus == TRANSLATION_STATUS_SUCCESS) + && !(mTranslationStatus == TRANSLATION_STATUS_UNKNOWN_ERROR) + && !(mTranslationStatus == TRANSLATION_STATUS_LANGUAGE_UNAVAILABLE)) { + throw new java.lang.IllegalArgumentException( + "translationStatus was " + mTranslationStatus + " but must be one of: " + + "TRANSLATION_STATUS_SUCCESS(" + TRANSLATION_STATUS_SUCCESS + "), " + + "TRANSLATION_STATUS_UNKNOWN_ERROR(" + TRANSLATION_STATUS_UNKNOWN_ERROR + "), " + + "TRANSLATION_STATUS_LANGUAGE_UNAVAILABLE(" + TRANSLATION_STATUS_LANGUAGE_UNAVAILABLE + ")"); + } + + } + + /** + * The translation result status code. + */ + @DataClass.Generated.Member + public @NonNull Builder setTranslationStatus(@TranslationStatus int value) { + checkNotUsed(); + mBuilderFieldsSet |= 0x1; + mTranslationStatus = value; + return this; + } + + /** + * The translation results. If there is no translation result, set it with an empty list. + */ + @DataClass.Generated.Member + public @NonNull Builder setTranslations(@NonNull List<TranslationRequest> value) { + checkNotUsed(); + mBuilderFieldsSet |= 0x2; + mTranslations = value; + return this; + } + + /** @see #setTranslations */ + @DataClass.Generated.Member + public @NonNull Builder addTranslations(@NonNull TranslationRequest value) { + // You can refine this method's name by providing item's singular name, e.g.: + // @DataClass.PluralOf("item")) mItems = ... + + if (mTranslations == null) setTranslations(new ArrayList<>()); + mTranslations.add(value); + return this; + } + + /** Builds the instance. This builder should not be touched after calling this! */ + public @NonNull TranslationResponse build() { + checkNotUsed(); + mBuilderFieldsSet |= 0x4; // Mark builder used + + if ((mBuilderFieldsSet & 0x2) == 0) { + mTranslations = new ArrayList(); + } + TranslationResponse o = new TranslationResponse( + mTranslationStatus, + mTranslations); + return o; + } + + private void checkNotUsed() { + if ((mBuilderFieldsSet & 0x4) != 0) { + throw new IllegalStateException( + "This Builder should not be reused. Use a new Builder instance instead"); + } + } + } + + @DataClass.Generated( + time = 1609973911361L, + codegenVersion = "1.0.22", + sourceFile = "frameworks/base/core/java/android/view/translation/TranslationResponse.java", + inputSignatures = "public static final int TRANSLATION_STATUS_SUCCESS\npublic static final int TRANSLATION_STATUS_UNKNOWN_ERROR\npublic static final int TRANSLATION_STATUS_LANGUAGE_UNAVAILABLE\nprivate final @android.view.translation.TranslationResponse.TranslationStatus int mTranslationStatus\nprivate @android.annotation.NonNull java.util.List<android.view.translation.TranslationRequest> mTranslations\nclass TranslationResponse extends java.lang.Object implements [android.os.Parcelable]\n@com.android.internal.util.DataClass(genBuilder=true, genToString=true, genHiddenConstDefs=true)") + @Deprecated + private void __metadata() {} + + + //@formatter:on + // End of generated code + +} diff --git a/core/java/android/view/translation/TranslationSpec.aidl b/core/java/android/view/translation/TranslationSpec.aidl new file mode 100644 index 000000000000..875d798370d4 --- /dev/null +++ b/core/java/android/view/translation/TranslationSpec.aidl @@ -0,0 +1,19 @@ +/* + * Copyright (C) 2020 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.translation; + +parcelable TranslationSpec; diff --git a/core/java/android/view/translation/TranslationSpec.java b/core/java/android/view/translation/TranslationSpec.java new file mode 100644 index 000000000000..ab1bc477e0fd --- /dev/null +++ b/core/java/android/view/translation/TranslationSpec.java @@ -0,0 +1,189 @@ +/* + * Copyright (C) 2020 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.translation; + +import android.annotation.NonNull; +import android.os.Parcel; +import android.os.Parcelable; + +import com.android.internal.util.DataClass; + +/** + * Specs and additional info for the translation data. + * + * <p>This spec help specify information such as the language/locale for the translation, as well + * as the data format for the translation (text, audio, etc.)</p> + */ +@DataClass(genEqualsHashCode = true, genHiddenConstDefs = true) +public final class TranslationSpec implements Parcelable { + + /** Data format for translation is text. */ + public static final int DATA_FORMAT_TEXT = 1; + + /** @hide */ + @android.annotation.IntDef(prefix = "DATA_FORMAT_", value = { + DATA_FORMAT_TEXT + }) + @java.lang.annotation.Retention(java.lang.annotation.RetentionPolicy.SOURCE) + @DataClass.Generated.Member + public @interface DataFormat {} + + /** + * String representation of language codes e.g. "en", "es", etc. + */ + private final @NonNull String mLanguage; + + private final @DataFormat int mDataFormat; + + + + // Code below generated by codegen v1.0.22. + // + // DO NOT MODIFY! + // CHECKSTYLE:OFF Generated code + // + // To regenerate run: + // $ codegen $ANDROID_BUILD_TOP/frameworks/base/core/java/android/view/translation/TranslationSpec.java + // + // To exclude the generated code from IntelliJ auto-formatting enable (one-time): + // Settings > Editor > Code Style > Formatter Control + //@formatter:off + + + /** + * Creates a new TranslationSpec. + * + * @param language + * String representation of language codes e.g. "en", "es", etc. + */ + @DataClass.Generated.Member + public TranslationSpec( + @NonNull String language, + @DataFormat int dataFormat) { + this.mLanguage = language; + com.android.internal.util.AnnotationValidations.validate( + NonNull.class, null, mLanguage); + this.mDataFormat = dataFormat; + com.android.internal.util.AnnotationValidations.validate( + DataFormat.class, null, mDataFormat); + + // onConstructed(); // You can define this method to get a callback + } + + /** + * String representation of language codes e.g. "en", "es", etc. + */ + @DataClass.Generated.Member + public @NonNull String getLanguage() { + return mLanguage; + } + + @DataClass.Generated.Member + public @DataFormat int getDataFormat() { + return mDataFormat; + } + + @Override + @DataClass.Generated.Member + public boolean equals(@android.annotation.Nullable Object o) { + // You can override field equality logic by defining either of the methods like: + // boolean fieldNameEquals(TranslationSpec other) { ... } + // boolean fieldNameEquals(FieldType otherValue) { ... } + + if (this == o) return true; + if (o == null || getClass() != o.getClass()) return false; + @SuppressWarnings("unchecked") + TranslationSpec that = (TranslationSpec) o; + //noinspection PointlessBooleanExpression + return true + && java.util.Objects.equals(mLanguage, that.mLanguage) + && mDataFormat == that.mDataFormat; + } + + @Override + @DataClass.Generated.Member + public int hashCode() { + // You can override field hashCode logic by defining methods like: + // int fieldNameHashCode() { ... } + + int _hash = 1; + _hash = 31 * _hash + java.util.Objects.hashCode(mLanguage); + _hash = 31 * _hash + mDataFormat; + return _hash; + } + + @Override + @DataClass.Generated.Member + public void writeToParcel(@NonNull Parcel dest, int flags) { + // You can override field parcelling by defining methods like: + // void parcelFieldName(Parcel dest, int flags) { ... } + + dest.writeString(mLanguage); + dest.writeInt(mDataFormat); + } + + @Override + @DataClass.Generated.Member + public int describeContents() { return 0; } + + /** @hide */ + @SuppressWarnings({"unchecked", "RedundantCast"}) + @DataClass.Generated.Member + /* package-private */ TranslationSpec(@NonNull Parcel in) { + // You can override field unparcelling by defining methods like: + // static FieldType unparcelFieldName(Parcel in) { ... } + + String language = in.readString(); + int dataFormat = in.readInt(); + + this.mLanguage = language; + com.android.internal.util.AnnotationValidations.validate( + NonNull.class, null, mLanguage); + this.mDataFormat = dataFormat; + com.android.internal.util.AnnotationValidations.validate( + DataFormat.class, null, mDataFormat); + + // onConstructed(); // You can define this method to get a callback + } + + @DataClass.Generated.Member + public static final @NonNull Parcelable.Creator<TranslationSpec> CREATOR + = new Parcelable.Creator<TranslationSpec>() { + @Override + public TranslationSpec[] newArray(int size) { + return new TranslationSpec[size]; + } + + @Override + public TranslationSpec createFromParcel(@NonNull Parcel in) { + return new TranslationSpec(in); + } + }; + + @DataClass.Generated( + time = 1609964630624L, + codegenVersion = "1.0.22", + sourceFile = "frameworks/base/core/java/android/view/translation/TranslationSpec.java", + inputSignatures = "public static final int DATA_FORMAT_TEXT\nprivate final @android.annotation.NonNull java.lang.String mLanguage\nprivate final @android.view.translation.TranslationSpec.DataFormat int mDataFormat\nclass TranslationSpec extends java.lang.Object implements [android.os.Parcelable]\n@com.android.internal.util.DataClass(genEqualsHashCode=true, genHiddenConstDefs=true)") + @Deprecated + private void __metadata() {} + + + //@formatter:on + // End of generated code + +} diff --git a/core/java/android/view/translation/Translator.java b/core/java/android/view/translation/Translator.java new file mode 100644 index 000000000000..675f32b19d17 --- /dev/null +++ b/core/java/android/view/translation/Translator.java @@ -0,0 +1,298 @@ +/* + * Copyright (C) 2020 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.translation; + +import static android.view.translation.TranslationManager.STATUS_SYNC_CALL_FAIL; +import static android.view.translation.TranslationManager.SYNC_CALLS_TIMEOUT_MS; + +import android.annotation.NonNull; +import android.annotation.Nullable; +import android.annotation.SuppressLint; +import android.annotation.WorkerThread; +import android.content.Context; +import android.os.Bundle; +import android.os.Handler; +import android.os.IBinder; +import android.os.RemoteException; +import android.util.Log; + +import com.android.internal.annotations.GuardedBy; +import com.android.internal.os.IResultReceiver; +import com.android.internal.util.SyncResultReceiver; + +import java.lang.ref.WeakReference; +import java.util.ArrayList; +import java.util.Objects; +import java.util.concurrent.CountDownLatch; +import java.util.concurrent.TimeUnit; + +/** + * The {@link Translator} for translation, defined by a source and a dest {@link TranslationSpec}. + */ +@SuppressLint("NotCloseable") +public class Translator { + + private static final String TAG = "Translator"; + + // TODO: make this configurable and cross the Translation component + private static boolean sDEBUG = false; + + private final Object mLock = new Object(); + + private int mId; + + @NonNull + private final Context mContext; + + @NonNull + private final TranslationSpec mSourceSpec; + + @NonNull + private final TranslationSpec mDestSpec; + + @NonNull + private final TranslationManager mManager; + + @NonNull + private final Handler mHandler; + + /** + * Interface to the system_server binder object. + */ + private ITranslationManager mSystemServerBinder; + + /** + * Direct interface to the TranslationService binder object. + */ + @Nullable + private ITranslationDirectManager mDirectServiceBinder; + + @NonNull + private final ServiceBinderReceiver mServiceBinderReceiver; + + @GuardedBy("mLock") + private boolean mDestroyed; + + /** + * Name of the {@link IResultReceiver} extra used to pass the binder interface to Translator. + * @hide + */ + public static final String EXTRA_SERVICE_BINDER = "binder"; + /** + * Name of the extra used to pass the session id to Translator. + * @hide + */ + public static final String EXTRA_SESSION_ID = "sessionId"; + + static class ServiceBinderReceiver extends IResultReceiver.Stub { + private final WeakReference<Translator> mTranslator; + private final CountDownLatch mLatch = new CountDownLatch(1); + private int mSessionId; + + ServiceBinderReceiver(Translator translator) { + mTranslator = new WeakReference<>(translator); + } + + int getSessionStateResult() throws TimeoutException { + try { + if (!mLatch.await(SYNC_CALLS_TIMEOUT_MS, TimeUnit.MILLISECONDS)) { + throw new TimeoutException( + "Session not created in " + SYNC_CALLS_TIMEOUT_MS + "ms"); + } + } catch (InterruptedException e) { + Thread.currentThread().interrupt(); + throw new TimeoutException("Session not created because interrupted"); + } + return mSessionId; + } + + @Override + public void send(int resultCode, Bundle resultData) { + if (resultCode == STATUS_SYNC_CALL_FAIL) { + mLatch.countDown(); + return; + } + mSessionId = resultData.getInt(EXTRA_SESSION_ID); + final Translator translator = mTranslator.get(); + if (translator == null) { + Log.w(TAG, "received result after session is finished"); + return; + } + final IBinder binder; + if (resultData != null) { + binder = resultData.getBinder(EXTRA_SERVICE_BINDER); + if (binder == null) { + Log.wtf(TAG, "No " + EXTRA_SERVICE_BINDER + " extra result"); + return; + } + } else { + binder = null; + } + translator.setServiceBinder(binder); + mLatch.countDown(); + } + + // TODO(b/176464808): maybe make SyncResultReceiver.TimeoutException constructor public + // and use it. + static final class TimeoutException extends Exception { + private TimeoutException(String msg) { + super(msg); + } + } + } + + /** + * Create the Translator. + * + * @hide + */ + public Translator(@NonNull Context context, + @NonNull TranslationSpec sourceSpec, + @NonNull TranslationSpec destSpec, int sessionId, + @NonNull TranslationManager translationManager, @NonNull Handler handler, + @Nullable ITranslationManager systemServerBinder) { + mContext = context; + mSourceSpec = sourceSpec; + mDestSpec = destSpec; + mId = sessionId; + mManager = translationManager; + mHandler = handler; + mSystemServerBinder = systemServerBinder; + mServiceBinderReceiver = new ServiceBinderReceiver(this); + } + + /** + * Starts this Translator session. + */ + void start() { + try { + mSystemServerBinder.onSessionCreated(mSourceSpec, mDestSpec, mId, + mServiceBinderReceiver, mContext.getUserId()); + } catch (RemoteException e) { + Log.w(TAG, "RemoteException calling startSession(): " + e); + } + } + + /** + * Wait this Translator session created. + * + * @return {@code true} if the session is created successfully. + */ + boolean isSessionCreated() throws ServiceBinderReceiver.TimeoutException { + int receivedId = mServiceBinderReceiver.getSessionStateResult(); + return receivedId > 0; + } + + private int getNextRequestId() { + // Get from manager to keep the request id unique to different Translators + return mManager.getAvailableRequestId().getAndIncrement(); + } + + private void setServiceBinder(@Nullable IBinder binder) { + synchronized (mLock) { + if (mDirectServiceBinder != null) { + return; + } + if (binder != null) { + mDirectServiceBinder = ITranslationDirectManager.Stub.asInterface(binder); + } + } + } + + /** @hide */ + public int getTranslatorId() { + return mId; + } + + /** + * Requests a translation for the provided {@link TranslationRequest} using the Translator's + * source spec and destination spec. + * + * <p><strong>NOTE: </strong>Call on a worker thread. + * + * @param request {@link TranslationRequest} request to be translated. + * + * @return {@link TranslationRequest} containing translated request, + * or null if translation could not be done. + * @throws IllegalStateException if this TextClassification session was destroyed when calls + */ + @Nullable + @WorkerThread + public TranslationResponse translate(@NonNull TranslationRequest request) { + Objects.requireNonNull(request, "Translation request cannot be null"); + if (isDestroyed()) { + // TODO(b/176464808): Disallow multiple Translator now, it will throw + // IllegalStateException. Need to discuss if we can allow multiple Translators. + throw new IllegalStateException( + "This translator has been destroyed"); + } + final ArrayList<TranslationRequest> requests = new ArrayList<>(); + requests.add(request); + final android.service.translation.TranslationRequest internalRequest = + new android.service.translation.TranslationRequest + .Builder(getNextRequestId(), mSourceSpec, mDestSpec, requests) + .build(); + + TranslationResponse response = null; + try { + final SyncResultReceiver receiver = new SyncResultReceiver(SYNC_CALLS_TIMEOUT_MS); + mDirectServiceBinder.onTranslationRequest(internalRequest, mId, null, receiver); + + response = receiver.getParcelableResult(); + } catch (RemoteException e) { + Log.w(TAG, "RemoteException calling requestTranslate(): " + e); + } catch (SyncResultReceiver.TimeoutException e) { + Log.e(TAG, "Timed out calling requestTranslate: " + e); + } + if (sDEBUG) { + Log.v(TAG, "Receive translation response: " + response); + } + return response; + } + + /** + * Destroy this Translator. + */ + public void destroy() { + synchronized (mLock) { + if (mDestroyed) { + return; + } + mDestroyed = true; + try { + mDirectServiceBinder.onFinishTranslationSession(mId); + } catch (RemoteException e) { + Log.w(TAG, "RemoteException calling onSessionFinished"); + } + mDirectServiceBinder = null; + mManager.removeTranslator(mId); + } + } + + /** + * Returns whether or not this Translator has been destroyed. + * + * @see #destroy() + */ + public boolean isDestroyed() { + synchronized (mLock) { + return mDestroyed; + } + } + + // TODO: add methods for UI-toolkit case. +} diff --git a/core/java/android/window/ITaskOrganizerController.aidl b/core/java/android/window/ITaskOrganizerController.aidl index 4a43a438b69d..2d0211e129bf 100644 --- a/core/java/android/window/ITaskOrganizerController.aidl +++ b/core/java/android/window/ITaskOrganizerController.aidl @@ -56,12 +56,6 @@ interface ITaskOrganizerController { WindowContainerToken getImeTarget(int display); /** - * Set's the root task to launch new tasks into on a display. {@code null} means no launch root - * and thus new tasks just end up directly on the display. - */ - void setLaunchRoot(int displayId, in WindowContainerToken root); - - /** * Requests that the given task organizer is notified when back is pressed on the root activity * of one of its controlled tasks. */ diff --git a/core/java/android/window/TaskOrganizer.java b/core/java/android/window/TaskOrganizer.java index 73b2fe1ff4d5..f29eb39045ae 100644 --- a/core/java/android/window/TaskOrganizer.java +++ b/core/java/android/window/TaskOrganizer.java @@ -184,19 +184,6 @@ public class TaskOrganizer extends WindowOrganizer { } /** - * Set's the root task to launch new tasks into on a display. {@code null} means no launch - * root and thus new tasks just end up directly on the display. - */ - @RequiresPermission(android.Manifest.permission.MANAGE_ACTIVITY_TASKS) - public void setLaunchRoot(int displayId, @NonNull WindowContainerToken root) { - try { - mTaskOrganizerController.setLaunchRoot(displayId, root); - } catch (RemoteException e) { - throw e.rethrowFromSystemServer(); - } - } - - /** * Requests that the given task organizer is notified when back is pressed on the root activity * of one of its controlled tasks. */ diff --git a/core/java/android/window/WindowContainerTransaction.java b/core/java/android/window/WindowContainerTransaction.java index eba4fd21166d..6bc31107bddf 100644 --- a/core/java/android/window/WindowContainerTransaction.java +++ b/core/java/android/window/WindowContainerTransaction.java @@ -30,6 +30,7 @@ import android.util.ArrayMap; import android.view.SurfaceControl; import java.util.ArrayList; +import java.util.Arrays; import java.util.List; import java.util.Map; @@ -263,8 +264,9 @@ public final class WindowContainerTransaction implements Parcelable { @NonNull public WindowContainerTransaction reparent(@NonNull WindowContainerToken child, @Nullable WindowContainerToken parent, boolean onTop) { - mHierarchyOps.add(new HierarchyOp(child.asBinder(), - parent == null ? null : parent.asBinder(), onTop)); + mHierarchyOps.add(HierarchyOp.createForReparent(child.asBinder(), + parent == null ? null : parent.asBinder(), + onTop)); return this; } @@ -276,7 +278,47 @@ public final class WindowContainerTransaction implements Parcelable { */ @NonNull public WindowContainerTransaction reorder(@NonNull WindowContainerToken child, boolean onTop) { - mHierarchyOps.add(new HierarchyOp(child.asBinder(), onTop)); + mHierarchyOps.add(HierarchyOp.createForReorder(child.asBinder(), onTop)); + return this; + } + + /** + * Reparent's all children tasks of {@param currentParent} in the specified + * {@param windowingMode} and {@param activityType} to {@param newParent} in their current + * z-order. + * + * @param currentParent of the tasks to perform the operation no. + * {@code null} will perform the operation on the display. + * @param newParent for the tasks. {@code null} will perform the operation on the display. + * @param windowingModes of the tasks to reparent. + * @param activityTypes of the tasks to reparent. + * @param onTop When {@code true}, the child goes to the top of parent; otherwise it goes to + * the bottom. + */ + @NonNull + public WindowContainerTransaction reparentTasks(@Nullable WindowContainerToken currentParent, + @Nullable WindowContainerToken newParent, @Nullable int[] windowingModes, + @Nullable int[] activityTypes, boolean onTop) { + mHierarchyOps.add(HierarchyOp.createForChildrenTasksReparent( + currentParent != null ? currentParent.asBinder() : null, + newParent != null ? newParent.asBinder() : null, + windowingModes, + activityTypes, + onTop)); + return this; + } + + /** + * Sets whether a container should be the launch root for the specified windowing mode and + * activity type. This currently only applies to Task containers created by organizer. + */ + @NonNull + public WindowContainerTransaction setLaunchRoot(@NonNull WindowContainerToken container, + @Nullable int[] windowingModes, @Nullable int[] activityTypes) { + mHierarchyOps.add(HierarchyOp.createForSetLaunchRoot( + container.asBinder(), + windowingModes, + activityTypes)); return this; } @@ -363,6 +405,7 @@ public final class WindowContainerTransaction implements Parcelable { private boolean mFocusable = true; private boolean mHidden = false; private boolean mIgnoreOrientationRequest = false; + private int mChangeMask = 0; private @ActivityInfo.Config int mConfigSetMask = 0; private @WindowConfiguration.WindowConfig int mWindowSetMask = 0; @@ -595,6 +638,14 @@ public final class WindowContainerTransaction implements Parcelable { * @hide */ public static class HierarchyOp implements Parcelable { + public static final int HIERARCHY_OP_TYPE_REPARENT = 0; + public static final int HIERARCHY_OP_TYPE_REORDER = 1; + public static final int HIERARCHY_OP_TYPE_CHILDREN_TASKS_REPARENT = 2; + public static final int HIERARCHY_OP_TYPE_SET_LAUNCH_ROOT = 3; + + private final int mType; + + // Container we are performing the operation on. private final IBinder mContainer; // If this is same as mContainer, then only change position, don't reparent. @@ -603,32 +654,68 @@ public final class WindowContainerTransaction implements Parcelable { // Moves/reparents to top of parent when {@code true}, otherwise moves/reparents to bottom. private final boolean mToTop; - public HierarchyOp(@NonNull IBinder container, @Nullable IBinder reparent, boolean toTop) { - mContainer = container; - mReparent = reparent; - mToTop = toTop; + final private int[] mWindowingModes; + final private int[] mActivityTypes; + + public static HierarchyOp createForReparent( + @NonNull IBinder container, @Nullable IBinder reparent, boolean toTop) { + return new HierarchyOp(HIERARCHY_OP_TYPE_REPARENT, + container, reparent, null, null, toTop); } - public HierarchyOp(@NonNull IBinder container, boolean toTop) { + public static HierarchyOp createForReorder(@NonNull IBinder container, boolean toTop) { + return new HierarchyOp(HIERARCHY_OP_TYPE_REORDER, + container, container, null, null, toTop); + } + + public static HierarchyOp createForChildrenTasksReparent(IBinder currentParent, + IBinder newParent, int[] windowingModes, int[] activityTypes, boolean onTop) { + return new HierarchyOp(HIERARCHY_OP_TYPE_CHILDREN_TASKS_REPARENT, + currentParent, newParent, windowingModes, activityTypes, onTop); + } + + public static HierarchyOp createForSetLaunchRoot(IBinder container, + int[] windowingModes, int[] activityTypes) { + return new HierarchyOp(HIERARCHY_OP_TYPE_SET_LAUNCH_ROOT, + container, null, windowingModes, activityTypes, false); + } + + private HierarchyOp(int type, @NonNull IBinder container, @Nullable IBinder reparent, + int[] windowingModes, int[] activityTypes, boolean toTop) { + mType = type; mContainer = container; - mReparent = container; + mReparent = reparent; + mWindowingModes = windowingModes != null ? + Arrays.copyOf(windowingModes, windowingModes.length) : null; + mActivityTypes = activityTypes != null ? + Arrays.copyOf(activityTypes, activityTypes.length) : null; mToTop = toTop; } public HierarchyOp(@NonNull HierarchyOp copy) { + mType = copy.mType; mContainer = copy.mContainer; mReparent = copy.mReparent; mToTop = copy.mToTop; + mWindowingModes = copy.mWindowingModes; + mActivityTypes = copy.mActivityTypes; } protected HierarchyOp(Parcel in) { + mType = in.readInt(); mContainer = in.readStrongBinder(); mReparent = in.readStrongBinder(); mToTop = in.readBoolean(); + mWindowingModes = in.createIntArray(); + mActivityTypes = in.createIntArray(); + } + + public int getType() { + return mType; } public boolean isReparent() { - return mContainer != mReparent; + return mType == HIERARCHY_OP_TYPE_REPARENT; } @Nullable @@ -645,21 +732,45 @@ public final class WindowContainerTransaction implements Parcelable { return mToTop; } + public int[] getWindowingModes() { + return mWindowingModes; + } + + public int[] getActivityTypes() { + return mActivityTypes; + } + @Override public String toString() { - if (isReparent()) { - return "{reparent: " + mContainer + " to " + (mToTop ? "top of " : "bottom of ") - + mReparent + "}"; - } else { - return "{reorder: " + mContainer + " to " + (mToTop ? "top" : "bottom") + "}"; + switch (mType) { + case HIERARCHY_OP_TYPE_CHILDREN_TASKS_REPARENT: + return "{ChildrenTasksReparent: from=" + mContainer + " to=" + mReparent + + " mToTop=" + mToTop + " mWindowingMode=" + mWindowingModes + + " mActivityType=" + mActivityTypes + "}"; + case HIERARCHY_OP_TYPE_SET_LAUNCH_ROOT: + return "{SetLaunchRoot: container=" + mContainer + + " mWindowingMode=" + mWindowingModes + + " mActivityType=" + mActivityTypes + "}"; + case HIERARCHY_OP_TYPE_REPARENT: + return "{reparent: " + mContainer + " to " + (mToTop ? "top of " : "bottom of ") + + mReparent + "}"; + case HIERARCHY_OP_TYPE_REORDER: + return "{reorder: " + mContainer + " to " + (mToTop ? "top" : "bottom") + "}"; + default: + return "{mType=" + mType + " container=" + mContainer + " reparent=" + mReparent + + " mToTop=" + mToTop + " mWindowingMode=" + mWindowingModes + + " mActivityType=" + mActivityTypes + "}"; } } @Override public void writeToParcel(Parcel dest, int flags) { + dest.writeInt(mType); dest.writeStrongBinder(mContainer); dest.writeStrongBinder(mReparent); dest.writeBoolean(mToTop); + dest.writeIntArray(mWindowingModes); + dest.writeIntArray(mActivityTypes); } @Override diff --git a/core/java/com/android/internal/app/ChooserActivity.java b/core/java/com/android/internal/app/ChooserActivity.java index e06413783fe4..34e03af4c666 100644 --- a/core/java/com/android/internal/app/ChooserActivity.java +++ b/core/java/com/android/internal/app/ChooserActivity.java @@ -431,19 +431,22 @@ public class ChooserActivity extends ResolverActivity implements } private void maybeHideContentPreview() { - if (!mAtLeastOneLoaded && mHideParentOnFail) { - Log.i(TAG, "Hiding image preview area. Timed out waiting for preview to load" - + " within " + mImageLoadTimeoutMillis + "ms."); - collapseParentView(); - if (shouldShowTabs()) { - hideStickyContentPreview(); - } else if (mChooserMultiProfilePagerAdapter.getCurrentRootAdapter() != null) { - mChooserMultiProfilePagerAdapter.getCurrentRootAdapter().hideContentPreview(); + if (!mAtLeastOneLoaded) { + if (mHideParentOnFail) { + Log.i(TAG, "Hiding image preview area. Timed out waiting for preview to load" + + " within " + mImageLoadTimeoutMillis + "ms."); + collapseParentView(); + if (shouldShowTabs()) { + hideStickyContentPreview(); + } else if (mChooserMultiProfilePagerAdapter.getCurrentRootAdapter() != null) { + mChooserMultiProfilePagerAdapter.getCurrentRootAdapter() + .hideContentPreview(); + } + mHideParentOnFail = false; } - mHideParentOnFail = false; + mRemoveSharedElements = true; + startPostponedEnterTransition(); } - mRemoveSharedElements = true; - startPostponedEnterTransition(); } private void collapseParentView() { diff --git a/core/java/com/android/internal/compat/CompatibilityChangeInfo.java b/core/java/com/android/internal/compat/CompatibilityChangeInfo.java index 670ca9f6091e..03fe4551c249 100644 --- a/core/java/com/android/internal/compat/CompatibilityChangeInfo.java +++ b/core/java/com/android/internal/compat/CompatibilityChangeInfo.java @@ -32,6 +32,7 @@ public class CompatibilityChangeInfo implements Parcelable { private final boolean mDisabled; private final boolean mLoggingOnly; private final @Nullable String mDescription; + private final boolean mOverridable; public long getId() { return mChangeId; @@ -58,9 +59,13 @@ public class CompatibilityChangeInfo implements Parcelable { return mDescription; } + public boolean getOverridable() { + return mOverridable; + } + public CompatibilityChangeInfo( Long changeId, String name, int enableAfterTargetSdk, int enableSinceTargetSdk, - boolean disabled, boolean loggingOnly, String description) { + boolean disabled, boolean loggingOnly, String description, boolean overridable) { this.mChangeId = changeId; this.mName = name; if (enableAfterTargetSdk > 0) { @@ -75,6 +80,7 @@ public class CompatibilityChangeInfo implements Parcelable { this.mDisabled = disabled; this.mLoggingOnly = loggingOnly; this.mDescription = description; + this.mOverridable = overridable; } public CompatibilityChangeInfo(CompatibilityChangeInfo other) { @@ -84,6 +90,7 @@ public class CompatibilityChangeInfo implements Parcelable { this.mDisabled = other.mDisabled; this.mLoggingOnly = other.mLoggingOnly; this.mDescription = other.mDescription; + this.mOverridable = other.mOverridable; } private CompatibilityChangeInfo(Parcel in) { @@ -93,6 +100,7 @@ public class CompatibilityChangeInfo implements Parcelable { mDisabled = in.readBoolean(); mLoggingOnly = in.readBoolean(); mDescription = in.readString(); + mOverridable = in.readBoolean(); } @Override @@ -108,6 +116,7 @@ public class CompatibilityChangeInfo implements Parcelable { dest.writeBoolean(mDisabled); dest.writeBoolean(mLoggingOnly); dest.writeString(mDescription); + dest.writeBoolean(mOverridable); } @Override @@ -126,6 +135,9 @@ public class CompatibilityChangeInfo implements Parcelable { if (getLoggingOnly()) { sb.append("; loggingOnly"); } + if (getOverridable()) { + sb.append("; overridable"); + } return sb.append(")").toString(); } @@ -143,8 +155,8 @@ public class CompatibilityChangeInfo implements Parcelable { && this.mEnableSinceTargetSdk == that.mEnableSinceTargetSdk && this.mDisabled == that.mDisabled && this.mLoggingOnly == that.mLoggingOnly - && this.mDescription.equals(that.mDescription); - + && this.mDescription.equals(that.mDescription) + && this.mOverridable == that.mOverridable; } public static final Parcelable.Creator<CompatibilityChangeInfo> CREATOR = diff --git a/core/java/com/android/internal/os/IDropBoxManagerService.aidl b/core/java/com/android/internal/os/IDropBoxManagerService.aidl index 9141719d7a35..38a7203ead4a 100644 --- a/core/java/com/android/internal/os/IDropBoxManagerService.aidl +++ b/core/java/com/android/internal/os/IDropBoxManagerService.aidl @@ -17,6 +17,7 @@ package com.android.internal.os; import android.os.DropBoxManager; +import android.os.ParcelFileDescriptor; /** * "Backend" interface used by {@link android.os.DropBoxManager} to talk to the @@ -26,12 +27,8 @@ import android.os.DropBoxManager; * @hide */ interface IDropBoxManagerService { - /** - * @see DropBoxManager#addText - * @see DropBoxManager#addData - * @see DropBoxManager#addFile - */ - void add(in DropBoxManager.Entry entry); + void addData(String tag, in byte[] data, int flags); + void addFile(String tag, in ParcelFileDescriptor fd, int flags); /** @see DropBoxManager#getNextEntry */ boolean isTagEnabled(String tag); diff --git a/core/java/com/android/server/net/BaseNetworkObserver.java b/core/java/com/android/server/net/BaseNetworkObserver.java index 93f89b5db820..139b88b108c5 100644 --- a/core/java/com/android/server/net/BaseNetworkObserver.java +++ b/core/java/com/android/server/net/BaseNetworkObserver.java @@ -64,7 +64,7 @@ public class BaseNetworkObserver extends INetworkManagementEventObserver.Stub { } @Override - public void interfaceClassDataActivityChanged(int networkType, boolean active, long tsNanos, + public void interfaceClassDataActivityChanged(int transportType, boolean active, long tsNanos, int uid) { // default no-op } diff --git a/core/jni/com_android_internal_os_Zygote.cpp b/core/jni/com_android_internal_os_Zygote.cpp index 3156f71fa113..2ff474b140e0 100644 --- a/core/jni/com_android_internal_os_Zygote.cpp +++ b/core/jni/com_android_internal_os_Zygote.cpp @@ -14,15 +14,6 @@ * limitations under the License. */ -/* - * Disable optimization of this file if we are compiling with the address - * sanitizer. This is a mitigation for b/122921367 and can be removed once the - * bug is fixed. - */ -#if __has_feature(address_sanitizer) -#pragma clang optimize off -#endif - #define LOG_TAG "Zygote" #define ATRACE_TAG ATRACE_TAG_DALVIK diff --git a/core/proto/android/server/biometrics.proto b/core/proto/android/server/biometrics.proto index 632c1e5d419e..14b5c52acce4 100644 --- a/core/proto/android/server/biometrics.proto +++ b/core/proto/android/server/biometrics.proto @@ -106,16 +106,25 @@ message SensorServiceStateProto { // State of a single sensor. message SensorStateProto { + enum Modality { + UNKNOWN = 0; + FINGERPRINT = 1; + FACE = 2; + IRIS = 3; + } + option (.android.msg_privacy).dest = DEST_AUTOMATIC; // Unique sensorId optional int32 sensor_id = 1; + optional Modality modality = 2; + // State of the sensor's scheduler. True if currently handling an operation, false if idle. - optional bool is_busy = 2; + optional bool is_busy = 3; // User states for this sensor. - repeated UserStateProto user_states = 3; + repeated UserStateProto user_states = 4; } // State of a specific user for a specific sensor. diff --git a/core/res/AndroidManifest.xml b/core/res/AndroidManifest.xml index 30535185bdae..ce3ed9dc8ba6 100644 --- a/core/res/AndroidManifest.xml +++ b/core/res/AndroidManifest.xml @@ -2676,11 +2676,11 @@ The app can check whether it has this authorization by calling {@link android.provider.Settings#canDrawOverlays Settings.canDrawOverlays()}. - <p>Protection level: signature|preinstalled|appop|pre23|development --> + <p>Protection level: signature|appop|installer|pre23|development --> <permission android:name="android.permission.SYSTEM_ALERT_WINDOW" android:label="@string/permlab_systemAlertWindow" android:description="@string/permdesc_systemAlertWindow" - android:protectionLevel="signature|preinstalled|appop|pre23|development" /> + android:protectionLevel="signature|appop|installer|pre23|development" /> <!-- @deprecated Use {@link android.Manifest.permission#REQUEST_COMPANION_RUN_IN_BACKGROUND} @hide @@ -3591,6 +3591,14 @@ <permission android:name="android.permission.BIND_CONTENT_CAPTURE_SERVICE" android:protectionLevel="signature" /> + <!-- Must be required by a android.service.translation.TranslationService, + to ensure that only the system can bind to it. + @SystemApi @hide This is not a third-party API (intended for OEMs and system apps). + <p>Protection level: signature + --> + <permission android:name="android.permission.BIND_TRANSLATION_SERVICE" + android:protectionLevel="signature" /> + <!-- Must be required by a android.service.contentsuggestions.ContentSuggestionsService, to ensure that only the system can bind to it. @SystemApi @hide This is not a third-party API (intended for OEMs and system apps). diff --git a/core/res/res/values/attrs.xml b/core/res/res/values/attrs.xml index 7ca3fafdca47..ef54db1a422c 100644 --- a/core/res/res/values/attrs.xml +++ b/core/res/res/values/attrs.xml @@ -8292,6 +8292,23 @@ </declare-styleable> <!-- =============================== --> + <!-- Translation attributes --> + <!-- =============================== --> + <eat-comment /> + + <!-- Use <code>translation-service</code> as the root tag of the XML resource that describes + a {@link android.service.translation.TranslationService}, which is referenced from + its {@link android.service.translation.TranslationService#SERVICE_META_DATA} meta-data + entry. + @hide @SystemApi + --> + <declare-styleable name="TranslationService"> + <!-- Fully qualified class name of an activity that allows the user to modify + the settings for this service. --> + <attr name="settingsActivity" /> + </declare-styleable> + + <!-- =============================== --> <!-- Contacts meta-data attributes --> <!-- =============================== --> <eat-comment /> diff --git a/core/res/res/values/config.xml b/core/res/res/values/config.xml index da658cc3d525..487847a0b075 100644 --- a/core/res/res/values/config.xml +++ b/core/res/res/values/config.xml @@ -450,6 +450,10 @@ --> </string-array> + <!-- Whether the internal vehicle network should remain active even when no + apps requested it. --> + <bool name="config_vehicleInternalNetworkAlwaysRequested">false</bool> + <!-- Configuration of network interfaces that support WakeOnLAN --> <string-array translatable="false" name="config_wakeonlan_supported_interfaces"> <!-- @@ -3724,6 +3728,14 @@ --> <string name="config_defaultAugmentedAutofillService" translatable="false"></string> + <!-- The package name for the system's translation service. + This service must be trusted, as it can be activated without explicit consent of the user. + If no service with the specified name exists on the device, translation wil be + disabled. + Example: "com.android.translation/.TranslationService" +--> + <string name="config_defaultTranslationService" translatable="false"></string> + <!-- The package name for the system's app prediction service. This service must be trusted, as it can be activated without explicit consent of the user. Example: "com.android.intelligence/.AppPredictionService" diff --git a/core/res/res/values/strings.xml b/core/res/res/values/strings.xml index 3a593b93346d..5715b31b94bc 100644 --- a/core/res/res/values/strings.xml +++ b/core/res/res/values/strings.xml @@ -292,6 +292,17 @@ <!-- WFC, summary for Wi-Fi Only --> <string name="wfc_mode_wifi_only_summary">Wi-Fi only</string> + <!-- Template for showing mobile network operator name while Cross SIM calling is active --> + <string-array name="crossSimSpnFormats" translatable="false"> + <item>@string/crossSimFormat_spn</item> + <item>@string/crossSimFormat_spn_cross_sim_calling</item> + </string-array> + + <!-- Spn during Cross-SIM Calling: "<operator> " [CHAR LIMIT=NONE] --> + <string name="crossSimFormat_spn"><xliff:g id="spn" example="Operator">%s</xliff:g></string> + <!-- Spn during Cross SIM Calling: "<operator> Cross-SIM Calling" [CHAR LIMIT=NONE] --> + <string name="crossSimFormat_spn_cross_sim_calling"><xliff:g id="spn" example="Operator">%s</xliff:g> Cross-SIM Calling</string> + <!-- {0} is one of "bearerServiceCode*" {1} is dialing number diff --git a/core/res/res/values/symbols.xml b/core/res/res/values/symbols.xml index 31b4edd4e6e8..96ebdaa84698 100644 --- a/core/res/res/values/symbols.xml +++ b/core/res/res/values/symbols.xml @@ -684,6 +684,7 @@ <java-symbol type="string" name="config_ethernet_iface_regex" /> <java-symbol type="string" name="not_checked" /> <java-symbol type="array" name="config_ethernet_interfaces" /> + <java-symbol type="bool" name="config_vehicleInternalNetworkAlwaysRequested" /> <java-symbol type="array" name="config_wakeonlan_supported_interfaces" /> <java-symbol type="string" name="config_forceVoiceInteractionServicePackage" /> <java-symbol type="string" name="config_mms_user_agent" /> @@ -874,6 +875,7 @@ <java-symbol type="string" name="wfc_mode_wifi_preferred_summary" /> <java-symbol type="string" name="wfc_mode_cellular_preferred_summary" /> <java-symbol type="string" name="wfc_mode_wifi_only_summary" /> + <java-symbol type="array" name="crossSimSpnFormats" /> <java-symbol type="string" name="policydesc_disableCamera" /> <java-symbol type="string" name="policydesc_encryptedStorage" /> <java-symbol type="string" name="policydesc_expirePassword" /> @@ -3484,6 +3486,7 @@ <java-symbol type="string" name="config_defaultWellbeingPackage" /> <java-symbol type="string" name="config_defaultContentCaptureService" /> <java-symbol type="string" name="config_defaultAugmentedAutofillService" /> + <java-symbol type="string" name="config_defaultTranslationService" /> <java-symbol type="string" name="config_defaultAppPredictionService" /> <java-symbol type="string" name="config_defaultContentSuggestionsService" /> <java-symbol type="string" name="config_defaultSearchUiService" /> diff --git a/core/tests/overlaytests/device/Android.bp b/core/tests/overlaytests/device/Android.bp index f86ac9ce37e1..12a2b0815050 100644 --- a/core/tests/overlaytests/device/Android.bp +++ b/core/tests/overlaytests/device/Android.bp @@ -16,11 +16,7 @@ android_test { name: "OverlayDeviceTests", srcs: ["src/**/*.java"], platform_apis: true, - certificate: "platform", - static_libs: [ - "androidx.test.rules", - "testng", - ], + static_libs: ["androidx.test.rules"], test_suites: ["device-tests"], data: [ ":OverlayDeviceTests_AppOverlayOne", diff --git a/core/tests/overlaytests/device/AndroidManifest.xml b/core/tests/overlaytests/device/AndroidManifest.xml index a69911f8d827..4881636c7095 100644 --- a/core/tests/overlaytests/device/AndroidManifest.xml +++ b/core/tests/overlaytests/device/AndroidManifest.xml @@ -19,8 +19,6 @@ <uses-sdk android:minSdkVersion="21" /> - <uses-permission android:name="android.permission.CHANGE_OVERLAY_PACKAGES" /> - <application> <uses-library android:name="android.test.runner"/> </application> diff --git a/core/tests/overlaytests/device/AndroidTest.xml b/core/tests/overlaytests/device/AndroidTest.xml index db45750ff6dc..6507839a4288 100644 --- a/core/tests/overlaytests/device/AndroidTest.xml +++ b/core/tests/overlaytests/device/AndroidTest.xml @@ -19,20 +19,9 @@ <option name="test-suite-tag" value="apct" /> <option name="test-suite-tag" value="apct-instrumentation" /> - <target_preparer class="com.android.compatibility.common.tradefed.targetprep.FilePusher"> - <option name="cleanup" value="true" /> - <option name="remount-system" value="true" /> - <option name="push" value="OverlayDeviceTests.apk->/system/app/OverlayDeviceTests.apk" /> - </target_preparer> - - <!-- Reboot to have the test APK scanned by PM and reboot after to remove the test APK. --> - <target_preparer class="com.android.tradefed.targetprep.RebootTargetPreparer"> - <option name="pre-reboot" value="true" /> - <option name="post-reboot" value="true" /> - </target_preparer> - <target_preparer class="com.android.tradefed.targetprep.TestAppInstallSetup"> <option name="cleanup-apks" value="true" /> + <option name="test-file-name" value="OverlayDeviceTests.apk" /> <option name="test-file-name" value="OverlayDeviceTests_AppOverlayOne.apk" /> <option name="test-file-name" value="OverlayDeviceTests_AppOverlayTwo.apk" /> <option name="test-file-name" value="OverlayDeviceTests_FrameworkOverlay.apk" /> diff --git a/core/tests/overlaytests/device/src/com/android/overlaytest/LocalOverlayManager.java b/core/tests/overlaytests/device/src/com/android/overlaytest/LocalOverlayManager.java index 76c01a7e1125..390bb766ab81 100644 --- a/core/tests/overlaytests/device/src/com/android/overlaytest/LocalOverlayManager.java +++ b/core/tests/overlaytests/device/src/com/android/overlaytest/LocalOverlayManager.java @@ -18,76 +18,60 @@ package com.android.overlaytest; import static java.util.concurrent.TimeUnit.SECONDS; -import android.annotation.NonNull; -import android.content.Context; -import android.content.om.OverlayManager; -import android.content.om.OverlayManagerTransaction; -import android.os.UserHandle; +import android.app.UiAutomation; +import android.content.res.Resources; +import android.os.ParcelFileDescriptor; import androidx.test.InstrumentationRegistry; +import java.io.BufferedReader; +import java.io.InputStream; +import java.io.InputStreamReader; +import java.nio.charset.StandardCharsets; +import java.util.Arrays; import java.util.concurrent.Executor; import java.util.concurrent.FutureTask; class LocalOverlayManager { private static final long TIMEOUT = 30; - public static void toggleOverlaysAndWait(@NonNull final String[] overlaysToEnable, - @NonNull final String[] overlaysToDisable) throws Exception { - final int userId = UserHandle.myUserId(); - OverlayManagerTransaction.Builder builder = new OverlayManagerTransaction.Builder(); - for (String pkg : overlaysToEnable) { - builder.setEnabled(pkg, true, userId); + public static void setEnabledAndWait(Executor executor, final String packageName, + boolean enable) throws Exception { + final String pattern = (enable ? "[x]" : "[ ]") + " " + packageName; + if (executeShellCommand("cmd overlay list").contains(pattern)) { + // nothing to do, overlay already in the requested state + return; } - for (String pkg : overlaysToDisable) { - builder.setEnabled(pkg, false, userId); - } - OverlayManagerTransaction transaction = builder.build(); - final Context ctx = InstrumentationRegistry.getTargetContext(); + final Resources res = InstrumentationRegistry.getContext().getResources(); + final String[] oldApkPaths = res.getAssets().getApkPaths(); FutureTask<Boolean> task = new FutureTask<>(() -> { while (true) { - final String[] paths = ctx.getResources().getAssets().getApkPaths(); - if (arrayTailContains(paths, overlaysToEnable) - && arrayDoesNotContain(paths, overlaysToDisable)) { + if (!Arrays.equals(oldApkPaths, res.getAssets().getApkPaths())) { return true; } Thread.sleep(10); } }); - - OverlayManager om = ctx.getSystemService(OverlayManager.class); - om.commit(transaction); - - Executor executor = (cmd) -> new Thread(cmd).start(); executor.execute(task); + executeShellCommand("cmd overlay " + (enable ? "enable " : "disable ") + packageName); task.get(TIMEOUT, SECONDS); } - private static boolean arrayTailContains(@NonNull final String[] array, - @NonNull final String[] substrings) { - if (array.length < substrings.length) { - return false; - } - for (int i = 0; i < substrings.length; i++) { - String a = array[array.length - substrings.length + i]; - String s = substrings[i]; - if (!a.contains(s)) { - return false; - } - } - return true; - } - - private static boolean arrayDoesNotContain(@NonNull final String[] array, - @NonNull final String[] substrings) { - for (String s : substrings) { - for (String a : array) { - if (a.contains(s)) { - return false; - } + private static String executeShellCommand(final String command) + throws Exception { + final UiAutomation uiAutomation = + InstrumentationRegistry.getInstrumentation().getUiAutomation(); + final ParcelFileDescriptor pfd = uiAutomation.executeShellCommand(command); + try (InputStream in = new ParcelFileDescriptor.AutoCloseInputStream(pfd)) { + final BufferedReader reader = new BufferedReader( + new InputStreamReader(in, StandardCharsets.UTF_8)); + StringBuilder str = new StringBuilder(); + String line; + while ((line = reader.readLine()) != null) { + str.append(line); } + return str.toString(); } - return true; } } diff --git a/core/tests/overlaytests/device/src/com/android/overlaytest/TransactionTest.java b/core/tests/overlaytests/device/src/com/android/overlaytest/TransactionTest.java deleted file mode 100644 index 0b4f5e227169..000000000000 --- a/core/tests/overlaytests/device/src/com/android/overlaytest/TransactionTest.java +++ /dev/null @@ -1,133 +0,0 @@ -/* - * Copyright (C) 2019 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package com.android.overlaytest; - -import static org.junit.Assert.assertEquals; -import static org.junit.Assert.assertNotNull; -import static org.testng.Assert.assertThrows; - -import android.content.Context; -import android.content.om.OverlayInfo; -import android.content.om.OverlayManager; -import android.content.om.OverlayManagerTransaction; -import android.content.res.Resources; -import android.os.UserHandle; - -import androidx.test.InstrumentationRegistry; -import androidx.test.filters.MediumTest; - -import org.junit.Before; -import org.junit.Test; -import org.junit.runner.RunWith; -import org.junit.runners.JUnit4; - -import java.util.List; - -@RunWith(JUnit4.class) -@MediumTest -public class TransactionTest { - static final String APP_OVERLAY_ONE_PKG = "com.android.overlaytest.app_overlay_one"; - static final String APP_OVERLAY_TWO_PKG = "com.android.overlaytest.app_overlay_two"; - - private Context mContext; - private Resources mResources; - private OverlayManager mOverlayManager; - private int mUserId; - private UserHandle mUserHandle; - - @Before - public void setUp() throws Exception { - mContext = InstrumentationRegistry.getContext(); - mResources = mContext.getResources(); - mOverlayManager = mContext.getSystemService(OverlayManager.class); - mUserId = UserHandle.myUserId(); - mUserHandle = UserHandle.of(mUserId); - - LocalOverlayManager.toggleOverlaysAndWait( - new String[]{}, - new String[]{APP_OVERLAY_ONE_PKG, APP_OVERLAY_TWO_PKG}); - } - - @Test - public void testValidTransaction() throws Exception { - assertOverlayIsEnabled(APP_OVERLAY_ONE_PKG, false, mUserId); - assertOverlayIsEnabled(APP_OVERLAY_TWO_PKG, false, mUserId); - - OverlayManagerTransaction t = new OverlayManagerTransaction.Builder() - .setEnabled(APP_OVERLAY_ONE_PKG, true) - .setEnabled(APP_OVERLAY_TWO_PKG, true) - .build(); - mOverlayManager.commit(t); - - assertOverlayIsEnabled(APP_OVERLAY_ONE_PKG, true, mUserId); - assertOverlayIsEnabled(APP_OVERLAY_TWO_PKG, true, mUserId); - List<OverlayInfo> ois = - mOverlayManager.getOverlayInfosForTarget("com.android.overlaytest", mUserHandle); - assertEquals(ois.size(), 2); - assertEquals(ois.get(0).packageName, APP_OVERLAY_ONE_PKG); - assertEquals(ois.get(1).packageName, APP_OVERLAY_TWO_PKG); - - OverlayManagerTransaction t2 = new OverlayManagerTransaction.Builder() - .setEnabled(APP_OVERLAY_TWO_PKG, true) - .setEnabled(APP_OVERLAY_ONE_PKG, true) - .build(); - mOverlayManager.commit(t2); - - assertOverlayIsEnabled(APP_OVERLAY_ONE_PKG, true, mUserId); - assertOverlayIsEnabled(APP_OVERLAY_TWO_PKG, true, mUserId); - List<OverlayInfo> ois2 = - mOverlayManager.getOverlayInfosForTarget("com.android.overlaytest", mUserHandle); - assertEquals(ois2.size(), 2); - assertEquals(ois2.get(0).packageName, APP_OVERLAY_TWO_PKG); - assertEquals(ois2.get(1).packageName, APP_OVERLAY_ONE_PKG); - - OverlayManagerTransaction t3 = new OverlayManagerTransaction.Builder() - .setEnabled(APP_OVERLAY_TWO_PKG, false) - .build(); - mOverlayManager.commit(t3); - - assertOverlayIsEnabled(APP_OVERLAY_ONE_PKG, true, mUserId); - assertOverlayIsEnabled(APP_OVERLAY_TWO_PKG, false, mUserId); - List<OverlayInfo> ois3 = - mOverlayManager.getOverlayInfosForTarget("com.android.overlaytest", mUserHandle); - assertEquals(ois3.size(), 2); - assertEquals(ois3.get(0).packageName, APP_OVERLAY_TWO_PKG); - assertEquals(ois3.get(1).packageName, APP_OVERLAY_ONE_PKG); - } - - @Test - public void testInvalidRequestHasNoEffect() { - assertOverlayIsEnabled(APP_OVERLAY_ONE_PKG, false, mUserId); - assertOverlayIsEnabled(APP_OVERLAY_TWO_PKG, false, mUserId); - - OverlayManagerTransaction t = new OverlayManagerTransaction.Builder() - .setEnabled(APP_OVERLAY_ONE_PKG, true) - .setEnabled("does-not-exist", true) - .setEnabled(APP_OVERLAY_TWO_PKG, true) - .build(); - assertThrows(SecurityException.class, () -> mOverlayManager.commit(t)); - - assertOverlayIsEnabled(APP_OVERLAY_ONE_PKG, false, mUserId); - assertOverlayIsEnabled(APP_OVERLAY_TWO_PKG, false, mUserId); - } - - private void assertOverlayIsEnabled(final String packageName, boolean enabled, int userId) { - final OverlayInfo oi = mOverlayManager.getOverlayInfo(packageName, UserHandle.of(userId)); - assertNotNull(oi); - assertEquals(oi.isEnabled(), enabled); - } -} diff --git a/core/tests/overlaytests/device/src/com/android/overlaytest/WithMultipleOverlaysTest.java b/core/tests/overlaytests/device/src/com/android/overlaytest/WithMultipleOverlaysTest.java index 420f755c5251..d28c47d9c922 100644 --- a/core/tests/overlaytests/device/src/com/android/overlaytest/WithMultipleOverlaysTest.java +++ b/core/tests/overlaytests/device/src/com/android/overlaytest/WithMultipleOverlaysTest.java @@ -22,6 +22,8 @@ import org.junit.BeforeClass; import org.junit.runner.RunWith; import org.junit.runners.JUnit4; +import java.util.concurrent.Executor; + @RunWith(JUnit4.class) @MediumTest public class WithMultipleOverlaysTest extends OverlayBaseTest { @@ -31,8 +33,9 @@ public class WithMultipleOverlaysTest extends OverlayBaseTest { @BeforeClass public static void enableOverlay() throws Exception { - LocalOverlayManager.toggleOverlaysAndWait( - new String[]{FRAMEWORK_OVERLAY_PKG, APP_OVERLAY_ONE_PKG, APP_OVERLAY_TWO_PKG}, - new String[]{}); + Executor executor = (cmd) -> new Thread(cmd).start(); + LocalOverlayManager.setEnabledAndWait(executor, APP_OVERLAY_ONE_PKG, true); + LocalOverlayManager.setEnabledAndWait(executor, APP_OVERLAY_TWO_PKG, true); + LocalOverlayManager.setEnabledAndWait(executor, FRAMEWORK_OVERLAY_PKG, true); } } diff --git a/core/tests/overlaytests/device/src/com/android/overlaytest/WithOverlayTest.java b/core/tests/overlaytests/device/src/com/android/overlaytest/WithOverlayTest.java index a86255e96388..6566ad304c1c 100644 --- a/core/tests/overlaytests/device/src/com/android/overlaytest/WithOverlayTest.java +++ b/core/tests/overlaytests/device/src/com/android/overlaytest/WithOverlayTest.java @@ -22,6 +22,8 @@ import org.junit.BeforeClass; import org.junit.runner.RunWith; import org.junit.runners.JUnit4; +import java.util.concurrent.Executor; + @RunWith(JUnit4.class) @MediumTest public class WithOverlayTest extends OverlayBaseTest { @@ -30,9 +32,10 @@ public class WithOverlayTest extends OverlayBaseTest { } @BeforeClass - public static void enableOverlays() throws Exception { - LocalOverlayManager.toggleOverlaysAndWait( - new String[]{FRAMEWORK_OVERLAY_PKG, APP_OVERLAY_ONE_PKG}, - new String[]{APP_OVERLAY_TWO_PKG}); + public static void enableOverlay() throws Exception { + Executor executor = (cmd) -> new Thread(cmd).start(); + LocalOverlayManager.setEnabledAndWait(executor, APP_OVERLAY_ONE_PKG, true); + LocalOverlayManager.setEnabledAndWait(executor, APP_OVERLAY_TWO_PKG, false); + LocalOverlayManager.setEnabledAndWait(executor, FRAMEWORK_OVERLAY_PKG, true); } } diff --git a/core/tests/overlaytests/device/src/com/android/overlaytest/WithoutOverlayTest.java b/core/tests/overlaytests/device/src/com/android/overlaytest/WithoutOverlayTest.java index 51c411819b87..48cfeab2fbff 100644 --- a/core/tests/overlaytests/device/src/com/android/overlaytest/WithoutOverlayTest.java +++ b/core/tests/overlaytests/device/src/com/android/overlaytest/WithoutOverlayTest.java @@ -22,6 +22,8 @@ import org.junit.BeforeClass; import org.junit.runner.RunWith; import org.junit.runners.JUnit4; +import java.util.concurrent.Executor; + @RunWith(JUnit4.class) @MediumTest public class WithoutOverlayTest extends OverlayBaseTest { @@ -31,8 +33,9 @@ public class WithoutOverlayTest extends OverlayBaseTest { @BeforeClass public static void disableOverlays() throws Exception { - LocalOverlayManager.toggleOverlaysAndWait( - new String[]{}, - new String[]{FRAMEWORK_OVERLAY_PKG, APP_OVERLAY_ONE_PKG, APP_OVERLAY_TWO_PKG}); + Executor executor = (cmd) -> new Thread(cmd).start(); + LocalOverlayManager.setEnabledAndWait(executor, APP_OVERLAY_ONE_PKG, false); + LocalOverlayManager.setEnabledAndWait(executor, APP_OVERLAY_TWO_PKG, false); + LocalOverlayManager.setEnabledAndWait(executor, FRAMEWORK_OVERLAY_PKG, false); } } diff --git a/core/tests/overlaytests/device/test-apps/AppOverlayOne/Android.bp b/core/tests/overlaytests/device/test-apps/AppOverlayOne/Android.bp index 847b4915530b..da3aa007135a 100644 --- a/core/tests/overlaytests/device/test-apps/AppOverlayOne/Android.bp +++ b/core/tests/overlaytests/device/test-apps/AppOverlayOne/Android.bp @@ -15,6 +15,6 @@ android_test { name: "OverlayDeviceTests_AppOverlayOne", sdk_version: "current", - certificate: "platform", + aaptflags: ["--no-resource-removal"], } diff --git a/core/tests/overlaytests/device/test-apps/AppOverlayTwo/Android.bp b/core/tests/overlaytests/device/test-apps/AppOverlayTwo/Android.bp index 7d5f82a71b44..215b66da36dc 100644 --- a/core/tests/overlaytests/device/test-apps/AppOverlayTwo/Android.bp +++ b/core/tests/overlaytests/device/test-apps/AppOverlayTwo/Android.bp @@ -15,6 +15,6 @@ android_test { name: "OverlayDeviceTests_AppOverlayTwo", sdk_version: "current", - certificate: "platform", + aaptflags: ["--no-resource-removal"], } diff --git a/data/etc/OWNERS b/data/etc/OWNERS index 5efd0bd06b74..9867d810dba2 100644 --- a/data/etc/OWNERS +++ b/data/etc/OWNERS @@ -11,3 +11,5 @@ svetoslavganov@google.com toddke@android.com toddke@google.com yamasani@google.com + +per-file preinstalled-packages* = file:/MULTIUSER_OWNERS
\ No newline at end of file diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/ShellTaskOrganizer.java b/libs/WindowManager/Shell/src/com/android/wm/shell/ShellTaskOrganizer.java index faa4a0ed2294..c9b38d00c0ae 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/ShellTaskOrganizer.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/ShellTaskOrganizer.java @@ -262,12 +262,6 @@ public class ShellTaskOrganizer extends TaskOrganizer { synchronized (mLock) { ProtoLog.v(WM_SHELL_TASK_ORG, "Task info changed taskId=%d", taskInfo.taskId); final TaskAppearedInfo data = mTasks.get(taskInfo.taskId); - if (data == null) { - // TODO(b/171749427): It means onTaskInfoChanged send before onTaskAppeared or - // after onTaskVanished, it should be fixed in controller side. - return; - } - final TaskListener oldListener = getTaskListener(data.getTaskInfo()); final TaskListener newListener = getTaskListener(taskInfo); mTasks.put(taskInfo.taskId, new TaskAppearedInfo(taskInfo, data.getLeash())); diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/common/HandlerExecutor.java b/libs/WindowManager/Shell/src/com/android/wm/shell/common/HandlerExecutor.java index fa0a75c2d364..4874d3ccae7e 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/common/HandlerExecutor.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/common/HandlerExecutor.java @@ -18,6 +18,7 @@ package com.android.wm.shell.common; import android.annotation.NonNull; import android.os.Handler; +import android.os.Looper; /** Executor implementation which is backed by a Handler. */ public class HandlerExecutor implements ShellExecutor { @@ -49,4 +50,14 @@ public class HandlerExecutor implements ShellExecutor { public void removeCallbacks(@NonNull Runnable r) { mHandler.removeCallbacks(r); } + + @Override + public boolean hasCallback(Runnable r) { + return mHandler.hasCallbacks(r); + } + + @Override + public Looper getLooper() { + return mHandler.getLooper(); + } } diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/common/ShellExecutor.java b/libs/WindowManager/Shell/src/com/android/wm/shell/common/ShellExecutor.java index f2c57f71f5b8..d37e628b9a51 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/common/ShellExecutor.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/common/ShellExecutor.java @@ -16,6 +16,8 @@ package com.android.wm.shell.common; +import android.os.Looper; + import java.util.concurrent.CountDownLatch; import java.util.concurrent.Executor; import java.util.concurrent.TimeUnit; @@ -69,4 +71,14 @@ public interface ShellExecutor extends Executor { * See {@link android.os.Handler#removeCallbacks}. */ void removeCallbacks(Runnable r); + + /** + * See {@link android.os.Handler#hasCallbacks(Runnable)}. + */ + boolean hasCallback(Runnable r); + + /** + * Returns the looper that this executor is running on. + */ + Looper getLooper(); } diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/legacysplitscreen/WindowManagerProxy.java b/libs/WindowManager/Shell/src/com/android/wm/shell/legacysplitscreen/WindowManagerProxy.java index ad05e6d3e6c4..a8cd1dd4fbde 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/legacysplitscreen/WindowManagerProxy.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/legacysplitscreen/WindowManagerProxy.java @@ -18,7 +18,10 @@ package com.android.wm.shell.legacysplitscreen; import static android.app.WindowConfiguration.ACTIVITY_TYPE_HOME; import static android.app.WindowConfiguration.ACTIVITY_TYPE_RECENTS; +import static android.app.WindowConfiguration.ACTIVITY_TYPE_STANDARD; +import static android.app.WindowConfiguration.ACTIVITY_TYPE_UNDEFINED; import static android.app.WindowConfiguration.WINDOWING_MODE_FULLSCREEN; +import static android.app.WindowConfiguration.WINDOWING_MODE_SPLIT_SCREEN_SECONDARY; import static android.app.WindowConfiguration.WINDOWING_MODE_UNDEFINED; import static android.content.res.Configuration.ORIENTATION_LANDSCAPE; import static android.content.res.Configuration.ORIENTATION_UNDEFINED; @@ -54,6 +57,17 @@ class WindowManagerProxy { private static final String TAG = "WindowManagerProxy"; private static final int[] HOME_AND_RECENTS = {ACTIVITY_TYPE_HOME, ACTIVITY_TYPE_RECENTS}; + private static final int[] CONTROLLED_ACTIVITY_TYPES = { + ACTIVITY_TYPE_STANDARD, + ACTIVITY_TYPE_HOME, + ACTIVITY_TYPE_RECENTS, + ACTIVITY_TYPE_UNDEFINED + }; + private static final int[] CONTROLLED_WINDOWING_MODES = { + WINDOWING_MODE_FULLSCREEN, + WINDOWING_MODE_SPLIT_SCREEN_SECONDARY, + WINDOWING_MODE_UNDEFINED + }; @GuardedBy("mDockedRect") private final Rect mDockedRect = new Rect(); @@ -191,8 +205,9 @@ class WindowManagerProxy { // Set launchtile first so that any stack created after // getAllRootTaskInfos and before reparent (even if unlikely) are placed // correctly. - mTaskOrganizer.setLaunchRoot(DEFAULT_DISPLAY, tiles.mSecondary.token); WindowContainerTransaction wct = new WindowContainerTransaction(); + wct.setLaunchRoot(tiles.mSecondary.token, CONTROLLED_WINDOWING_MODES, + CONTROLLED_ACTIVITY_TYPES); final boolean isHomeResizable = buildEnterSplit(wct, tiles, layout); applySyncTransaction(wct); return isHomeResizable; @@ -251,12 +266,12 @@ class WindowManagerProxy { /** @see #buildDismissSplit */ void applyDismissSplit(LegacySplitScreenTaskListener tiles, LegacySplitDisplayLayout layout, boolean dismissOrMaximize) { - // Set launch root first so that any task created after getChildContainers and - // before reparent (pretty unlikely) are put into fullscreen. - mTaskOrganizer.setLaunchRoot(Display.DEFAULT_DISPLAY, null); // TODO(task-org): Once task-org is more complete, consider using Appeared/Vanished // plus specific APIs to clean this up. final WindowContainerTransaction wct = new WindowContainerTransaction(); + // Set launch root first so that any task created after getChildContainers and + // before reparent (pretty unlikely) are put into fullscreen. + wct.setLaunchRoot(tiles.mSecondary.token, null, null); buildDismissSplit(wct, tiles, layout, dismissOrMaximize); applySyncTransaction(wct); } diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/onehanded/OneHandedAnimationController.java b/libs/WindowManager/Shell/src/com/android/wm/shell/onehanded/OneHandedAnimationController.java index 146f231a8854..d22abe4dd19b 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/onehanded/OneHandedAnimationController.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/onehanded/OneHandedAnimationController.java @@ -127,7 +127,6 @@ public class OneHandedAnimationController { mSurfaceControlTransactionFactory; private @TransitionDirection int mTransitionDirection; - private int mTransitionOffset; private OneHandedTransitionAnimator(SurfaceControl leash, T startValue, T endValue) { mLeash = leash; @@ -231,11 +230,6 @@ public class OneHandedAnimationController { return this; } - OneHandedTransitionAnimator<T> setTransitionOffset(int offset) { - mTransitionOffset = offset; - return this; - } - T getStartValue() { return mStartValue; } 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 00605d872e39..48d6a7b40ee1 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 @@ -26,7 +26,6 @@ import android.content.om.OverlayInfo; import android.database.ContentObserver; import android.graphics.Point; import android.os.Handler; -import android.os.Looper; import android.os.RemoteException; import android.os.ServiceManager; import android.os.SystemProperties; @@ -41,8 +40,10 @@ import androidx.annotation.VisibleForTesting; import com.android.wm.shell.R; import com.android.wm.shell.common.DisplayChangeController; import com.android.wm.shell.common.DisplayController; +import com.android.wm.shell.common.ShellExecutor; import com.android.wm.shell.common.TaskStackListenerCallback; import com.android.wm.shell.common.TaskStackListenerImpl; +import com.android.wm.shell.common.annotations.ExternalThread; import com.android.wm.shell.onehanded.OneHandedGestureHandler.OneHandedGestureEventCallback; import java.io.PrintWriter; @@ -51,7 +52,7 @@ import java.util.concurrent.Executor; /** * Manages and manipulates the one handed states, transitions, and gesture for phones. */ -public class OneHandedController implements OneHanded { +public class OneHandedController { private static final String TAG = "OneHandedController"; private static final String ONE_HANDED_MODE_OFFSET_PERCENTAGE = @@ -61,8 +62,8 @@ public class OneHandedController implements OneHanded { static final String SUPPORT_ONE_HANDED_MODE = "ro.support_one_handed_mode"; - private boolean mIsOneHandedEnabled; - private boolean mIsSwipeToNotificationEnabled; + private volatile boolean mIsOneHandedEnabled; + private volatile boolean mIsSwipeToNotificationEnabled; private boolean mTaskChangeToExit; private float mOffSetFraction; @@ -73,7 +74,9 @@ public class OneHandedController implements OneHanded { private final OneHandedTouchHandler mTouchHandler; private final OneHandedTutorialHandler mTutorialHandler; private final IOverlayManager mOverlayManager; - private final Handler mMainHandler = new Handler(Looper.getMainLooper()); + private final ShellExecutor mMainExecutor; + private final Handler mMainHandler; + private final OneHandedImpl mImpl = new OneHandedImpl(); private OneHandedDisplayAreaOrganizer mDisplayAreaOrganizer; private final AccessibilityManager mAccessibilityManager; @@ -89,83 +92,10 @@ public class OneHandedController implements OneHanded { } }; - private final ContentObserver mEnabledObserver = new ContentObserver(mMainHandler) { - @Override - public void onChange(boolean selfChange) { - final boolean enabled = OneHandedSettingsUtil.getSettingsOneHandedModeEnabled( - mContext.getContentResolver()); - OneHandedEvents.writeEvent(enabled - ? OneHandedEvents.EVENT_ONE_HANDED_SETTINGS_ENABLED_ON - : OneHandedEvents.EVENT_ONE_HANDED_SETTINGS_ENABLED_OFF); - - setOneHandedEnabled(enabled); - - // Also checks swipe to notification settings since they all need gesture overlay. - setEnabledGesturalOverlay( - enabled || OneHandedSettingsUtil.getSettingsSwipeToNotificationEnabled( - mContext.getContentResolver())); - } - }; - - private final ContentObserver mTimeoutObserver = new ContentObserver(mMainHandler) { - @Override - public void onChange(boolean selfChange) { - final int newTimeout = OneHandedSettingsUtil.getSettingsOneHandedModeTimeout( - mContext.getContentResolver()); - int metricsId = OneHandedEvents.OneHandedSettingsTogglesEvent.INVALID.getId(); - switch (newTimeout) { - case OneHandedSettingsUtil.ONE_HANDED_TIMEOUT_NEVER: - metricsId = OneHandedEvents.EVENT_ONE_HANDED_SETTINGS_TIMEOUT_SECONDS_NEVER; - break; - case OneHandedSettingsUtil.ONE_HANDED_TIMEOUT_SHORT_IN_SECONDS: - metricsId = OneHandedEvents.EVENT_ONE_HANDED_SETTINGS_TIMEOUT_SECONDS_4; - break; - case OneHandedSettingsUtil.ONE_HANDED_TIMEOUT_MEDIUM_IN_SECONDS: - metricsId = OneHandedEvents.EVENT_ONE_HANDED_SETTINGS_TIMEOUT_SECONDS_8; - break; - case OneHandedSettingsUtil.ONE_HANDED_TIMEOUT_LONG_IN_SECONDS: - metricsId = OneHandedEvents.EVENT_ONE_HANDED_SETTINGS_TIMEOUT_SECONDS_12; - break; - default: - // do nothing - break; - } - OneHandedEvents.writeEvent(metricsId); - - if (mTimeoutHandler != null) { - mTimeoutHandler.setTimeout(newTimeout); - } - } - }; - - private final ContentObserver mTaskChangeExitObserver = new ContentObserver(mMainHandler) { - @Override - public void onChange(boolean selfChange) { - final boolean enabled = OneHandedSettingsUtil.getSettingsTapsAppToExit( - mContext.getContentResolver()); - OneHandedEvents.writeEvent(enabled - ? OneHandedEvents.EVENT_ONE_HANDED_SETTINGS_APP_TAPS_EXIT_ON - : OneHandedEvents.EVENT_ONE_HANDED_SETTINGS_APP_TAPS_EXIT_OFF); - - setTaskChangeToExit(enabled); - } - }; - - private final ContentObserver mSwipeToNotificationEnabledObserver = - new ContentObserver(mMainHandler) { - @Override - public void onChange(boolean selfChange) { - final boolean enabled = - OneHandedSettingsUtil.getSettingsSwipeToNotificationEnabled( - mContext.getContentResolver()); - setSwipeToNotificationEnabled(enabled); - - // Also checks one handed mode settings since they all need gesture overlay. - setEnabledGesturalOverlay( - enabled || OneHandedSettingsUtil.getSettingsOneHandedModeEnabled( - mContext.getContentResolver())); - } - }; + private final ContentObserver mEnabledObserver; + private final ContentObserver mTimeoutObserver; + private final ContentObserver mTaskChangeExitObserver; + private final ContentObserver mSwipeToNotificationEnabledObserver; private AccessibilityManager.AccessibilityStateChangeListener mAccessibilityStateChangeListener = @@ -188,33 +118,39 @@ public class OneHandedController implements OneHanded { }; /** - * Creates {@link OneHandedController}, returns {@code null} if the feature is not supported. + * Creates {@link OneHanded}, returns {@code null} if the feature is not supported. */ @Nullable - public static OneHandedController create( + public static OneHanded create( Context context, DisplayController displayController, - TaskStackListenerImpl taskStackListener, Executor executor) { + TaskStackListenerImpl taskStackListener, + ShellExecutor mainExecutor, + Handler mainHandler) { if (!SystemProperties.getBoolean(SUPPORT_ONE_HANDED_MODE, false)) { Slog.w(TAG, "Device doesn't support OneHanded feature"); return null; } - OneHandedTutorialHandler tutorialHandler = new OneHandedTutorialHandler(context); + OneHandedTimeoutHandler timeoutHandler = new OneHandedTimeoutHandler(mainExecutor); + OneHandedTutorialHandler tutorialHandler = new OneHandedTutorialHandler(context, + mainExecutor); OneHandedAnimationController animationController = new OneHandedAnimationController(context); - OneHandedTouchHandler touchHandler = new OneHandedTouchHandler(); + OneHandedTouchHandler touchHandler = new OneHandedTouchHandler(timeoutHandler, + mainExecutor); OneHandedGestureHandler gestureHandler = new OneHandedGestureHandler( - context, displayController); + context, displayController, mainExecutor); OneHandedBackgroundPanelOrganizer oneHandedBackgroundPanelOrganizer = - new OneHandedBackgroundPanelOrganizer(context, displayController, executor); + new OneHandedBackgroundPanelOrganizer(context, displayController, mainExecutor); OneHandedDisplayAreaOrganizer organizer = new OneHandedDisplayAreaOrganizer( - context, displayController, animationController, tutorialHandler, executor, - oneHandedBackgroundPanelOrganizer); + context, displayController, animationController, tutorialHandler, + oneHandedBackgroundPanelOrganizer, mainExecutor); IOverlayManager overlayManager = IOverlayManager.Stub.asInterface( ServiceManager.getService(Context.OVERLAY_SERVICE)); return new OneHandedController(context, displayController, oneHandedBackgroundPanelOrganizer, organizer, touchHandler, tutorialHandler, - gestureHandler, overlayManager, taskStackListener); + gestureHandler, timeoutHandler, overlayManager, taskStackListener, mainExecutor, + mainHandler).mImpl; } @VisibleForTesting @@ -225,8 +161,11 @@ public class OneHandedController implements OneHanded { OneHandedTouchHandler touchHandler, OneHandedTutorialHandler tutorialHandler, OneHandedGestureHandler gestureHandler, + OneHandedTimeoutHandler timeoutHandler, IOverlayManager overlayManager, - TaskStackListenerImpl taskStackListener) { + TaskStackListenerImpl taskStackListener, + ShellExecutor mainExecutor, + Handler mainHandler) { mContext = context; mBackgroundPanelOrganizer = backgroundPanelOrganizer; mDisplayAreaOrganizer = displayAreaOrganizer; @@ -235,6 +174,8 @@ public class OneHandedController implements OneHanded { mTutorialHandler = tutorialHandler; mGestureHandler = gestureHandler; mOverlayManager = overlayManager; + mMainExecutor = mainExecutor; + mMainHandler = mainHandler; final float offsetPercentageConfig = context.getResources().getFraction( R.fraction.config_one_handed_offset, 1, 1); @@ -246,7 +187,13 @@ public class OneHandedController implements OneHanded { mIsSwipeToNotificationEnabled = OneHandedSettingsUtil.getSettingsSwipeToNotificationEnabled( context.getContentResolver()); - mTimeoutHandler = OneHandedTimeoutHandler.get(); + mTimeoutHandler = timeoutHandler; + + mEnabledObserver = getObserver(this::onEnabledSettingChanged); + mTimeoutObserver = getObserver(this::onTimeoutSettingChanged); + mTaskChangeExitObserver = getObserver(this::onTaskChangeExitSettingChanged); + mSwipeToNotificationEnabledObserver = + getObserver(this::onSwipeToNotificationEnabledSettingChanged); mDisplayController.addDisplayChangingController(mRotationController); @@ -298,18 +245,8 @@ public class OneHandedController implements OneHanded { updateOneHandedEnabled(); } - @Override - public boolean isOneHandedEnabled() { - return mIsOneHandedEnabled; - } - - @Override - public boolean isSwipeToNotificationEnabled() { - return mIsSwipeToNotificationEnabled; - } - - @Override - public void startOneHanded() { + @VisibleForTesting + void startOneHanded() { if (!mDisplayAreaOrganizer.isInOneHanded()) { final int yOffSet = Math.round(getDisplaySize().y * mOffSetFraction); mDisplayAreaOrganizer.scheduleOffset(0, yOffSet); @@ -319,16 +256,15 @@ public class OneHandedController implements OneHanded { } } - @Override - public void stopOneHanded() { + @VisibleForTesting + void stopOneHanded() { if (mDisplayAreaOrganizer.isInOneHanded()) { mDisplayAreaOrganizer.scheduleOffset(0, 0); mTimeoutHandler.removeTimer(); } } - @Override - public void stopOneHanded(int event) { + private void stopOneHanded(int event) { if (!mTaskChangeToExit && event == OneHandedEvents.EVENT_ONE_HANDED_TRIGGER_APP_TAPS_OUT) { //Task change exit not enable, do nothing and return here. return; @@ -341,18 +277,16 @@ public class OneHandedController implements OneHanded { stopOneHanded(); } - @Override - public void setThreeButtonModeEnabled(boolean enabled) { + private void setThreeButtonModeEnabled(boolean enabled) { mGestureHandler.onThreeButtonModeEnabled(enabled); } - @Override - public void registerTransitionCallback(OneHandedTransitionCallback callback) { + @VisibleForTesting + void registerTransitionCallback(OneHandedTransitionCallback callback) { mDisplayAreaOrganizer.registerTransitionCallback(callback); } - @Override - public void registerGestureCallback(OneHandedGestureEventCallback callback) { + private void registerGestureCallback(OneHandedGestureEventCallback callback) { mGestureHandler.setGestureEventListener(callback); } @@ -388,6 +322,80 @@ public class OneHandedController implements OneHanded { .getSettingsSwipeToNotificationEnabled(mContext.getContentResolver())); } + private ContentObserver getObserver(Runnable onChangeRunnable) { + return new ContentObserver(mMainHandler) { + @Override + public void onChange(boolean selfChange) { + onChangeRunnable.run(); + } + }; + } + + private void onEnabledSettingChanged() { + final boolean enabled = OneHandedSettingsUtil.getSettingsOneHandedModeEnabled( + mContext.getContentResolver()); + OneHandedEvents.writeEvent(enabled + ? OneHandedEvents.EVENT_ONE_HANDED_SETTINGS_ENABLED_ON + : OneHandedEvents.EVENT_ONE_HANDED_SETTINGS_ENABLED_OFF); + + setOneHandedEnabled(enabled); + + // Also checks swipe to notification settings since they all need gesture overlay. + setEnabledGesturalOverlay( + enabled || OneHandedSettingsUtil.getSettingsSwipeToNotificationEnabled( + mContext.getContentResolver())); + } + + private void onTimeoutSettingChanged() { + final int newTimeout = OneHandedSettingsUtil.getSettingsOneHandedModeTimeout( + mContext.getContentResolver()); + int metricsId = OneHandedEvents.OneHandedSettingsTogglesEvent.INVALID.getId(); + switch (newTimeout) { + case OneHandedSettingsUtil.ONE_HANDED_TIMEOUT_NEVER: + metricsId = OneHandedEvents.EVENT_ONE_HANDED_SETTINGS_TIMEOUT_SECONDS_NEVER; + break; + case OneHandedSettingsUtil.ONE_HANDED_TIMEOUT_SHORT_IN_SECONDS: + metricsId = OneHandedEvents.EVENT_ONE_HANDED_SETTINGS_TIMEOUT_SECONDS_4; + break; + case OneHandedSettingsUtil.ONE_HANDED_TIMEOUT_MEDIUM_IN_SECONDS: + metricsId = OneHandedEvents.EVENT_ONE_HANDED_SETTINGS_TIMEOUT_SECONDS_8; + break; + case OneHandedSettingsUtil.ONE_HANDED_TIMEOUT_LONG_IN_SECONDS: + metricsId = OneHandedEvents.EVENT_ONE_HANDED_SETTINGS_TIMEOUT_SECONDS_12; + break; + default: + // do nothing + break; + } + OneHandedEvents.writeEvent(metricsId); + + if (mTimeoutHandler != null) { + mTimeoutHandler.setTimeout(newTimeout); + } + } + + private void onTaskChangeExitSettingChanged() { + final boolean enabled = OneHandedSettingsUtil.getSettingsTapsAppToExit( + mContext.getContentResolver()); + OneHandedEvents.writeEvent(enabled + ? OneHandedEvents.EVENT_ONE_HANDED_SETTINGS_APP_TAPS_EXIT_ON + : OneHandedEvents.EVENT_ONE_HANDED_SETTINGS_APP_TAPS_EXIT_OFF); + + setTaskChangeToExit(enabled); + } + + private void onSwipeToNotificationEnabledSettingChanged() { + final boolean enabled = + OneHandedSettingsUtil.getSettingsSwipeToNotificationEnabled( + mContext.getContentResolver()); + setSwipeToNotificationEnabled(enabled); + + // Also checks one handed mode settings since they all need gesture overlay. + setEnabledGesturalOverlay( + enabled || OneHandedSettingsUtil.getSettingsOneHandedModeEnabled( + mContext.getContentResolver())); + } + private void setupTimeoutListener() { mTimeoutHandler.registerTimeoutListener(timeoutTime -> { stopOneHanded(OneHandedEvents.EVENT_ONE_HANDED_TRIGGER_TIMEOUT_OUT); @@ -451,8 +459,7 @@ public class OneHandedController implements OneHanded { } } - @Override - public void dump(@NonNull PrintWriter pw) { + private void dump(@NonNull PrintWriter pw) { final String innerPrefix = " "; pw.println(TAG + "states: "); pw.print(innerPrefix + "mOffSetFraction="); @@ -489,4 +496,68 @@ public class OneHandedController implements OneHanded { } } } + + @ExternalThread + private class OneHandedImpl implements OneHanded { + @Override + public boolean isOneHandedEnabled() { + // This is volatile so return directly + return mIsOneHandedEnabled; + } + + @Override + public boolean isSwipeToNotificationEnabled() { + // This is volatile so return directly + return mIsSwipeToNotificationEnabled; + } + + @Override + public void startOneHanded() { + mMainExecutor.execute(() -> { + OneHandedController.this.startOneHanded(); + }); + } + + @Override + public void stopOneHanded() { + mMainExecutor.execute(() -> { + OneHandedController.this.stopOneHanded(); + }); + } + + @Override + public void stopOneHanded(int event) { + mMainExecutor.execute(() -> { + OneHandedController.this.stopOneHanded(event); + }); + } + + @Override + public void setThreeButtonModeEnabled(boolean enabled) { + mMainExecutor.execute(() -> { + OneHandedController.this.setThreeButtonModeEnabled(enabled); + }); + } + + @Override + public void registerTransitionCallback(OneHandedTransitionCallback callback) { + mMainExecutor.execute(() -> { + OneHandedController.this.registerTransitionCallback(callback); + }); + } + + @Override + public void registerGestureCallback(OneHandedGestureEventCallback callback) { + mMainExecutor.execute(() -> { + OneHandedController.this.registerGestureCallback(callback); + }); + } + + @Override + public void dump(@NonNull PrintWriter pw) { + mMainExecutor.execute(() -> { + OneHandedController.this.dump(pw); + }); + } + } } 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 7873318fc82d..d2d5591100d4 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 @@ -24,8 +24,6 @@ import static com.android.wm.shell.onehanded.OneHandedAnimationController.TRANSI import android.content.Context; import android.graphics.Point; import android.graphics.Rect; -import android.os.Handler; -import android.os.Looper; import android.os.SystemProperties; import android.util.ArrayMap; import android.util.Log; @@ -39,9 +37,9 @@ import androidx.annotation.NonNull; import androidx.annotation.Nullable; import androidx.annotation.VisibleForTesting; -import com.android.internal.os.SomeArgs; import com.android.wm.shell.R; import com.android.wm.shell.common.DisplayController; +import com.android.wm.shell.common.ShellExecutor; import java.io.PrintWriter; import java.util.ArrayList; @@ -64,17 +62,9 @@ public class OneHandedDisplayAreaOrganizer extends DisplayAreaOrganizer { private static final String ONE_HANDED_MODE_TRANSLATE_ANIMATION_DURATION = "persist.debug.one_handed_translate_animation_duration"; - @VisibleForTesting - static final int MSG_RESET_IMMEDIATE = 1; - @VisibleForTesting - static final int MSG_OFFSET_ANIMATE = 2; - @VisibleForTesting - static final int MSG_OFFSET_FINISH = 3; - private final Rect mLastVisualDisplayBounds = new Rect(); private final Rect mDefaultDisplayBounds = new Rect(); - private Handler mUpdateHandler; private boolean mIsInOneHanded; private int mEnterExitAnimationDurationMs; @@ -101,8 +91,8 @@ public class OneHandedDisplayAreaOrganizer extends DisplayAreaOrganizer { OneHandedAnimationController.OneHandedTransitionAnimator animator) { mAnimationController.removeAnimator(animator.getLeash()); if (mAnimationController.isAnimatorsConsumed()) { - mUpdateHandler.sendMessage(mUpdateHandler.obtainMessage(MSG_OFFSET_FINISH, - obtainArgsFromAnimator(animator))); + finishOffset(animator.getDestinationOffset(), + animator.getTransitionDirection()); } } @@ -111,52 +101,22 @@ public class OneHandedDisplayAreaOrganizer extends DisplayAreaOrganizer { OneHandedAnimationController.OneHandedTransitionAnimator animator) { mAnimationController.removeAnimator(animator.getLeash()); if (mAnimationController.isAnimatorsConsumed()) { - mUpdateHandler.sendMessage(mUpdateHandler.obtainMessage(MSG_OFFSET_FINISH, - obtainArgsFromAnimator(animator))); + finishOffset(animator.getDestinationOffset(), + animator.getTransitionDirection()); } } }; - @SuppressWarnings("unchecked") - private Handler.Callback mUpdateCallback = (msg) -> { - SomeArgs args = (SomeArgs) msg.obj; - final Rect currentBounds = args.arg1 != null ? (Rect) args.arg1 : mDefaultDisplayBounds; - final WindowContainerTransaction wctFromRotate = (WindowContainerTransaction) args.arg2; - final int yOffset = args.argi2; - final int direction = args.argi3; - - switch (msg.what) { - case MSG_RESET_IMMEDIATE: - resetWindowsOffset(wctFromRotate); - mDefaultDisplayBounds.set(currentBounds); - mLastVisualDisplayBounds.set(currentBounds); - finishOffset(0, TRANSITION_DIRECTION_EXIT); - break; - case MSG_OFFSET_ANIMATE: - final Rect toBounds = new Rect(mDefaultDisplayBounds.left, - mDefaultDisplayBounds.top + yOffset, - mDefaultDisplayBounds.right, - mDefaultDisplayBounds.bottom + yOffset); - offsetWindows(currentBounds, toBounds, direction, mEnterExitAnimationDurationMs); - break; - case MSG_OFFSET_FINISH: - finishOffset(yOffset, direction); - break; - } - args.recycle(); - return true; - }; - /** * Constructor of OneHandedDisplayAreaOrganizer */ public OneHandedDisplayAreaOrganizer(Context context, DisplayController displayController, OneHandedAnimationController animationController, - OneHandedTutorialHandler tutorialHandler, Executor executor, - OneHandedBackgroundPanelOrganizer oneHandedBackgroundGradientOrganizer) { - super(executor); - mUpdateHandler = new Handler(OneHandedThread.get().getLooper(), mUpdateCallback); + OneHandedTutorialHandler tutorialHandler, + OneHandedBackgroundPanelOrganizer oneHandedBackgroundGradientOrganizer, + ShellExecutor mainExecutor) { + super(mainExecutor); mAnimationController = animationController; mDisplayController = displayController; mDefaultDisplayBounds.set(getDisplayBounds()); @@ -176,12 +136,10 @@ public class OneHandedDisplayAreaOrganizer extends DisplayAreaOrganizer { @NonNull SurfaceControl leash) { Objects.requireNonNull(displayAreaInfo, "displayAreaInfo must not be null"); Objects.requireNonNull(leash, "leash must not be null"); - synchronized (this) { - if (mDisplayAreaMap.get(displayAreaInfo) == null) { - // mDefaultDisplayBounds may out of date after removeDisplayChangingController() - mDefaultDisplayBounds.set(getDisplayBounds()); - mDisplayAreaMap.put(displayAreaInfo, leash); - } + if (mDisplayAreaMap.get(displayAreaInfo) == null) { + // mDefaultDisplayBounds may out of date after removeDisplayChangingController() + mDefaultDisplayBounds.set(getDisplayBounds()); + mDisplayAreaMap.put(displayAreaInfo, leash); } } @@ -189,13 +147,11 @@ public class OneHandedDisplayAreaOrganizer extends DisplayAreaOrganizer { public void onDisplayAreaVanished(@NonNull DisplayAreaInfo displayAreaInfo) { Objects.requireNonNull(displayAreaInfo, "Requires valid displayArea, and displayArea must not be null"); - synchronized (this) { - if (!mDisplayAreaMap.containsKey(displayAreaInfo)) { - Log.w(TAG, "Unrecognized token: " + displayAreaInfo.token); - return; - } - mDisplayAreaMap.remove(displayAreaInfo); + if (!mDisplayAreaMap.containsKey(displayAreaInfo)) { + Log.w(TAG, "Unrecognized token: " + displayAreaInfo.token); + return; } + mDisplayAreaMap.remove(displayAreaInfo); } @Override @@ -212,7 +168,7 @@ public class OneHandedDisplayAreaOrganizer extends DisplayAreaOrganizer { @Override public void unregisterOrganizer() { super.unregisterOrganizer(); - mUpdateHandler.post(() -> resetWindowsOffset(null)); + resetWindowsOffset(null); } /** @@ -230,14 +186,10 @@ public class OneHandedDisplayAreaOrganizer extends DisplayAreaOrganizer { final boolean isOrientationDiff = Math.abs(fromRotation - toRotation) % 2 == 1; if (isOrientationDiff) { - newBounds.set(newBounds.left, newBounds.top, newBounds.bottom, newBounds.right); - SomeArgs args = SomeArgs.obtain(); - args.arg1 = newBounds; - args.arg2 = wct; - args.argi1 = 0 /* xOffset */; - args.argi2 = 0 /* yOffset */; - args.argi3 = TRANSITION_DIRECTION_EXIT; - mUpdateHandler.sendMessage(mUpdateHandler.obtainMessage(MSG_RESET_IMMEDIATE, args)); + resetWindowsOffset(wct); + mDefaultDisplayBounds.set(newBounds); + mLastVisualDisplayBounds.set(newBounds); + finishOffset(0, TRANSITION_DIRECTION_EXIT); } } @@ -246,79 +198,65 @@ public class OneHandedDisplayAreaOrganizer extends DisplayAreaOrganizer { * Directly perform manipulation/offset on the leash. */ public void scheduleOffset(int xOffset, int yOffset) { - SomeArgs args = SomeArgs.obtain(); - args.arg1 = getLastVisualDisplayBounds(); - args.argi1 = xOffset; - args.argi2 = yOffset; - args.argi3 = yOffset > 0 ? TRANSITION_DIRECTION_TRIGGER : TRANSITION_DIRECTION_EXIT; - mUpdateHandler.sendMessage(mUpdateHandler.obtainMessage(MSG_OFFSET_ANIMATE, args)); - } - - private void offsetWindows(Rect fromBounds, Rect toBounds, int direction, int durationMs) { - if (Looper.myLooper() != mUpdateHandler.getLooper()) { - throw new RuntimeException("Callers should call scheduleOffset() instead of this " - + "directly"); - } - synchronized (this) { - final WindowContainerTransaction wct = new WindowContainerTransaction(); - mDisplayAreaMap.forEach( - (key, leash) -> { - animateWindows(leash, fromBounds, toBounds, direction, durationMs); - wct.setBounds(key.token, toBounds); - }); - applyTransaction(wct); - } + final Rect toBounds = new Rect(mDefaultDisplayBounds.left, + mDefaultDisplayBounds.top + yOffset, + mDefaultDisplayBounds.right, + mDefaultDisplayBounds.bottom + yOffset); + final Rect fromBounds = getLastVisualDisplayBounds() != null + ? getLastVisualDisplayBounds() + : mDefaultDisplayBounds; + final int direction = yOffset > 0 + ? TRANSITION_DIRECTION_TRIGGER + : TRANSITION_DIRECTION_EXIT; + + final WindowContainerTransaction wct = new WindowContainerTransaction(); + mDisplayAreaMap.forEach( + (key, leash) -> { + animateWindows(leash, fromBounds, toBounds, direction, + mEnterExitAnimationDurationMs); + wct.setBounds(key.token, toBounds); + }); + applyTransaction(wct); } private void resetWindowsOffset(WindowContainerTransaction wct) { - synchronized (this) { - final SurfaceControl.Transaction tx = - mSurfaceControlTransactionFactory.getTransaction(); - mDisplayAreaMap.forEach( - (key, leash) -> { - final OneHandedAnimationController.OneHandedTransitionAnimator animator = - mAnimationController.getAnimatorMap().remove(leash); - if (animator != null && animator.isRunning()) { - animator.cancel(); - } - tx.setPosition(leash, 0, 0) - .setWindowCrop(leash, -1/* reset */, -1/* reset */); - // DisplayRotationController will applyTransaction() after finish rotating - if (wct != null) { - wct.setBounds(key.token, null/* reset */); - } - }); - tx.apply(); - } + final SurfaceControl.Transaction tx = + mSurfaceControlTransactionFactory.getTransaction(); + mDisplayAreaMap.forEach( + (key, leash) -> { + final OneHandedAnimationController.OneHandedTransitionAnimator animator = + mAnimationController.getAnimatorMap().remove(leash); + if (animator != null && animator.isRunning()) { + animator.cancel(); + } + tx.setPosition(leash, 0, 0) + .setWindowCrop(leash, -1/* reset */, -1/* reset */); + // DisplayRotationController will applyTransaction() after finish rotating + if (wct != null) { + wct.setBounds(key.token, null/* reset */); + } + }); + tx.apply(); } private void animateWindows(SurfaceControl leash, Rect fromBounds, Rect toBounds, @OneHandedAnimationController.TransitionDirection int direction, int durationMs) { - if (Looper.myLooper() != mUpdateHandler.getLooper()) { - throw new RuntimeException("Callers should call scheduleOffset() instead of " - + "this directly"); + final OneHandedAnimationController.OneHandedTransitionAnimator animator = + mAnimationController.getAnimator(leash, fromBounds, toBounds); + if (animator != null) { + animator.setTransitionDirection(direction) + .addOneHandedAnimationCallback(mOneHandedAnimationCallback) + .addOneHandedAnimationCallback(mTutorialHandler.getAnimationCallback()) + .addOneHandedAnimationCallback( + mBackgroundPanelOrganizer.getOneHandedAnimationCallback()) + .setDuration(durationMs) + .start(); } - mUpdateHandler.post(() -> { - final OneHandedAnimationController.OneHandedTransitionAnimator animator = - mAnimationController.getAnimator(leash, fromBounds, toBounds); - if (animator != null) { - animator.setTransitionDirection(direction) - .addOneHandedAnimationCallback(mOneHandedAnimationCallback) - .addOneHandedAnimationCallback(mTutorialHandler.getAnimationCallback()) - .addOneHandedAnimationCallback( - mBackgroundPanelOrganizer.getOneHandedAnimationCallback()) - .setDuration(durationMs) - .start(); - } - }); } - private void finishOffset(int offset, + @VisibleForTesting + void finishOffset(int offset, @OneHandedAnimationController.TransitionDirection int direction) { - if (Looper.myLooper() != mUpdateHandler.getLooper()) { - throw new RuntimeException( - "Callers should call scheduleOffset() instead of this directly."); - } // Only finishOffset() can update mIsInOneHanded to ensure the state is handle in sequence, // the flag *MUST* be updated before dispatch mTransitionCallbacks mIsInOneHanded = (offset > 0 || direction == TRANSITION_DIRECTION_TRIGGER); @@ -361,11 +299,6 @@ public class OneHandedDisplayAreaOrganizer extends DisplayAreaOrganizer { return new Rect(0, 0, realSize.x, realSize.y); } - @VisibleForTesting - void setUpdateHandler(Handler updateHandler) { - mUpdateHandler = updateHandler; - } - /** * Register transition callback */ @@ -373,16 +306,6 @@ public class OneHandedDisplayAreaOrganizer extends DisplayAreaOrganizer { mTransitionCallbacks.add(callback); } - private SomeArgs obtainArgsFromAnimator( - OneHandedAnimationController.OneHandedTransitionAnimator animator) { - SomeArgs args = SomeArgs.obtain(); - args.arg1 = animator.getDestinationBounds(); - args.argi1 = 0 /* xOffset */; - args.argi2 = animator.getDestinationOffset(); - args.argi3 = animator.getTransitionDirection(); - return args; - } - void dump(@NonNull PrintWriter pw) { final String innerPrefix = " "; pw.println(TAG + "states: "); diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/onehanded/OneHandedGestureHandler.java b/libs/WindowManager/Shell/src/com/android/wm/shell/onehanded/OneHandedGestureHandler.java index aa4ec1788a88..1ed121f35a59 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/onehanded/OneHandedGestureHandler.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/onehanded/OneHandedGestureHandler.java @@ -41,6 +41,7 @@ import androidx.annotation.VisibleForTesting; import com.android.wm.shell.R; import com.android.wm.shell.common.DisplayChangeController; import com.android.wm.shell.common.DisplayController; +import com.android.wm.shell.common.ShellExecutor; /** * The class manage swipe up and down gesture for 3-Button mode navigation, @@ -70,7 +71,8 @@ public class OneHandedGestureHandler implements OneHandedTransitionCallback, InputMonitor mInputMonitor; @VisibleForTesting InputEventReceiver mInputEventReceiver; - private DisplayController mDisplayController; + private final DisplayController mDisplayController; + private final ShellExecutor mMainExecutor; @VisibleForTesting @Nullable OneHandedGestureEventCallback mGestureEventCallback; @@ -84,8 +86,10 @@ public class OneHandedGestureHandler implements OneHandedTransitionCallback, * @param context {@link Context} * @param displayController {@link DisplayController} */ - public OneHandedGestureHandler(Context context, DisplayController displayController) { + public OneHandedGestureHandler(Context context, DisplayController displayController, + ShellExecutor mainExecutor) { mDisplayController = displayController; + mMainExecutor = mainExecutor; displayController.addDisplayChangingController(this); mNavGestureHeight = context.getResources().getDimensionPixelSize( com.android.internal.R.dimen.navigation_bar_gesture_larger_height); @@ -93,6 +97,7 @@ public class OneHandedGestureHandler implements OneHandedTransitionCallback, R.dimen.gestures_onehanded_drag_threshold); final float slop = ViewConfiguration.get(context).getScaledTouchSlop(); mSquaredSlop = slop * slop; + updateIsEnabled(); } @@ -217,7 +222,7 @@ public class OneHandedGestureHandler implements OneHandedTransitionCallback, mInputMonitor = InputManager.getInstance().monitorGestureInput( "onehanded-gesture-offset", DEFAULT_DISPLAY); mInputEventReceiver = new EventReceiver( - mInputMonitor.getInputChannel(), Looper.getMainLooper()); + mInputMonitor.getInputChannel(), mMainExecutor.getLooper()); } } @@ -233,6 +238,7 @@ public class OneHandedGestureHandler implements OneHandedTransitionCallback, mRotation = toRotation; } + // TODO: Use BatchedInputEventReceiver private class EventReceiver extends InputEventReceiver { EventReceiver(InputChannel channel, Looper looper) { super(channel, looper); diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/onehanded/OneHandedThread.java b/libs/WindowManager/Shell/src/com/android/wm/shell/onehanded/OneHandedThread.java deleted file mode 100644 index 24d33ede5d63..000000000000 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/onehanded/OneHandedThread.java +++ /dev/null @@ -1,62 +0,0 @@ -/* - * Copyright (C) 2020 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.wm.shell.onehanded; - -import android.os.Handler; -import android.os.HandlerThread; - -/** - * Similar to {@link com.android.internal.os.BackgroundThread}, this is a shared singleton - * foreground thread for each process for updating one handed. - */ -public class OneHandedThread extends HandlerThread { - private static OneHandedThread sInstance; - private static Handler sHandler; - - private OneHandedThread() { - super("OneHanded"); - } - - private static void ensureThreadLocked() { - if (sInstance == null) { - sInstance = new OneHandedThread(); - sInstance.start(); - sHandler = new Handler(sInstance.getLooper()); - } - } - - /** - * @return the static update thread instance - */ - public static OneHandedThread get() { - synchronized (OneHandedThread.class) { - ensureThreadLocked(); - return sInstance; - } - } - - /** - * @return the static update thread handler instance - */ - public static Handler getHandler() { - synchronized (OneHandedThread.class) { - ensureThreadLocked(); - return sHandler; - } - } - -} diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/onehanded/OneHandedTimeoutHandler.java b/libs/WindowManager/Shell/src/com/android/wm/shell/onehanded/OneHandedTimeoutHandler.java index 9c97cd7db71f..4a98941b7410 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/onehanded/OneHandedTimeoutHandler.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/onehanded/OneHandedTimeoutHandler.java @@ -19,12 +19,12 @@ package com.android.wm.shell.onehanded; import static com.android.wm.shell.onehanded.OneHandedSettingsUtil.ONE_HANDED_TIMEOUT_MEDIUM_IN_SECONDS; import android.os.Handler; -import android.os.Looper; -import android.os.Message; import androidx.annotation.NonNull; import androidx.annotation.VisibleForTesting; +import com.android.wm.shell.common.ShellExecutor; + import java.io.PrintWriter; import java.util.ArrayList; import java.util.List; @@ -35,18 +35,15 @@ import java.util.concurrent.TimeUnit; */ public class OneHandedTimeoutHandler { private static final String TAG = "OneHandedTimeoutHandler"; - private static boolean sIsDragging = false; + + private final ShellExecutor mMainExecutor; + // Default timeout is ONE_HANDED_TIMEOUT_MEDIUM - private static @OneHandedSettingsUtil.OneHandedTimeout int sTimeout = + private @OneHandedSettingsUtil.OneHandedTimeout int mTimeout = ONE_HANDED_TIMEOUT_MEDIUM_IN_SECONDS; - private static long sTimeoutMs = TimeUnit.SECONDS.toMillis(sTimeout); - private static OneHandedTimeoutHandler sInstance; - private static List<TimeoutListener> sListeners = new ArrayList<>(); - - @VisibleForTesting - static final int ONE_HANDED_TIMEOUT_STOP_MSG = 1; - @VisibleForTesting - static Handler sHandler; + private long mTimeoutMs = TimeUnit.SECONDS.toMillis(mTimeout); + private final Runnable mTimeoutRunnable = this::onStop; + private List<TimeoutListener> mListeners = new ArrayList<>(); /** * Get the current config of timeout @@ -54,7 +51,7 @@ public class OneHandedTimeoutHandler { * @return timeout of current config */ public @OneHandedSettingsUtil.OneHandedTimeout int getTimeout() { - return sTimeout; + return mTimeout; } /** @@ -69,32 +66,36 @@ public class OneHandedTimeoutHandler { void onTimeout(int timeoutTime); } + public OneHandedTimeoutHandler(ShellExecutor mainExecutor) { + mMainExecutor = mainExecutor; + } + /** * Set the specific timeout of {@link OneHandedSettingsUtil.OneHandedTimeout} */ - public static void setTimeout(@OneHandedSettingsUtil.OneHandedTimeout int timeout) { - sTimeout = timeout; - sTimeoutMs = TimeUnit.SECONDS.toMillis(sTimeout); + public void setTimeout(@OneHandedSettingsUtil.OneHandedTimeout int timeout) { + mTimeout = timeout; + mTimeoutMs = TimeUnit.SECONDS.toMillis(mTimeout); resetTimer(); } /** * Reset the timer when one handed trigger or user is operating in some conditions */ - public static void removeTimer() { - sHandler.removeMessages(ONE_HANDED_TIMEOUT_STOP_MSG); + public void removeTimer() { + mMainExecutor.removeCallbacks(mTimeoutRunnable); } /** * Reset the timer when one handed trigger or user is operating in some conditions */ - public static void resetTimer() { + public void resetTimer() { removeTimer(); - if (sTimeout == OneHandedSettingsUtil.ONE_HANDED_TIMEOUT_NEVER) { + if (mTimeout == OneHandedSettingsUtil.ONE_HANDED_TIMEOUT_NEVER) { return; } - if (sTimeout != OneHandedSettingsUtil.ONE_HANDED_TIMEOUT_NEVER) { - sHandler.sendEmptyMessageDelayed(ONE_HANDED_TIMEOUT_STOP_MSG, sTimeoutMs); + if (mTimeout != OneHandedSettingsUtil.ONE_HANDED_TIMEOUT_NEVER) { + mMainExecutor.executeDelayed(mTimeoutRunnable, mTimeoutMs); } } @@ -103,47 +104,19 @@ public class OneHandedTimeoutHandler { * * @param listener the listener be sent events when times up */ - public static void registerTimeoutListener(TimeoutListener listener) { - sListeners.add(listener); + public void registerTimeoutListener(TimeoutListener listener) { + mListeners.add(listener); } - /** - * Private constructor due to Singleton pattern - */ - private OneHandedTimeoutHandler() { - } - - /** - * Singleton pattern to get {@link OneHandedTimeoutHandler} instance - * - * @return the static update thread instance - */ - public static OneHandedTimeoutHandler get() { - synchronized (OneHandedTimeoutHandler.class) { - if (sInstance == null) { - sInstance = new OneHandedTimeoutHandler(); - } - if (sHandler == null) { - sHandler = new Handler(Looper.myLooper()) { - @Override - public void handleMessage(Message msg) { - if (msg.what == ONE_HANDED_TIMEOUT_STOP_MSG) { - onStop(); - } - } - }; - if (sTimeout != OneHandedSettingsUtil.ONE_HANDED_TIMEOUT_NEVER) { - sHandler.sendEmptyMessageDelayed(ONE_HANDED_TIMEOUT_STOP_MSG, sTimeoutMs); - } - } - return sInstance; - } + @VisibleForTesting + boolean hasScheduledTimeout() { + return mMainExecutor.hasCallback(mTimeoutRunnable); } - private static void onStop() { - for (int i = sListeners.size() - 1; i >= 0; i--) { - final TimeoutListener listener = sListeners.get(i); - listener.onTimeout(sTimeout); + private void onStop() { + for (int i = mListeners.size() - 1; i >= 0; i--) { + final TimeoutListener listener = mListeners.get(i); + listener.onTimeout(mTimeout); } } @@ -151,9 +124,9 @@ public class OneHandedTimeoutHandler { final String innerPrefix = " "; pw.println(TAG + "states: "); pw.print(innerPrefix + "sTimeout="); - pw.println(sTimeout); + pw.println(mTimeout); pw.print(innerPrefix + "sListeners="); - pw.println(sListeners); + pw.println(mListeners); } } diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/onehanded/OneHandedTouchHandler.java b/libs/WindowManager/Shell/src/com/android/wm/shell/onehanded/OneHandedTouchHandler.java index 721382d52717..60709bef4daf 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/onehanded/OneHandedTouchHandler.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/onehanded/OneHandedTouchHandler.java @@ -30,6 +30,8 @@ import android.view.MotionEvent; import androidx.annotation.NonNull; import androidx.annotation.VisibleForTesting; +import com.android.wm.shell.common.ShellExecutor; + import java.io.PrintWriter; /** @@ -41,7 +43,8 @@ public class OneHandedTouchHandler implements OneHandedTransitionCallback { private static final String TAG = "OneHandedTouchHandler"; private final Rect mLastUpdatedBounds = new Rect(); - private OneHandedTimeoutHandler mTimeoutHandler; + private final OneHandedTimeoutHandler mTimeoutHandler; + private final ShellExecutor mMainExecutor; @VisibleForTesting InputMonitor mInputMonitor; @@ -54,8 +57,10 @@ public class OneHandedTouchHandler implements OneHandedTransitionCallback { private boolean mIsOnStopTransitioning; private boolean mIsInOutsideRegion; - public OneHandedTouchHandler() { - mTimeoutHandler = OneHandedTimeoutHandler.get(); + public OneHandedTouchHandler(OneHandedTimeoutHandler timeoutHandler, + ShellExecutor mainExecutor) { + mTimeoutHandler = timeoutHandler; + mMainExecutor = mainExecutor; updateIsEnabled(); } @@ -128,7 +133,7 @@ public class OneHandedTouchHandler implements OneHandedTransitionCallback { mInputMonitor = InputManager.getInstance().monitorGestureInput( "onehanded-touch", DEFAULT_DISPLAY); mInputEventReceiver = new EventReceiver( - mInputMonitor.getInputChannel(), Looper.getMainLooper()); + mInputMonitor.getInputChannel(), mMainExecutor.getLooper()); } } @@ -150,6 +155,7 @@ public class OneHandedTouchHandler implements OneHandedTransitionCallback { pw.println(mLastUpdatedBounds); } + // TODO: Use BatchedInputEventReceiver private class EventReceiver extends InputEventReceiver { EventReceiver(InputChannel channel, Looper looper) { super(channel, looper); diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/onehanded/OneHandedTutorialHandler.java b/libs/WindowManager/Shell/src/com/android/wm/shell/onehanded/OneHandedTutorialHandler.java index a944e3bc50cf..492130bebb30 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/onehanded/OneHandedTutorialHandler.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/onehanded/OneHandedTutorialHandler.java @@ -21,7 +21,6 @@ import android.content.Context; import android.graphics.PixelFormat; import android.graphics.Point; import android.graphics.Rect; -import android.os.Handler; import android.os.SystemProperties; import android.provider.Settings; import android.view.Gravity; @@ -36,6 +35,7 @@ import android.widget.FrameLayout; import androidx.annotation.NonNull; import com.android.wm.shell.R; +import com.android.wm.shell.common.ShellExecutor; import java.io.PrintWriter; @@ -57,7 +57,6 @@ public class OneHandedTutorialHandler implements OneHandedTransitionCallback { private View mTutorialView; private Point mDisplaySize = new Point(); - private Handler mUpdateHandler; private ContentResolver mContentResolver; private boolean mCanShowTutorial; private String mStartOneHandedDescription; @@ -82,69 +81,67 @@ public class OneHandedTutorialHandler implements OneHandedTransitionCallback { private final OneHandedAnimationCallback mAnimationCallback = new OneHandedAnimationCallback() { @Override public void onTutorialAnimationUpdate(int offset) { - mUpdateHandler.post(() -> onAnimationUpdate(offset)); + onAnimationUpdate(offset); } @Override public void onOneHandedAnimationStart( OneHandedAnimationController.OneHandedTransitionAnimator animator) { - mUpdateHandler.post(() -> { - final Rect startValue = (Rect) animator.getStartValue(); - if (mTriggerState == ONE_HANDED_TRIGGER_STATE.UNSET) { - mTriggerState = (startValue.top == 0) - ? ONE_HANDED_TRIGGER_STATE.ENTERING : ONE_HANDED_TRIGGER_STATE.EXITING; - if (mCanShowTutorial && mTriggerState == ONE_HANDED_TRIGGER_STATE.ENTERING) { - createTutorialTarget(); - } + final Rect startValue = (Rect) animator.getStartValue(); + if (mTriggerState == ONE_HANDED_TRIGGER_STATE.UNSET) { + mTriggerState = (startValue.top == 0) + ? ONE_HANDED_TRIGGER_STATE.ENTERING : ONE_HANDED_TRIGGER_STATE.EXITING; + if (mCanShowTutorial && mTriggerState == ONE_HANDED_TRIGGER_STATE.ENTERING) { + createTutorialTarget(); } - }); + } } }; - public OneHandedTutorialHandler(Context context) { + public OneHandedTutorialHandler(Context context, ShellExecutor mainExecutor) { context.getDisplay().getRealSize(mDisplaySize); mPackageName = context.getPackageName(); mContentResolver = context.getContentResolver(); - mUpdateHandler = new Handler(); mWindowManager = context.getSystemService(WindowManager.class); mAccessibilityManager = (AccessibilityManager) context.getSystemService(Context.ACCESSIBILITY_SERVICE); - mTargetViewContainer = new FrameLayout(context); - mTargetViewContainer.setClipChildren(false); + + mStartOneHandedDescription = context.getResources().getString( + R.string.accessibility_action_start_one_handed); + mStopOneHandedDescription = context.getResources().getString( + R.string.accessibility_action_stop_one_handed); + mCanShowTutorial = (Settings.Secure.getInt(mContentResolver, + Settings.Secure.ONE_HANDED_TUTORIAL_SHOW_COUNT, 0) >= MAX_TUTORIAL_SHOW_COUNT) + ? false : true; final float offsetPercentageConfig = context.getResources().getFraction( R.fraction.config_one_handed_offset, 1, 1); final int sysPropPercentageConfig = SystemProperties.getInt( ONE_HANDED_MODE_OFFSET_PERCENTAGE, Math.round(offsetPercentageConfig * 100.0f)); mTutorialAreaHeight = Math.round(mDisplaySize.y * (sysPropPercentageConfig / 100.0f)); - mTutorialView = LayoutInflater.from(context).inflate(R.layout.one_handed_tutorial, null); - mTargetViewContainer.addView(mTutorialView); - mCanShowTutorial = (Settings.Secure.getInt(mContentResolver, - Settings.Secure.ONE_HANDED_TUTORIAL_SHOW_COUNT, 0) >= MAX_TUTORIAL_SHOW_COUNT) - ? false : true; - mStartOneHandedDescription = context.getResources().getString( - R.string.accessibility_action_start_one_handed); - mStopOneHandedDescription = context.getResources().getString( - R.string.accessibility_action_stop_one_handed); + + mainExecutor.execute(() -> { + mTutorialView = LayoutInflater.from(context).inflate(R.layout.one_handed_tutorial, + null); + mTargetViewContainer = new FrameLayout(context); + mTargetViewContainer.setClipChildren(false); + mTargetViewContainer.addView(mTutorialView); + }); } @Override public void onStartFinished(Rect bounds) { - mUpdateHandler.post(() -> { - updateFinished(View.VISIBLE, 0f); - updateTutorialCount(); - announcementForScreenReader(true); - mTriggerState = ONE_HANDED_TRIGGER_STATE.UNSET; - }); + updateFinished(View.VISIBLE, 0f); + updateTutorialCount(); + announcementForScreenReader(true); + mTriggerState = ONE_HANDED_TRIGGER_STATE.UNSET; } @Override public void onStopFinished(Rect bounds) { - mUpdateHandler.post(() -> { - updateFinished(View.INVISIBLE, -mTargetViewContainer.getHeight()); - announcementForScreenReader(false); - removeTutorialFromWindowManager(); - mTriggerState = ONE_HANDED_TRIGGER_STATE.UNSET; - }); + updateFinished(View.INVISIBLE, -mTargetViewContainer.getHeight()); + announcementForScreenReader(false); + removeTutorialFromWindowManager(); + mTriggerState = ONE_HANDED_TRIGGER_STATE.UNSET; } private void updateFinished(int visible, float finalPosition) { diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipMediaController.java b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipMediaController.java index a7c34fd4465a..d96d4d0a6a3c 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipMediaController.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipMediaController.java @@ -138,7 +138,7 @@ public class PipMediaController { public void onActivityPinned() { // Once we enter PiP, try to find the active media controller for the top most activity resolveActiveMediaController(mMediaSessionManager.getActiveSessionsForUser(null, - UserHandle.USER_CURRENT)); + UserHandle.CURRENT)); } /** @@ -245,7 +245,7 @@ public class PipMediaController { public void registerSessionListenerForCurrentUser() { mMediaSessionManager.removeOnActiveSessionsChangedListener(mSessionsChangedListener); mMediaSessionManager.addOnActiveSessionsChangedListener(mSessionsChangedListener, null, - UserHandle.USER_CURRENT, null); + UserHandle.CURRENT, null); } /** diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipUiEventLogger.java b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipUiEventLogger.java index de3bb2950c0a..f8b4dd9bc621 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipUiEventLogger.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipUiEventLogger.java @@ -41,12 +41,12 @@ public class PipUiEventLogger { } public void setTaskInfo(TaskInfo taskInfo) { - if (taskInfo == null) { - mPackageName = null; - mPackageUid = INVALID_PACKAGE_UID; - } else { + if (taskInfo != null && taskInfo.topActivity != null) { mPackageName = taskInfo.topActivity.getPackageName(); mPackageUid = getUid(mPackageName, taskInfo.userId); + } else { + mPackageName = null; + mPackageUid = INVALID_PACKAGE_UID; } } diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipAccessibilityInteractionConnection.java b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipAccessibilityInteractionConnection.java index 7194fc70025c..3b65899364cd 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipAccessibilityInteractionConnection.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipAccessibilityInteractionConnection.java @@ -53,7 +53,7 @@ public class PipAccessibilityInteractionConnection { private List<AccessibilityNodeInfo> mAccessibilityNodeInfoList; private final Context mContext; - private final ShellExecutor mShellMainExcutor; + private final ShellExecutor mMainExcutor; private final @NonNull PipBoundsState mPipBoundsState; private final PipMotionHelper mMotionHelper; private final PipTaskOrganizer mTaskOrganizer; @@ -72,9 +72,9 @@ public class PipAccessibilityInteractionConnection { @NonNull PipBoundsState pipBoundsState, PipMotionHelper motionHelper, PipTaskOrganizer taskOrganizer, PipSnapAlgorithm snapAlgorithm, AccessibilityCallbacks callbacks, Runnable updateMovementBoundCallback, - ShellExecutor shellMainExcutor) { + ShellExecutor mainExcutor) { mContext = context; - mShellMainExcutor = shellMainExcutor; + mMainExcutor = mainExcutor; mPipBoundsState = pipBoundsState; mMotionHelper = motionHelper; mTaskOrganizer = taskOrganizer; @@ -271,7 +271,7 @@ public class PipAccessibilityInteractionConnection { IAccessibilityInteractionConnectionCallback callback, int flags, int interrogatingPid, long interrogatingTid, MagnificationSpec spec, Bundle arguments) throws RemoteException { - mShellMainExcutor.execute(() -> { + mMainExcutor.execute(() -> { PipAccessibilityInteractionConnection.this .findAccessibilityNodeInfoByAccessibilityId(accessibilityNodeId, bounds, interactionId, callback, flags, interrogatingPid, interrogatingTid, @@ -285,7 +285,7 @@ public class PipAccessibilityInteractionConnection { IAccessibilityInteractionConnectionCallback callback, int flags, int interrogatingPid, long interrogatingTid, MagnificationSpec spec) throws RemoteException { - mShellMainExcutor.execute(() -> { + mMainExcutor.execute(() -> { PipAccessibilityInteractionConnection.this.findAccessibilityNodeInfosByViewId( accessibilityNodeId, viewId, bounds, interactionId, callback, flags, interrogatingPid, interrogatingTid, spec); @@ -298,7 +298,7 @@ public class PipAccessibilityInteractionConnection { IAccessibilityInteractionConnectionCallback callback, int flags, int interrogatingPid, long interrogatingTid, MagnificationSpec spec) throws RemoteException { - mShellMainExcutor.execute(() -> { + mMainExcutor.execute(() -> { PipAccessibilityInteractionConnection.this.findAccessibilityNodeInfosByText( accessibilityNodeId, text, bounds, interactionId, callback, flags, interrogatingPid, interrogatingTid, spec); @@ -310,7 +310,7 @@ public class PipAccessibilityInteractionConnection { int interactionId, IAccessibilityInteractionConnectionCallback callback, int flags, int interrogatingPid, long interrogatingTid, MagnificationSpec spec) throws RemoteException { - mShellMainExcutor.execute(() -> { + mMainExcutor.execute(() -> { PipAccessibilityInteractionConnection.this.findFocus(accessibilityNodeId, focusType, bounds, interactionId, callback, flags, interrogatingPid, interrogatingTid, spec); @@ -322,7 +322,7 @@ public class PipAccessibilityInteractionConnection { int interactionId, IAccessibilityInteractionConnectionCallback callback, int flags, int interrogatingPid, long interrogatingTid, MagnificationSpec spec) throws RemoteException { - mShellMainExcutor.execute(() -> { + mMainExcutor.execute(() -> { PipAccessibilityInteractionConnection.this.focusSearch(accessibilityNodeId, direction, bounds, interactionId, callback, flags, interrogatingPid, interrogatingTid, @@ -335,7 +335,7 @@ public class PipAccessibilityInteractionConnection { Bundle arguments, int interactionId, IAccessibilityInteractionConnectionCallback callback, int flags, int interrogatingPid, long interrogatingTid) throws RemoteException { - mShellMainExcutor.execute(() -> { + mMainExcutor.execute(() -> { PipAccessibilityInteractionConnection.this.performAccessibilityAction( accessibilityNodeId, action, arguments, interactionId, callback, flags, interrogatingPid, interrogatingTid); 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 5ab1c390a92b..854385020f03 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 @@ -44,6 +44,18 @@ fun LayersAssertion.appPairsDividerIsInvisible( } @JvmOverloads +fun LayersAssertion.appPairsDividerBecomesVisible( + bugId: Int = 0, + enabled: Boolean = bugId == 0 +) { + all("dividerLayerBecomesVisible") { + this.hidesLayer(FlickerTestBase.DOCKED_STACK_DIVIDER) + .then() + .showsLayer(FlickerTestBase.DOCKED_STACK_DIVIDER) + } +} + +@JvmOverloads fun LayersAssertion.dockedStackDividerIsVisible( bugId: Int = 0, enabled: Boolean = bugId == 0 diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/EnterLegacySplitScreenTest.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/EnterLegacySplitScreenTest.kt index b33fa55c2be1..85bf4a1f8c25 100644 --- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/EnterLegacySplitScreenTest.kt +++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/EnterLegacySplitScreenTest.kt @@ -21,7 +21,6 @@ import android.view.Surface import androidx.test.filters.RequiresDevice import com.android.server.wm.flicker.dsl.FlickerBuilder import com.android.server.wm.flicker.dsl.runWithFlicker -import com.android.server.wm.flicker.helpers.WindowUtils import com.android.server.wm.flicker.helpers.canSplitScreen import com.android.server.wm.flicker.helpers.exitSplitScreen import com.android.server.wm.flicker.helpers.isInSplitScreen @@ -35,6 +34,7 @@ import com.android.server.wm.flicker.navBarWindowIsAlwaysVisible import com.android.server.wm.flicker.statusBarLayerIsAlwaysVisible import com.android.server.wm.flicker.navBarLayerIsAlwaysVisible import com.android.server.wm.flicker.statusBarWindowIsAlwaysVisible + import com.android.wm.shell.flicker.dockedStackPrimaryBoundsIsVisible import com.android.wm.shell.flicker.dockedStackSecondaryBoundsIsVisible import org.junit.Assert @@ -59,8 +59,6 @@ class EnterLegacySplitScreenTest( rotationName: String, rotation: Int ) : SplitScreenTestBase(rotationName, rotation) { - private val letterBox = "Letterbox" - private val splitScreenSetup: FlickerBuilder get() = FlickerBuilder(instrumentation).apply { val testLaunchActivity = "launch_splitScreen_test_activity" @@ -91,7 +89,6 @@ class EnterLegacySplitScreenTest( windowManagerTrace { navBarWindowIsAlwaysVisible() statusBarWindowIsAlwaysVisible() - visibleWindowsShownMoreThanOneConsecutiveEntry(listOf(launcherPackageName)) } } } @@ -114,7 +111,8 @@ class EnterLegacySplitScreenTest( rotation, splitScreenApp.defaultWindowName, 169271943) dockedStackDividerBecomesVisible() visibleLayersShownMoreThanOneConsecutiveEntry( - listOf(launcherPackageName, splitScreenApp.defaultWindowName) + listOf(LAUNCHER_PACKAGE_NAME, splitScreenApp.defaultWindowName, + LIVE_WALLPAPER_PACKAGE_NAME) ) } windowManagerTrace { @@ -148,7 +146,7 @@ class EnterLegacySplitScreenTest( rotation, secondaryApp.defaultWindowName, 169271943) dockedStackDividerBecomesVisible() visibleLayersShownMoreThanOneConsecutiveEntry( - listOf(launcherPackageName, splitScreenApp.defaultWindowName, + listOf(LAUNCHER_PACKAGE_NAME, splitScreenApp.defaultWindowName, secondaryApp.defaultWindowName) ) } @@ -157,6 +155,7 @@ class EnterLegacySplitScreenTest( showsAppWindow(splitScreenApp.defaultWindowName) .and().showsAppWindow(secondaryApp.defaultWindowName) } + visibleWindowsShownMoreThanOneConsecutiveEntry(listOf(LAUNCHER_PACKAGE_NAME)) } } } @@ -181,85 +180,14 @@ class EnterLegacySplitScreenTest( layersTrace { dockedStackDividerIsInvisible() visibleLayersShownMoreThanOneConsecutiveEntry( - listOf(launcherPackageName, nonResizeableApp.defaultWindowName) + listOf(LAUNCHER_PACKAGE_NAME, nonResizeableApp.defaultWindowName) ) } windowManagerTrace { end { hidesAppWindow(nonResizeableApp.defaultWindowName) } - } - } - } - } - - @Test - fun testNonResizeableWhenAlreadyInSplitScreenPrimary() { - val testTag = "testNonResizeableWhenAlreadyInSplitScreenPrimary" - runWithFlicker(splitScreenSetup) { - withTestName { testTag } - repeat { - TEST_REPETITIONS - } - transitions { - nonResizeableApp.launchViaIntent() - splitScreenApp.launchViaIntent() - uiDevice.launchSplitScreen() - nonResizeableApp.reopenAppFromOverview() - } - assertions { - layersTrace { - dockedStackDividerIsInvisible() - end("appsEndingBounds", enabled = false) { - val displayBounds = WindowUtils.getDisplayBounds(rotation) - this.hasVisibleRegion(nonResizeableApp.defaultWindowName, displayBounds) - } - visibleLayersShownMoreThanOneConsecutiveEntry( - listOf(launcherPackageName, splitScreenApp.defaultWindowName, - nonResizeableApp.defaultWindowName, letterBox) - ) - } - windowManagerTrace { - end { - showsAppWindow(nonResizeableApp.defaultWindowName) - hidesAppWindow(splitScreenApp.defaultWindowName) - } - } - } - } - } - - @Test - fun testNonResizeableWhenAlreadyInSplitScreenSecondary() { - val testTag = "testNonResizeableWhenAlreadyInSplitScreenSecondary" - runWithFlicker(splitScreenSetup) { - withTestName { testTag } - repeat { - TEST_REPETITIONS - } - transitions { - splitScreenApp.launchViaIntent() - uiDevice.launchSplitScreen() - uiDevice.pressBack() - nonResizeableApp.launchViaIntent() - } - assertions { - layersTrace { - dockedStackDividerIsInvisible() - end("appsEndingBounds", enabled = false) { - val displayBounds = WindowUtils.getDisplayBounds(rotation) - this.hasVisibleRegion(nonResizeableApp.defaultWindowName, displayBounds) - } - visibleLayersShownMoreThanOneConsecutiveEntry( - listOf(launcherPackageName, splitScreenApp.defaultWindowName, - nonResizeableApp.defaultWindowName, letterBox) - ) - } - windowManagerTrace { - end { - showsAppWindow(nonResizeableApp.defaultWindowName) - hidesAppWindow(splitScreenApp.defaultWindowName) - } + visibleWindowsShownMoreThanOneConsecutiveEntry(listOf(LAUNCHER_PACKAGE_NAME)) } } } diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/ExitLegacySplitScreenFromBottomTest.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/ExitLegacySplitScreenFromBottomTest.kt index 573ffc6c3299..9586fd139eb5 100644 --- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/ExitLegacySplitScreenFromBottomTest.kt +++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/ExitLegacySplitScreenFromBottomTest.kt @@ -22,16 +22,20 @@ import androidx.test.platform.app.InstrumentationRegistry import com.android.server.wm.flicker.Flicker import com.android.server.wm.flicker.FlickerTestRunner import com.android.server.wm.flicker.FlickerTestRunnerFactory -import com.android.server.wm.flicker.helpers.StandardAppHelper import com.android.server.wm.flicker.endRotation import com.android.server.wm.flicker.helpers.buildTestTag import com.android.server.wm.flicker.helpers.exitSplitScreen import com.android.server.wm.flicker.helpers.exitSplitScreenFromBottom import com.android.server.wm.flicker.helpers.isInSplitScreen import com.android.server.wm.flicker.helpers.launchSplitScreen +import com.android.server.wm.flicker.helpers.openQuickStepAndClearRecentAppsFromOverview import com.android.server.wm.flicker.helpers.setRotation import com.android.server.wm.flicker.helpers.wakeUpAndGoToHomeScreen + import com.android.server.wm.flicker.repetitions +import com.android.wm.shell.flicker.TEST_APP_SPLITSCREEN_PRIMARY_LABEL +import com.android.wm.shell.flicker.helpers.SplitScreenHelper +import com.android.wm.shell.flicker.testapp.Components import org.junit.FixMethodOrder import org.junit.runner.RunWith import org.junit.runners.MethodSorters @@ -53,23 +57,24 @@ class ExitLegacySplitScreenFromBottomTest( @JvmStatic fun getParams(): Collection<Array<Any>> { val instrumentation = InstrumentationRegistry.getInstrumentation() - val testApp = StandardAppHelper(instrumentation, - "com.android.wm.shell.flicker.testapp", "SimpleApp") + val splitScreenApp = SplitScreenHelper(instrumentation, + TEST_APP_SPLITSCREEN_PRIMARY_LABEL, + Components.SplitScreenActivity()) - // b/161435597 causes the test not to work on 90 degrees - return FlickerTestRunnerFactory(instrumentation, listOf(Surface.ROTATION_0)) + // TODO(b/162923992) Use of multiple segments of flicker spec for testing + return FlickerTestRunnerFactory(instrumentation, + listOf(Surface.ROTATION_0, Surface.ROTATION_90)) .buildTest { configuration -> withTestName { - buildTestTag("exitSplitScreenFromBottom", testApp, + buildTestTag("exitSplitScreenFromBottom", splitScreenApp, configuration) } repeat { configuration.repetitions } setup { - test { - device.wakeUpAndGoToHomeScreen() - } eachRun { - testApp.open() + device.wakeUpAndGoToHomeScreen() + device.openQuickStepAndClearRecentAppsFromOverview() + splitScreenApp.launchViaIntent() device.launchSplitScreen() device.waitForIdle() this.setRotation(configuration.endRotation) @@ -77,12 +82,10 @@ class ExitLegacySplitScreenFromBottomTest( } teardown { eachRun { - testApp.exit() - } - test { if (device.isInSplitScreen()) { device.exitSplitScreen() } + splitScreenApp.exit() } } transitions { diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/ExitLegacySplitScreenTest.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/ExitLegacySplitScreenTest.kt index c51c73a9e248..84bfe9451e0a 100644 --- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/ExitLegacySplitScreenTest.kt +++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/ExitLegacySplitScreenTest.kt @@ -82,7 +82,7 @@ class ExitLegacySplitScreenTest( } layersTrace { visibleLayersShownMoreThanOneConsecutiveEntry( - listOf(launcherPackageName)) + listOf(LAUNCHER_PACKAGE_NAME)) } } } diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/NonResizableDismissInLegacySplitScreenTest.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/NonResizableDismissInLegacySplitScreenTest.kt new file mode 100644 index 000000000000..e9d3eb7f475d --- /dev/null +++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/NonResizableDismissInLegacySplitScreenTest.kt @@ -0,0 +1,88 @@ +/* + * 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.wm.shell.flicker.legacysplitscreen + +import android.view.Surface +import androidx.test.filters.RequiresDevice +import com.android.server.wm.flicker.dsl.runWithFlicker +import com.android.server.wm.flicker.helpers.WindowUtils +import com.android.server.wm.flicker.helpers.launchSplitScreen +import com.android.server.wm.flicker.visibleLayersShownMoreThanOneConsecutiveEntry +import com.android.wm.shell.flicker.dockedStackDividerIsInvisible +import com.android.wm.shell.flicker.helpers.SplitScreenHelper +import org.junit.FixMethodOrder +import org.junit.Test +import org.junit.runner.RunWith +import org.junit.runners.MethodSorters +import org.junit.runners.Parameterized + +/** + * Test open app to split screen. + * To run this test: `atest WMShellFlickerTests:NonResizableDismissInLegacySplitScreenTest` + */ +@RequiresDevice +@RunWith(Parameterized::class) +@FixMethodOrder(MethodSorters.NAME_ASCENDING) +class NonResizableDismissInLegacySplitScreenTest( + rotationName: String, + rotation: Int +) : SplitScreenTestBase(rotationName, rotation) { + + @Test + fun testNonResizableDismissInLegacySplitScreenTest() { + val testTag = "testNonResizableDismissInLegacySplitScreenTest" + + runWithFlicker(transitionSetup) { + withTestName { testTag } + repeat { SplitScreenHelper.TEST_REPETITIONS } + transitions { + nonResizeableApp.launchViaIntent() + splitScreenApp.launchViaIntent() + device.launchSplitScreen() + nonResizeableApp.reopenAppFromOverview() + } + assertions { + layersTrace { + dockedStackDividerIsInvisible() + end("appsEndingBounds", enabled = false) { + val displayBounds = WindowUtils.getDisplayBounds(rotation) + this.hasVisibleRegion(nonResizeableApp.defaultWindowName, displayBounds) + } + visibleLayersShownMoreThanOneConsecutiveEntry( + listOf(LAUNCHER_PACKAGE_NAME, splitScreenApp.defaultWindowName, + nonResizeableApp.defaultWindowName, LETTER_BOX_NAME) + ) + } + windowManagerTrace { + end { + showsAppWindow(nonResizeableApp.defaultWindowName) + hidesAppWindow(splitScreenApp.defaultWindowName) + } + } + } + } + } + + companion object { + @Parameterized.Parameters(name = "{0}") + @JvmStatic + fun getParams(): Collection<Array<Any>> { + val supportedRotations = intArrayOf(Surface.ROTATION_0, Surface.ROTATION_90) + return supportedRotations.map { arrayOf(Surface.rotationToString(it), it) } + } + } +}
\ No newline at end of file diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/NonResizableLaunchInLegacySplitScreenTest.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/NonResizableLaunchInLegacySplitScreenTest.kt new file mode 100644 index 000000000000..b5a36f5a31d4 --- /dev/null +++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/NonResizableLaunchInLegacySplitScreenTest.kt @@ -0,0 +1,88 @@ +/* + * 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.wm.shell.flicker.legacysplitscreen + +import android.view.Surface +import androidx.test.filters.RequiresDevice +import com.android.server.wm.flicker.dsl.runWithFlicker +import com.android.server.wm.flicker.helpers.WindowUtils +import com.android.server.wm.flicker.helpers.launchSplitScreen +import com.android.server.wm.flicker.visibleLayersShownMoreThanOneConsecutiveEntry +import com.android.wm.shell.flicker.dockedStackDividerIsInvisible +import com.android.wm.shell.flicker.helpers.SplitScreenHelper +import org.junit.FixMethodOrder +import org.junit.Test +import org.junit.runner.RunWith +import org.junit.runners.MethodSorters +import org.junit.runners.Parameterized + +/** + * Test open app to split screen. + * To run this test: `atest WMShellFlickerTests:NonResizableLaunchInLegacySplitScreenTest` + */ +@RequiresDevice +@RunWith(Parameterized::class) +@FixMethodOrder(MethodSorters.NAME_ASCENDING) +class NonResizableLaunchInLegacySplitScreenTest( + rotationName: String, + rotation: Int +) : SplitScreenTestBase(rotationName, rotation) { + + @Test + fun testNonResizableLaunchInLegacySplitScreenTest() { + val testTag = "NonResizableLaunchInLegacySplitScreenTest" + + runWithFlicker(transitionSetup) { + withTestName { testTag } + repeat { SplitScreenHelper.TEST_REPETITIONS } + transitions { + nonResizeableApp.launchViaIntent() + splitScreenApp.launchViaIntent() + device.launchSplitScreen() + nonResizeableApp.reopenAppFromOverview() + } + assertions { + layersTrace { + dockedStackDividerIsInvisible() + end("appsEndingBounds", enabled = false) { + val displayBounds = WindowUtils.getDisplayBounds(rotation) + this.hasVisibleRegion(nonResizeableApp.defaultWindowName, displayBounds) + } + visibleLayersShownMoreThanOneConsecutiveEntry( + listOf(LAUNCHER_PACKAGE_NAME, splitScreenApp.defaultWindowName, + nonResizeableApp.defaultWindowName, LETTER_BOX_NAME) + ) + } + windowManagerTrace { + end { + showsAppWindow(nonResizeableApp.defaultWindowName) + hidesAppWindow(splitScreenApp.defaultWindowName) + } + } + } + } + } + + companion object { + @Parameterized.Parameters(name = "{0}") + @JvmStatic + fun getParams(): Collection<Array<Any>> { + val supportedRotations = intArrayOf(Surface.ROTATION_0, Surface.ROTATION_90) + return supportedRotations.map { arrayOf(Surface.rotationToString(it), it) } + } + } +}
\ No newline at end of file diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/OpenAppToLegacySplitScreenTest.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/OpenAppToLegacySplitScreenTest.kt index af038698de80..90577ef19c1a 100644 --- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/OpenAppToLegacySplitScreenTest.kt +++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/OpenAppToLegacySplitScreenTest.kt @@ -17,37 +17,22 @@ package com.android.wm.shell.flicker.legacysplitscreen import android.platform.test.annotations.Presubmit -import android.support.test.launcherhelper.LauncherStrategyFactory import android.view.Surface import androidx.test.filters.RequiresDevice -import androidx.test.platform.app.InstrumentationRegistry -import com.android.server.wm.flicker.Flicker -import com.android.server.wm.flicker.FlickerTestRunner -import com.android.server.wm.flicker.FlickerTestRunnerFactory -import com.android.server.wm.flicker.helpers.StandardAppHelper -import com.android.server.wm.flicker.endRotation -import com.android.server.wm.flicker.focusChanges -import com.android.server.wm.flicker.helpers.buildTestTag -import com.android.server.wm.flicker.helpers.exitSplitScreen -import com.android.server.wm.flicker.helpers.isInSplitScreen -import com.android.server.wm.flicker.helpers.launchSplitScreen -import com.android.server.wm.flicker.helpers.openQuickStepAndClearRecentAppsFromOverview -import com.android.server.wm.flicker.helpers.setRotation -import com.android.server.wm.flicker.helpers.wakeUpAndGoToHomeScreen -import com.android.server.wm.flicker.navBarLayerIsAlwaysVisible -import com.android.server.wm.flicker.navBarLayerRotatesAndScales import com.android.server.wm.flicker.appWindowBecomesVisible -import com.android.server.wm.flicker.visibleWindowsShownMoreThanOneConsecutiveEntry +import com.android.server.wm.flicker.statusBarLayerIsAlwaysVisible import com.android.server.wm.flicker.visibleLayersShownMoreThanOneConsecutiveEntry import com.android.server.wm.flicker.layerBecomesVisible -import com.android.server.wm.flicker.navBarWindowIsAlwaysVisible +import com.android.server.wm.flicker.focusChanges +import com.android.server.wm.flicker.helpers.launchSplitScreen +import com.android.server.wm.flicker.dsl.runWithFlicker +import com.android.server.wm.flicker.navBarLayerIsAlwaysVisible import com.android.server.wm.flicker.noUncoveredRegions -import com.android.server.wm.flicker.repetitions -import com.android.server.wm.flicker.statusBarLayerIsAlwaysVisible -import com.android.server.wm.flicker.statusBarLayerRotatesScales -import com.android.server.wm.flicker.statusBarWindowIsAlwaysVisible -import com.android.wm.shell.flicker.dockedStackDividerBecomesVisible +import com.android.server.wm.flicker.visibleWindowsShownMoreThanOneConsecutiveEntry +import com.android.wm.shell.flicker.appPairsDividerBecomesVisible +import com.android.wm.shell.flicker.helpers.SplitScreenHelper import org.junit.FixMethodOrder +import org.junit.Test import org.junit.runner.RunWith import org.junit.runners.MethodSorters import org.junit.runners.Parameterized @@ -56,85 +41,59 @@ import org.junit.runners.Parameterized * Test open app to split screen. * To run this test: `atest WMShellFlickerTests:OpenAppToLegacySplitScreenTest` */ -@Presubmit +// TODO: Add back to pre-submit when stable. +//@Presubmit @RequiresDevice @RunWith(Parameterized::class) @FixMethodOrder(MethodSorters.NAME_ASCENDING) class OpenAppToLegacySplitScreenTest( - testName: String, - flickerSpec: Flicker -) : FlickerTestRunner(testName, flickerSpec) { - companion object { - @Parameterized.Parameters(name = "{0}") - @JvmStatic - fun getParams(): Collection<Array<Any>> { - val instrumentation = InstrumentationRegistry.getInstrumentation() - val launcherPackageName = LauncherStrategyFactory.getInstance(instrumentation) - .launcherStrategy.supportedLauncherPackage - val testApp = StandardAppHelper(instrumentation, - "com.android.wm.shell.flicker.testapp", "SimpleApp") - - // b/161435597 causes the test not to work on 90 degrees - return FlickerTestRunnerFactory(instrumentation, listOf(Surface.ROTATION_0)) - .buildTest { configuration -> - withTestName { - buildTestTag("appToSplitScreen", testApp, configuration) - } - repeat { configuration.repetitions } - setup { - test { - device.wakeUpAndGoToHomeScreen() - device.openQuickStepAndClearRecentAppsFromOverview() - } - eachRun { - testApp.open() - device.pressHome() - this.setRotation(configuration.endRotation) - } - } - teardown { - eachRun { - if (device.isInSplitScreen()) { - device.exitSplitScreen() - } - } - test { - testApp.exit() - } - } - transitions { - device.launchSplitScreen() - } - assertions { - windowManagerTrace { - navBarWindowIsAlwaysVisible() - statusBarWindowIsAlwaysVisible() - visibleWindowsShownMoreThanOneConsecutiveEntry() - - appWindowBecomesVisible(testApp.getPackage()) - } + rotationName: String, + rotation: Int +) : SplitScreenTestBase(rotationName, rotation) { + @Test + fun OpenAppToLegacySplitScreenTest() { + val testTag = "OpenAppToLegacySplitScreenTest" - layersTrace { - navBarLayerIsAlwaysVisible(bugId = 140855415) - statusBarLayerIsAlwaysVisible() - noUncoveredRegions(configuration.endRotation, enabled = false) - navBarLayerRotatesAndScales(configuration.endRotation, - bugId = 140855415) - statusBarLayerRotatesScales(configuration.endRotation) - visibleLayersShownMoreThanOneConsecutiveEntry( - listOf(launcherPackageName)) + runWithFlicker(transitionSetup) { + withTestName { testTag } + repeat { SplitScreenHelper.TEST_REPETITIONS } + transitions { + splitScreenApp.launchViaIntent() + device.pressHome() + this.setRotation(rotation) + device.launchSplitScreen() + } + assertions { + windowManagerTrace { + visibleWindowsShownMoreThanOneConsecutiveEntry() + appWindowBecomesVisible(splitScreenApp.getPackage()) + } - dockedStackDividerBecomesVisible() - layerBecomesVisible(testApp.getPackage()) - } + layersTrace { + navBarLayerIsAlwaysVisible(bugId = 140855415) + noUncoveredRegions(rotation, enabled = false) + statusBarLayerIsAlwaysVisible(bugId = 140855415) + visibleLayersShownMoreThanOneConsecutiveEntry( + listOf(LAUNCHER_PACKAGE_NAME)) + appPairsDividerBecomesVisible() + layerBecomesVisible(splitScreenApp.getPackage()) + } - eventLog { - focusChanges(testApp.`package`, - "recents_animation_input_consumer", "NexusLauncherActivity", - bugId = 151179149) - } - } + eventLog { + focusChanges(splitScreenApp.`package`, + "recents_animation_input_consumer", "NexusLauncherActivity", + bugId = 151179149) } + } + } + } + + companion object { + @Parameterized.Parameters(name = "{0}") + @JvmStatic + fun getParams(): Collection<Array<Any>> { + val supportedRotations = intArrayOf(Surface.ROTATION_0, Surface.ROTATION_90) + return supportedRotations.map { arrayOf(Surface.rotationToString(it), it) } } } } diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/SplitScreenTestBase.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/SplitScreenTestBase.kt index a536ec8e2e0b..2b94c5f3fee9 100644 --- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/SplitScreenTestBase.kt +++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/SplitScreenTestBase.kt @@ -17,6 +17,15 @@ package com.android.wm.shell.flicker.legacysplitscreen import android.support.test.launcherhelper.LauncherStrategyFactory +import com.android.server.wm.flicker.dsl.FlickerBuilder +import com.android.server.wm.flicker.helpers.exitSplitScreen +import com.android.server.wm.flicker.helpers.isInSplitScreen +import com.android.server.wm.flicker.helpers.openQuickStepAndClearRecentAppsFromOverview +import com.android.server.wm.flicker.helpers.wakeUpAndGoToHomeScreen +import com.android.server.wm.flicker.navBarLayerIsAlwaysVisible +import com.android.server.wm.flicker.navBarWindowIsAlwaysVisible +import com.android.server.wm.flicker.statusBarLayerIsAlwaysVisible +import com.android.server.wm.flicker.statusBarWindowIsAlwaysVisible import com.android.wm.shell.flicker.NonRotationTestBase import com.android.wm.shell.flicker.TEST_APP_NONRESIZEABLE_LABEL import com.android.wm.shell.flicker.TEST_APP_SPLITSCREEN_PRIMARY_LABEL @@ -37,6 +46,39 @@ abstract class SplitScreenTestBase( protected val nonResizeableApp = SplitScreenHelper(instrumentation, TEST_APP_NONRESIZEABLE_LABEL, Components.NonResizeableActivity()) - protected val launcherPackageName = LauncherStrategyFactory.getInstance(instrumentation) + + protected val LAUNCHER_PACKAGE_NAME = LauncherStrategyFactory.getInstance(instrumentation) .launcherStrategy.supportedLauncherPackage + protected val LIVE_WALLPAPER_PACKAGE_NAME = + "com.breel.wallpapers18.soundviz.wallpaper.variations.SoundVizWallpaperV2" + protected val LETTER_BOX_NAME = "Letterbox" + + protected val transitionSetup: FlickerBuilder + get() = FlickerBuilder(instrumentation).apply { + setup { + eachRun { + uiDevice.wakeUpAndGoToHomeScreen() + uiDevice.openQuickStepAndClearRecentAppsFromOverview() + } + } + teardown { + eachRun { + if (uiDevice.isInSplitScreen()) { + uiDevice.exitSplitScreen() + } + splitScreenApp.exit() + nonResizeableApp.exit() + } + } + assertions { + layersTrace { + navBarLayerIsAlwaysVisible() + statusBarLayerIsAlwaysVisible() + } + windowManagerTrace { + navBarWindowIsAlwaysVisible() + statusBarWindowIsAlwaysVisible() + } + } + } } diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/onehanded/OneHandedAnimationControllerTest.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/onehanded/OneHandedAnimationControllerTest.java index a8a3a9fd7da2..17fc0578dd2b 100644 --- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/onehanded/OneHandedAnimationControllerTest.java +++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/onehanded/OneHandedAnimationControllerTest.java @@ -25,6 +25,8 @@ import android.view.SurfaceControl; import androidx.test.filters.SmallTest; +import com.android.wm.shell.common.ShellExecutor; + import org.junit.Before; import org.junit.Test; import org.junit.runner.RunWith; @@ -49,11 +51,14 @@ public class OneHandedAnimationControllerTest extends OneHandedTestCase { @Mock private SurfaceControl mMockLeash; + @Mock + private ShellExecutor mMainExecutor; + @Before public void setUp() throws Exception { MockitoAnnotations.initMocks(this); - mTutorialHandler = new OneHandedTutorialHandler(mContext); + mTutorialHandler = new OneHandedTutorialHandler(mContext, mMainExecutor); mOneHandedAnimationController = new OneHandedAnimationController(mContext); } 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 20184bfd5541..16d13f40f840 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 @@ -25,6 +25,7 @@ import static org.mockito.Mockito.verify; import static org.mockito.Mockito.when; import android.content.om.IOverlayManager; +import android.os.Handler; import android.provider.Settings; import android.testing.AndroidTestingRunner; import android.testing.TestableLooper; @@ -33,6 +34,7 @@ import android.view.Display; import androidx.test.filters.SmallTest; import com.android.wm.shell.common.DisplayController; +import com.android.wm.shell.common.ShellExecutor; import com.android.wm.shell.common.TaskStackListenerImpl; import org.junit.Before; @@ -45,7 +47,6 @@ import org.mockito.MockitoAnnotations; @SmallTest @RunWith(AndroidTestingRunner.class) -@TestableLooper.RunWithLooper public class OneHandedControllerTest extends OneHandedTestCase { Display mDisplay; OneHandedController mOneHandedController; @@ -69,11 +70,16 @@ public class OneHandedControllerTest extends OneHandedTestCase { IOverlayManager mMockOverlayManager; @Mock TaskStackListenerImpl mMockTaskStackListener; + @Mock + ShellExecutor mMockShellMainExecutor; + @Mock + Handler mMockShellMainHandler; @Before public void setUp() throws Exception { MockitoAnnotations.initMocks(this); mDisplay = mContext.getDisplay(); + mTimeoutHandler = Mockito.spy(new OneHandedTimeoutHandler(mMockShellMainExecutor)); OneHandedController oneHandedController = new OneHandedController( mContext, mMockDisplayController, @@ -82,10 +88,12 @@ public class OneHandedControllerTest extends OneHandedTestCase { mMockTouchHandler, mMockTutorialHandler, mMockGestureHandler, + mTimeoutHandler, mMockOverlayManager, - mMockTaskStackListener); + mMockTaskStackListener, + mMockShellMainExecutor, + mMockShellMainHandler); mOneHandedController = Mockito.spy(oneHandedController); - mTimeoutHandler = Mockito.spy(OneHandedTimeoutHandler.get()); when(mMockDisplayController.getDisplay(anyInt())).thenReturn(mDisplay); when(mMockDisplayAreaOrganizer.isInOneHanded()).thenReturn(false); @@ -97,7 +105,7 @@ public class OneHandedControllerTest extends OneHandedTestCase { mContext); OneHandedDisplayAreaOrganizer displayAreaOrganizer = new OneHandedDisplayAreaOrganizer( mContext, mMockDisplayController, animationController, mMockTutorialHandler, - Runnable::run, mMockBackgroundOrganizer); + mMockBackgroundOrganizer, mMockShellMainExecutor); assertThat(displayAreaOrganizer.isInOneHanded()).isFalse(); } 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 3d9fad9097f8..6cfd0c43724c 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 @@ -44,6 +44,7 @@ import android.window.WindowContainerTransaction; import androidx.test.filters.SmallTest; import com.android.wm.shell.common.DisplayController; +import com.android.wm.shell.common.ShellExecutor; import org.junit.Before; import org.junit.Test; @@ -53,7 +54,6 @@ import org.mockito.MockitoAnnotations; @SmallTest @RunWith(AndroidTestingRunner.class) -@TestableLooper.RunWithLooper public class OneHandedDisplayAreaOrganizerTest extends OneHandedTestCase { static final int DISPLAY_WIDTH = 1000; static final int DISPLAY_HEIGHT = 1000; @@ -82,9 +82,8 @@ public class OneHandedDisplayAreaOrganizerTest extends OneHandedTestCase { WindowContainerTransaction mMockWindowContainerTransaction; @Mock OneHandedBackgroundPanelOrganizer mMockBackgroundOrganizer; - - Handler mSpyUpdateHandler; - Handler.Callback mUpdateCallback = (msg) -> false; + @Mock + ShellExecutor mMockShellMainExecutor; @Before public void setUp() throws Exception { @@ -110,19 +109,17 @@ public class OneHandedDisplayAreaOrganizerTest extends OneHandedTestCase { when(mMockLeash.getWidth()).thenReturn(DISPLAY_WIDTH); when(mMockLeash.getHeight()).thenReturn(DISPLAY_HEIGHT); - mDisplayAreaOrganizer = new OneHandedDisplayAreaOrganizer(mContext, + mDisplayAreaOrganizer = spy(new OneHandedDisplayAreaOrganizer(mContext, mMockDisplayController, mMockAnimationController, mTutorialHandler, - Runnable::run, mMockBackgroundOrganizer); - mSpyUpdateHandler = spy(new Handler(OneHandedThread.get().getLooper(), mUpdateCallback)); - mDisplayAreaOrganizer.setUpdateHandler(mSpyUpdateHandler); + mMockBackgroundOrganizer, + mMockShellMainExecutor)); } @Test public void testOnDisplayAreaAppeared() { mDisplayAreaOrganizer.onDisplayAreaAppeared(mDisplayAreaInfo, mLeash); - mTestableLooper.processAllMessages(); verify(mMockAnimationController, never()).getAnimator(any(), any(), any()); } @@ -130,31 +127,18 @@ public class OneHandedDisplayAreaOrganizerTest extends OneHandedTestCase { @Test public void testOnDisplayAreaVanished() { mDisplayAreaOrganizer.onDisplayAreaAppeared(mDisplayAreaInfo, mLeash); - mTestableLooper.processAllMessages(); mDisplayAreaOrganizer.onDisplayAreaVanished(mDisplayAreaInfo); assertThat(mDisplayAreaOrganizer.mDisplayAreaMap).isEmpty(); } @Test - public void testScheduleOffset() { - final int xOffSet = 0; - final int yOffSet = 100; - mDisplayAreaOrganizer.scheduleOffset(xOffSet, yOffSet); - mTestableLooper.processAllMessages(); - - verify(mSpyUpdateHandler).sendMessage(any()); - } - - @Test public void testRotation_portrait_0_to_landscape_90() { when(mMockLeash.isValid()).thenReturn(false); // Rotate 0 -> 90 mDisplayAreaOrganizer.onRotateDisplay(Surface.ROTATION_0, Surface.ROTATION_90, mMockWindowContainerTransaction); - mTestableLooper.processAllMessages(); - - verify(mSpyUpdateHandler).sendMessage(any()); + verify(mDisplayAreaOrganizer).finishOffset(anyInt(), anyInt()); } @Test @@ -163,9 +147,7 @@ public class OneHandedDisplayAreaOrganizerTest extends OneHandedTestCase { // Rotate 0 -> 270 mDisplayAreaOrganizer.onRotateDisplay(Surface.ROTATION_0, Surface.ROTATION_270, mMockWindowContainerTransaction); - mTestableLooper.processAllMessages(); - - verify(mSpyUpdateHandler).sendMessage(any()); + verify(mDisplayAreaOrganizer).finishOffset(anyInt(), anyInt()); } @Test @@ -174,9 +156,7 @@ public class OneHandedDisplayAreaOrganizerTest extends OneHandedTestCase { // Rotate 180 -> 90 mDisplayAreaOrganizer.onRotateDisplay(Surface.ROTATION_180, Surface.ROTATION_90, mMockWindowContainerTransaction); - mTestableLooper.processAllMessages(); - - verify(mSpyUpdateHandler).sendMessage(any()); + verify(mDisplayAreaOrganizer).finishOffset(anyInt(), anyInt()); } @Test @@ -185,9 +165,7 @@ public class OneHandedDisplayAreaOrganizerTest extends OneHandedTestCase { // Rotate 180 -> 270 mDisplayAreaOrganizer.onRotateDisplay(Surface.ROTATION_180, Surface.ROTATION_270, mMockWindowContainerTransaction); - mTestableLooper.processAllMessages(); - - verify(mSpyUpdateHandler).sendMessage(any()); + verify(mDisplayAreaOrganizer).finishOffset(anyInt(), anyInt()); } @Test @@ -196,9 +174,7 @@ public class OneHandedDisplayAreaOrganizerTest extends OneHandedTestCase { // Rotate 90 -> 0 mDisplayAreaOrganizer.onRotateDisplay(Surface.ROTATION_90, Surface.ROTATION_0, mMockWindowContainerTransaction); - mTestableLooper.processAllMessages(); - - verify(mSpyUpdateHandler).sendMessage(any()); + verify(mDisplayAreaOrganizer).finishOffset(anyInt(), anyInt()); } @Test @@ -207,9 +183,7 @@ public class OneHandedDisplayAreaOrganizerTest extends OneHandedTestCase { // Rotate 90 -> 180 mDisplayAreaOrganizer.onRotateDisplay(Surface.ROTATION_90, Surface.ROTATION_180, mMockWindowContainerTransaction); - mTestableLooper.processAllMessages(); - - verify(mSpyUpdateHandler).sendMessage(any()); + verify(mDisplayAreaOrganizer).finishOffset(anyInt(), anyInt()); } @Test @@ -218,9 +192,7 @@ public class OneHandedDisplayAreaOrganizerTest extends OneHandedTestCase { // Rotate 270 -> 0 mDisplayAreaOrganizer.onRotateDisplay(Surface.ROTATION_270, Surface.ROTATION_0, mMockWindowContainerTransaction); - mTestableLooper.processAllMessages(); - - verify(mSpyUpdateHandler).sendMessage(any()); + verify(mDisplayAreaOrganizer).finishOffset(anyInt(), anyInt()); } @Test @@ -229,9 +201,7 @@ public class OneHandedDisplayAreaOrganizerTest extends OneHandedTestCase { // Rotate 270 -> 180 mDisplayAreaOrganizer.onRotateDisplay(Surface.ROTATION_270, Surface.ROTATION_180, mMockWindowContainerTransaction); - mTestableLooper.processAllMessages(); - - verify(mSpyUpdateHandler).sendMessage(any()); + verify(mDisplayAreaOrganizer).finishOffset(anyInt(), anyInt()); } @Test @@ -240,9 +210,7 @@ public class OneHandedDisplayAreaOrganizerTest extends OneHandedTestCase { // Rotate 0 -> 0 mDisplayAreaOrganizer.onRotateDisplay(Surface.ROTATION_0, Surface.ROTATION_0, mMockWindowContainerTransaction); - mTestableLooper.processAllMessages(); - - verify(mSpyUpdateHandler, never()).sendMessage(any()); + verify(mDisplayAreaOrganizer, never()).finishOffset(anyInt(), anyInt()); } @Test @@ -251,9 +219,7 @@ public class OneHandedDisplayAreaOrganizerTest extends OneHandedTestCase { // Rotate 0 -> 180 mDisplayAreaOrganizer.onRotateDisplay(Surface.ROTATION_0, Surface.ROTATION_180, mMockWindowContainerTransaction); - mTestableLooper.processAllMessages(); - - verify(mSpyUpdateHandler, never()).sendMessage(any()); + verify(mDisplayAreaOrganizer, never()).finishOffset(anyInt(), anyInt()); } @Test @@ -262,9 +228,7 @@ public class OneHandedDisplayAreaOrganizerTest extends OneHandedTestCase { // Rotate 180 -> 180 mDisplayAreaOrganizer.onRotateDisplay(Surface.ROTATION_180, Surface.ROTATION_180, mMockWindowContainerTransaction); - mTestableLooper.processAllMessages(); - - verify(mSpyUpdateHandler, never()).sendMessage(any()); + verify(mDisplayAreaOrganizer, never()).finishOffset(anyInt(), anyInt()); } @Test @@ -273,9 +237,7 @@ public class OneHandedDisplayAreaOrganizerTest extends OneHandedTestCase { // Rotate 180 -> 0 mDisplayAreaOrganizer.onRotateDisplay(Surface.ROTATION_180, Surface.ROTATION_0, mMockWindowContainerTransaction); - mTestableLooper.processAllMessages(); - - verify(mSpyUpdateHandler, never()).sendMessage(any()); + verify(mDisplayAreaOrganizer, never()).finishOffset(anyInt(), anyInt()); } @Test @@ -284,9 +246,7 @@ public class OneHandedDisplayAreaOrganizerTest extends OneHandedTestCase { // Rotate 90 -> 90 mDisplayAreaOrganizer.onRotateDisplay(Surface.ROTATION_90, Surface.ROTATION_90, mMockWindowContainerTransaction); - mTestableLooper.processAllMessages(); - - verify(mSpyUpdateHandler, never()).sendMessage(any()); + verify(mDisplayAreaOrganizer, never()).finishOffset(anyInt(), anyInt()); } @Test @@ -295,9 +255,7 @@ public class OneHandedDisplayAreaOrganizerTest extends OneHandedTestCase { // Rotate 90 -> 270 mDisplayAreaOrganizer.onRotateDisplay(Surface.ROTATION_90, Surface.ROTATION_270, mMockWindowContainerTransaction); - mTestableLooper.processAllMessages(); - - verify(mSpyUpdateHandler, never()).sendMessage(any()); + verify(mDisplayAreaOrganizer, never()).finishOffset(anyInt(), anyInt()); } @Test @@ -306,9 +264,7 @@ public class OneHandedDisplayAreaOrganizerTest extends OneHandedTestCase { // Rotate 270 -> 270 mDisplayAreaOrganizer.onRotateDisplay(Surface.ROTATION_270, Surface.ROTATION_270, mMockWindowContainerTransaction); - mTestableLooper.processAllMessages(); - - verify(mSpyUpdateHandler, never()).sendMessage(any()); + verify(mDisplayAreaOrganizer, never()).finishOffset(anyInt(), anyInt()); } @Test @@ -317,8 +273,6 @@ public class OneHandedDisplayAreaOrganizerTest extends OneHandedTestCase { // Rotate 270 -> 90 mDisplayAreaOrganizer.onRotateDisplay(Surface.ROTATION_270, Surface.ROTATION_90, mMockWindowContainerTransaction); - mTestableLooper.processAllMessages(); - - verify(mSpyUpdateHandler, never()).sendMessage(any()); + verify(mDisplayAreaOrganizer, never()).finishOffset(anyInt(), anyInt()); } } diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/onehanded/OneHandedGestureHandlerTest.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/onehanded/OneHandedGestureHandlerTest.java index fb417c8ca5e8..e5f2ff717e37 100644 --- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/onehanded/OneHandedGestureHandlerTest.java +++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/onehanded/OneHandedGestureHandlerTest.java @@ -26,6 +26,7 @@ import android.testing.TestableLooper; import androidx.test.filters.SmallTest; import com.android.wm.shell.common.DisplayController; +import com.android.wm.shell.common.ShellExecutor; import org.junit.Before; import org.junit.Ignore; @@ -36,17 +37,20 @@ import org.mockito.MockitoAnnotations; @SmallTest @RunWith(AndroidTestingRunner.class) -@TestableLooper.RunWithLooper public class OneHandedGestureHandlerTest extends OneHandedTestCase { OneHandedTutorialHandler mTutorialHandler; OneHandedGestureHandler mGestureHandler; @Mock DisplayController mMockDisplayController; + @Mock + ShellExecutor mMockShellMainExecutor; + @Before public void setUp() throws Exception { MockitoAnnotations.initMocks(this); - mTutorialHandler = new OneHandedTutorialHandler(mContext); - mGestureHandler = new OneHandedGestureHandler(mContext, mMockDisplayController); + mTutorialHandler = new OneHandedTutorialHandler(mContext, mMockShellMainExecutor); + mGestureHandler = new OneHandedGestureHandler(mContext, mMockDisplayController, + mMockShellMainExecutor); } @Test diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/onehanded/OneHandedSettingsUtilTest.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/onehanded/OneHandedSettingsUtilTest.java index 7c11138a47aa..f8c9d535ba94 100644 --- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/onehanded/OneHandedSettingsUtilTest.java +++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/onehanded/OneHandedSettingsUtilTest.java @@ -38,7 +38,6 @@ import org.junit.runner.RunWith; @SmallTest @RunWith(AndroidTestingRunner.class) -@TestableLooper.RunWithLooper public class OneHandedSettingsUtilTest extends OneHandedTestCase { ContentResolver mContentResolver; ContentObserver mContentObserver; diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/onehanded/OneHandedTimeoutHandlerTest.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/onehanded/OneHandedTimeoutHandlerTest.java index e2b70c3bcc70..9219f15afc7f 100644 --- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/onehanded/OneHandedTimeoutHandlerTest.java +++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/onehanded/OneHandedTimeoutHandlerTest.java @@ -20,43 +20,46 @@ import static com.android.wm.shell.onehanded.OneHandedSettingsUtil.ONE_HANDED_TI import static com.android.wm.shell.onehanded.OneHandedSettingsUtil.ONE_HANDED_TIMEOUT_MEDIUM_IN_SECONDS; import static com.android.wm.shell.onehanded.OneHandedSettingsUtil.ONE_HANDED_TIMEOUT_NEVER; import static com.android.wm.shell.onehanded.OneHandedSettingsUtil.ONE_HANDED_TIMEOUT_SHORT_IN_SECONDS; -import static com.android.wm.shell.onehanded.OneHandedTimeoutHandler.ONE_HANDED_TIMEOUT_STOP_MSG; import static com.google.common.truth.Truth.assertThat; +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertTrue; import static org.mockito.Mockito.verify; +import android.os.Looper; import android.testing.AndroidTestingRunner; import android.testing.TestableLooper; import androidx.test.filters.SmallTest; +import com.android.wm.shell.common.ShellExecutor; + import org.junit.Before; import org.junit.Test; import org.junit.runner.RunWith; +import org.mockito.Mock; import org.mockito.Mockito; import org.mockito.MockitoAnnotations; +import java.util.ArrayList; + @SmallTest @RunWith(AndroidTestingRunner.class) -@TestableLooper.RunWithLooper public class OneHandedTimeoutHandlerTest extends OneHandedTestCase { - OneHandedTimeoutHandler mTimeoutHandler; + private OneHandedTimeoutHandler mTimeoutHandler; + private ShellExecutor mMainExecutor; @Before public void setUp() throws Exception { MockitoAnnotations.initMocks(this); - mTimeoutHandler = Mockito.spy(OneHandedTimeoutHandler.get()); - } - - @Test - public void testTimeoutHandler_isNotNull() { - assertThat(OneHandedTimeoutHandler.get()).isNotNull(); + mMainExecutor = new TestShellExecutor(); + mTimeoutHandler = Mockito.spy(new OneHandedTimeoutHandler(mMainExecutor)); } @Test public void testTimeoutHandler_getTimeout_defaultMedium() { - assertThat(OneHandedTimeoutHandler.get().getTimeout()).isEqualTo( + assertThat(mTimeoutHandler.getTimeout()).isEqualTo( ONE_HANDED_TIMEOUT_MEDIUM_IN_SECONDS); } @@ -64,28 +67,29 @@ public class OneHandedTimeoutHandlerTest extends OneHandedTestCase { public void testTimeoutHandler_setNewTime_resetTimer() { mTimeoutHandler.setTimeout(ONE_HANDED_TIMEOUT_MEDIUM_IN_SECONDS); verify(mTimeoutHandler).resetTimer(); - assertThat(mTimeoutHandler.sHandler.hasMessages(ONE_HANDED_TIMEOUT_STOP_MSG)).isNotNull(); + assertTrue(mTimeoutHandler.hasScheduledTimeout()); } @Test public void testSetTimeoutNever_neverResetTimer() { mTimeoutHandler.setTimeout(ONE_HANDED_TIMEOUT_NEVER); - assertThat(!mTimeoutHandler.sHandler.hasMessages(ONE_HANDED_TIMEOUT_STOP_MSG)).isNotNull(); + assertFalse(mTimeoutHandler.hasScheduledTimeout()); } @Test public void testSetTimeoutShort() { mTimeoutHandler.setTimeout(ONE_HANDED_TIMEOUT_SHORT_IN_SECONDS); verify(mTimeoutHandler).resetTimer(); - assertThat(mTimeoutHandler.sHandler.hasMessages(ONE_HANDED_TIMEOUT_STOP_MSG)).isNotNull(); + assertThat(mTimeoutHandler.getTimeout()).isEqualTo(ONE_HANDED_TIMEOUT_SHORT_IN_SECONDS); + assertTrue(mTimeoutHandler.hasScheduledTimeout()); } @Test public void testSetTimeoutMedium() { mTimeoutHandler.setTimeout(ONE_HANDED_TIMEOUT_MEDIUM_IN_SECONDS); verify(mTimeoutHandler).resetTimer(); - assertThat(mTimeoutHandler.sHandler.hasMessages( - ONE_HANDED_TIMEOUT_MEDIUM_IN_SECONDS)).isNotNull(); + assertThat(mTimeoutHandler.getTimeout()).isEqualTo(ONE_HANDED_TIMEOUT_MEDIUM_IN_SECONDS); + assertTrue(mTimeoutHandler.hasScheduledTimeout()); } @Test @@ -96,10 +100,38 @@ public class OneHandedTimeoutHandlerTest extends OneHandedTestCase { @Test public void testDragging_shouldRemoveAndSendEmptyMessageDelay() { - final boolean isDragging = true; mTimeoutHandler.setTimeout(ONE_HANDED_TIMEOUT_LONG_IN_SECONDS); mTimeoutHandler.resetTimer(); - TestableLooper.get(this).processAllMessages(); - assertThat(mTimeoutHandler.sHandler.hasMessages(ONE_HANDED_TIMEOUT_STOP_MSG)).isNotNull(); + assertTrue(mTimeoutHandler.hasScheduledTimeout()); + } + + private class TestShellExecutor implements ShellExecutor { + private ArrayList<Runnable> mExecuted = new ArrayList<>(); + private ArrayList<Runnable> mDelayed = new ArrayList<>(); + + @Override + public void execute(Runnable runnable) { + mExecuted.add(runnable); + } + + @Override + public void executeDelayed(Runnable r, long delayMillis) { + mDelayed.add(r); + } + + @Override + public void removeCallbacks(Runnable r) { + mDelayed.remove(r); + } + + @Override + public boolean hasCallback(Runnable r) { + return mDelayed.contains(r); + } + + @Override + public Looper getLooper() { + return Looper.myLooper(); + } } } diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/onehanded/OneHandedTouchHandlerTest.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/onehanded/OneHandedTouchHandlerTest.java index c69e385b2602..d3b02caf8b65 100644 --- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/onehanded/OneHandedTouchHandlerTest.java +++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/onehanded/OneHandedTouchHandlerTest.java @@ -23,22 +23,30 @@ import android.testing.TestableLooper; import androidx.test.filters.SmallTest; +import com.android.wm.shell.common.ShellExecutor; + import org.junit.Before; import org.junit.Ignore; import org.junit.Test; import org.junit.runner.RunWith; +import org.mockito.Mock; import org.mockito.MockitoAnnotations; @SmallTest @RunWith(AndroidTestingRunner.class) -@TestableLooper.RunWithLooper public class OneHandedTouchHandlerTest extends OneHandedTestCase { - OneHandedTouchHandler mTouchHandler; + private OneHandedTouchHandler mTouchHandler; + + @Mock + private OneHandedTimeoutHandler mTimeoutHandler; + + @Mock + private ShellExecutor mMainExecutor; @Before public void setUp() { MockitoAnnotations.initMocks(this); - mTouchHandler = new OneHandedTouchHandler(); + mTouchHandler = new OneHandedTouchHandler(mTimeoutHandler, mMainExecutor); } @Test diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/onehanded/OneHandedTutorialHandlerTest.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/onehanded/OneHandedTutorialHandlerTest.java index b187dc981bf8..c451b8b2d2d7 100644 --- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/onehanded/OneHandedTutorialHandlerTest.java +++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/onehanded/OneHandedTutorialHandlerTest.java @@ -19,12 +19,14 @@ package com.android.wm.shell.onehanded; import static org.mockito.Mockito.verify; import android.content.om.IOverlayManager; +import android.os.Handler; import android.testing.AndroidTestingRunner; import android.testing.TestableLooper; import androidx.test.filters.SmallTest; import com.android.wm.shell.common.DisplayController; +import com.android.wm.shell.common.ShellExecutor; import com.android.wm.shell.common.TaskStackListenerImpl; import org.junit.Before; @@ -35,12 +37,12 @@ import org.mockito.MockitoAnnotations; @SmallTest @RunWith(AndroidTestingRunner.class) -@TestableLooper.RunWithLooper public class OneHandedTutorialHandlerTest extends OneHandedTestCase { @Mock OneHandedTouchHandler mTouchHandler; OneHandedTutorialHandler mTutorialHandler; OneHandedGestureHandler mGestureHandler; + OneHandedTimeoutHandler mTimeoutHandler; OneHandedController mOneHandedController; @Mock DisplayController mMockDisplayController; @@ -52,12 +54,18 @@ public class OneHandedTutorialHandlerTest extends OneHandedTestCase { IOverlayManager mMockOverlayManager; @Mock TaskStackListenerImpl mMockTaskStackListener; + @Mock + ShellExecutor mMockShellMainExecutor; + @Mock + Handler mMockShellMainHandler; @Before public void setUp() { MockitoAnnotations.initMocks(this); - mTutorialHandler = new OneHandedTutorialHandler(mContext); - mGestureHandler = new OneHandedGestureHandler(mContext, mMockDisplayController); + mTutorialHandler = new OneHandedTutorialHandler(mContext, mMockShellMainExecutor); + mTimeoutHandler = new OneHandedTimeoutHandler(mMockShellMainExecutor); + mGestureHandler = new OneHandedGestureHandler(mContext, mMockDisplayController, + mMockShellMainExecutor); mOneHandedController = new OneHandedController( getContext(), mMockDisplayController, @@ -66,8 +74,11 @@ public class OneHandedTutorialHandlerTest extends OneHandedTestCase { mTouchHandler, mTutorialHandler, mGestureHandler, + mTimeoutHandler, mMockOverlayManager, - mMockTaskStackListener); + mMockTaskStackListener, + mMockShellMainExecutor, + mMockShellMainHandler); } @Test diff --git a/libs/services/include/android/os/DropBoxManager.h b/libs/services/include/android/os/DropBoxManager.h index 07472435d8a3..5689286f0b32 100644 --- a/libs/services/include/android/os/DropBoxManager.h +++ b/libs/services/include/android/os/DropBoxManager.h @@ -93,8 +93,6 @@ private: enum { HAS_BYTE_ARRAY = 8 }; - - Status add(const Entry& entry); }; }} // namespace android::os diff --git a/libs/services/src/os/DropBoxManager.cpp b/libs/services/src/os/DropBoxManager.cpp index 429f996bd65e..3716e019f69a 100644 --- a/libs/services/src/os/DropBoxManager.cpp +++ b/libs/services/src/os/DropBoxManager.cpp @@ -18,7 +18,9 @@ #include <android/os/DropBoxManager.h> +#include <android-base/unique_fd.h> #include <binder/IServiceManager.h> +#include <binder/ParcelFileDescriptor.h> #include <com/android/internal/os/IDropBoxManagerService.h> #include <cutils/log.h> @@ -178,18 +180,24 @@ DropBoxManager::~DropBoxManager() Status DropBoxManager::addText(const String16& tag, const string& text) { - Entry entry(tag, IS_TEXT); - entry.mData.assign(text.c_str(), text.c_str() + text.size()); - return add(entry); + return addData(tag, reinterpret_cast<uint8_t const*>(text.c_str()), text.size(), IS_TEXT); } Status DropBoxManager::addData(const String16& tag, uint8_t const* data, size_t size, int flags) { - Entry entry(tag, flags); - entry.mData.assign(data, data+size); - return add(entry); + sp<IDropBoxManagerService> service = interface_cast<IDropBoxManagerService>( + defaultServiceManager()->getService(android::String16("dropbox"))); + if (service == NULL) { + return Status::fromExceptionCode(Status::EX_NULL_POINTER, "can't find dropbox service"); + } + ALOGD("About to call service->add()"); + vector<uint8_t> dataArg; + dataArg.assign(data, data + size); + Status status = service->addData(tag, dataArg, flags); + ALOGD("service->add returned %s", status.toString8().string()); + return status; } Status @@ -213,20 +221,15 @@ DropBoxManager::addFile(const String16& tag, int fd, int flags) ALOGW("DropboxManager: %s", message.c_str()); return Status::fromExceptionCode(Status::EX_ILLEGAL_STATE, message.c_str()); } - Entry entry(tag, flags, fd); - return add(entry); -} - -Status -DropBoxManager::add(const Entry& entry) -{ sp<IDropBoxManagerService> service = interface_cast<IDropBoxManagerService>( defaultServiceManager()->getService(android::String16("dropbox"))); if (service == NULL) { return Status::fromExceptionCode(Status::EX_NULL_POINTER, "can't find dropbox service"); } ALOGD("About to call service->add()"); - Status status = service->add(entry); + android::base::unique_fd uniqueFd(fd); + android::os::ParcelFileDescriptor parcelFd(std::move(uniqueFd)); + Status status = service->addFile(tag, parcelFd, flags); ALOGD("service->add returned %s", status.toString8().string()); return status; } diff --git a/media/java/android/media/MediaCas.java b/media/java/android/media/MediaCas.java index 9957975f1692..582a28ee278e 100644 --- a/media/java/android/media/MediaCas.java +++ b/media/java/android/media/MediaCas.java @@ -716,8 +716,9 @@ public final class MediaCas implements AutoCloseable { context.getSystemService(Context.TV_TUNER_RESOURCE_MGR_SERVICE); if (mTunerResourceManager != null) { int[] clientId = new int[1]; - ResourceClientProfile profile = - new ResourceClientProfile(tvInputServiceSessionId, priorityHint); + ResourceClientProfile profile = new ResourceClientProfile(); + profile.tvInputSessionId = tvInputServiceSessionId; + profile.useCase = priorityHint; mTunerResourceManager.registerClientProfile( profile, context.getMainExecutor(), mResourceListener, clientId); mClientId = clientId[0]; @@ -921,7 +922,9 @@ public final class MediaCas implements AutoCloseable { int[] sessionResourceHandle = new int[1]; sessionResourceHandle[0] = -1; if (mTunerResourceManager != null) { - CasSessionRequest casSessionRequest = new CasSessionRequest(mClientId, mCasSystemId); + CasSessionRequest casSessionRequest = new CasSessionRequest(); + casSessionRequest.clientId = mClientId; + casSessionRequest.casSystemId = mCasSystemId; if (!mTunerResourceManager .requestCasSession(casSessionRequest, sessionResourceHandle)) { throw new MediaCasException.InsufficientResourceException( diff --git a/media/java/android/media/session/MediaSessionManager.java b/media/java/android/media/session/MediaSessionManager.java index 1fd132d00f10..f580ea5d57de 100644 --- a/media/java/android/media/session/MediaSessionManager.java +++ b/media/java/android/media/session/MediaSessionManager.java @@ -140,6 +140,8 @@ public final class MediaSessionManager { @NonNull public ISession createSession(@NonNull MediaSession.CallbackStub cbStub, @NonNull String tag, @Nullable Bundle sessionInfo) { + Objects.requireNonNull(cbStub, "cbStub shouldn't be null"); + Objects.requireNonNull(tag, "tag shouldn't be null"); try { return mService.createSession(mContext.getPackageName(), cbStub, tag, sessionInfo, UserHandle.myUserId()); @@ -163,9 +165,7 @@ public final class MediaSessionManager { * @param token newly created session2 token */ public void notifySession2Created(@NonNull Session2Token token) { - if (token == null) { - throw new IllegalArgumentException("token shouldn't be null"); - } + Objects.requireNonNull(token, "token shouldn't be null"); if (token.getType() != Session2Token.TYPE_SESSION) { throw new IllegalArgumentException("token's type should be TYPE_SESSION"); } @@ -209,16 +209,24 @@ public final class MediaSessionManager { * retrieve sessions for user ids that do not belong to current process. * * @param notificationListener The enabled notification listener component. May be null. - * @param userId The user id to fetch sessions for. + * @param userHandle The user handle to fetch sessions for. * @return A list of controllers for ongoing sessions. * @hide */ @SystemApi(client = SystemApi.Client.MODULE_LIBRARIES) + @SuppressLint("UserHandle") public @NonNull List<MediaController> getActiveSessionsForUser( - @Nullable ComponentName notificationListener, int userId) { + @Nullable ComponentName notificationListener, @NonNull UserHandle userHandle) { + Objects.requireNonNull(userHandle, "userHandle shouldn't be null"); + return getActiveSessionsForUser(notificationListener, userHandle.getIdentifier()); + } + + private List<MediaController> getActiveSessionsForUser(ComponentName notificationListener, + int userId) { ArrayList<MediaController> controllers = new ArrayList<MediaController>(); try { - List<MediaSession.Token> tokens = mService.getSessions(notificationListener, userId); + List<MediaSession.Token> tokens = mService.getSessions(notificationListener, + userId); int size = tokens.size(); for (int i = 0; i < size; i++) { MediaController controller = new MediaController(mContext, tokens.get(i)); @@ -257,12 +265,19 @@ public final class MediaSessionManager { * {@link android.Manifest.permission#INTERACT_ACROSS_USERS_FULL} permission in order to * retrieve session tokens for user ids that do not belong to current process. * - * @param userId The user id to fetch sessions for. + * @param userHandle The user handle to fetch sessions for. * @return A list of {@link Session2Token} * @hide */ @NonNull - public List<Session2Token> getSession2Tokens(int userId) { + @SuppressLint("UserHandle") + public List<Session2Token> getSession2Tokens(@NonNull UserHandle userHandle) { + Objects.requireNonNull(userHandle, "userHandle shouldn't be null"); + return getSession2Tokens(userHandle.getIdentifier()); + + } + + private List<Session2Token> getSession2Tokens(int userId) { try { ParceledListSlice slice = mService.getSession2Tokens(userId); return slice == null ? new ArrayList<>() : slice.getList(); @@ -324,18 +339,26 @@ public final class MediaSessionManager { * * @param sessionListener The listener to add. * @param notificationListener The enabled notification listener component. May be null. - * @param userId The userId to listen for changes on. + * @param userHandle The user handle to listen for changes on. * @param handler The handler to post updates on. * @hide */ - @SuppressLint({"ExecutorRegistration", "SamShouldBeLast"}) + @SuppressLint({"ExecutorRegistration", "SamShouldBeLast", "UserHandle"}) @SystemApi(client = SystemApi.Client.MODULE_LIBRARIES) public void addOnActiveSessionsChangedListener( @NonNull OnActiveSessionsChangedListener sessionListener, - @Nullable ComponentName notificationListener, int userId, @Nullable Handler handler) { - if (sessionListener == null) { - throw new IllegalArgumentException("listener may not be null"); - } + @Nullable ComponentName notificationListener, @NonNull UserHandle userHandle, + @Nullable Handler handler) { + Objects.requireNonNull(userHandle, "userHandle shouldn't be null"); + addOnActiveSessionsChangedListener(sessionListener, notificationListener, + userHandle.getIdentifier(), handler); + } + + private void addOnActiveSessionsChangedListener( + @NonNull OnActiveSessionsChangedListener sessionListener, + @Nullable ComponentName notificationListener, int userId, + @Nullable Handler handler) { + Objects.requireNonNull(sessionListener, "sessionListener shouldn't be null"); if (handler == null) { handler = new Handler(); } @@ -358,15 +381,13 @@ public final class MediaSessionManager { /** * Stop receiving active sessions updates on the specified listener. * - * @param listener The listener to remove. + * @param sessionListener The listener to remove. */ public void removeOnActiveSessionsChangedListener( - @NonNull OnActiveSessionsChangedListener listener) { - if (listener == null) { - throw new IllegalArgumentException("listener may not be null"); - } + @NonNull OnActiveSessionsChangedListener sessionListener) { + Objects.requireNonNull(sessionListener, "sessionListener shouldn't be null"); synchronized (mLock) { - SessionsChangedWrapper wrapper = mListeners.remove(listener); + SessionsChangedWrapper wrapper = mListeners.remove(sessionListener); if (wrapper != null) { try { mService.removeSessionsListener(wrapper.mStub); @@ -422,17 +443,23 @@ public final class MediaSessionManager { * {@link android.Manifest.permission#INTERACT_ACROSS_USERS_FULL} permission in order to * add listeners for user ids that do not belong to current process. * - * @param userId The userId to listen for changes on + * @param userHandle The userHandle to listen for changes on * @param listener The listener to add * @param handler The handler to call listener on. If {@code null}, calling thread's looper will * be used. * @hide */ - public void addOnSession2TokensChangedListener(int userId, - @NonNull OnSession2TokensChangedListener listener, @Nullable Handler handler) { - if (listener == null) { - throw new IllegalArgumentException("listener shouldn't be null"); - } + @SuppressLint("UserHandle") + public void addOnSession2TokensChangedListener(@NonNull UserHandle userHandle, + @NonNull OnSession2TokensChangedListener listener, @NonNull Handler handler) { + Objects.requireNonNull(userHandle, "userHandle shouldn't be null"); + addOnSession2TokensChangedListener(userHandle.getIdentifier(), listener, handler); + } + + private void addOnSession2TokensChangedListener(int userId, + OnSession2TokensChangedListener listener, Handler handler) { + Objects.requireNonNull(handler, "handler shouldn't be null"); + Objects.requireNonNull(listener, "listener shouldn't be null"); synchronized (mLock) { if (mSession2TokensListeners.get(listener) != null) { Log.w(TAG, "Attempted to add session listener twice, ignoring."); @@ -462,9 +489,7 @@ public final class MediaSessionManager { */ public void removeOnSession2TokensChangedListener( @NonNull OnSession2TokensChangedListener listener) { - if (listener == null) { - throw new IllegalArgumentException("listener may not be null"); - } + Objects.requireNonNull(listener, "listener shouldn't be null"); final Session2TokensChangedWrapper wrapper; synchronized (mLock) { wrapper = mSession2TokensListeners.remove(listener); @@ -581,9 +606,7 @@ public final class MediaSessionManager { private void dispatchMediaKeyEventInternal(KeyEvent keyEvent, boolean asSystemService, boolean needWakeLock) { - if (keyEvent == null) { - throw new NullPointerException("keyEvent shouldn't be null"); - } + Objects.requireNonNull(keyEvent, "keyEvent shouldn't be null"); try { mService.dispatchMediaKeyEvent(mContext.getPackageName(), asSystemService, keyEvent, needWakeLock); @@ -606,12 +629,8 @@ public final class MediaSessionManager { @SystemApi(client = SystemApi.Client.MODULE_LIBRARIES) public boolean dispatchMediaKeyEventToSessionAsSystemService(@NonNull KeyEvent keyEvent, @NonNull MediaSession.Token sessionToken) { - if (sessionToken == null) { - throw new NullPointerException("sessionToken shouldn't be null"); - } - if (keyEvent == null) { - throw new NullPointerException("keyEvent shouldn't be null"); - } + Objects.requireNonNull(sessionToken, "sessionToken shouldn't be null"); + Objects.requireNonNull(keyEvent, "keyEvent shouldn't be null"); if (!KeyEvent.isMediaSessionKey(keyEvent.getKeyCode())) { return false; } @@ -662,9 +681,7 @@ public final class MediaSessionManager { private void dispatchVolumeKeyEventInternal(@NonNull KeyEvent keyEvent, int stream, boolean musicOnly, boolean asSystemService) { - if (keyEvent == null) { - throw new NullPointerException("keyEvent shouldn't be null"); - } + Objects.requireNonNull(keyEvent, "keyEvent shouldn't be null"); try { mService.dispatchVolumeKeyEvent(mContext.getPackageName(), mContext.getOpPackageName(), asSystemService, keyEvent, stream, musicOnly); @@ -686,12 +703,8 @@ public final class MediaSessionManager { @SystemApi(client = SystemApi.Client.MODULE_LIBRARIES) public void dispatchVolumeKeyEventToSessionAsSystemService(@NonNull KeyEvent keyEvent, @NonNull MediaSession.Token sessionToken) { - if (sessionToken == null) { - throw new NullPointerException("sessionToken shouldn't be null"); - } - if (keyEvent == null) { - throw new NullPointerException("keyEvent shouldn't be null"); - } + Objects.requireNonNull(sessionToken, "sessionToken shouldn't be null"); + Objects.requireNonNull(keyEvent, "keyEvent shouldn't be null"); try { mService.dispatchVolumeKeyEventToSessionAsSystemService(mContext.getPackageName(), mContext.getOpPackageName(), keyEvent, sessionToken); @@ -735,9 +748,7 @@ public final class MediaSessionManager { * {@code false} otherwise. */ public boolean isTrustedForMediaControl(@NonNull RemoteUserInfo userInfo) { - if (userInfo == null) { - throw new IllegalArgumentException("userInfo may not be null"); - } + Objects.requireNonNull(userInfo, "userInfo shouldn't be null"); if (userInfo.getPackageName() == null) { return false; } @@ -845,12 +856,8 @@ public final class MediaSessionManager { public void addOnMediaKeyEventDispatchedListener( @NonNull @CallbackExecutor Executor executor, @NonNull OnMediaKeyEventDispatchedListener listener) { - if (executor == null) { - throw new NullPointerException("executor shouldn't be null"); - } - if (listener == null) { - throw new NullPointerException("listener shouldn't be null"); - } + Objects.requireNonNull(executor, "executor shouldn't be null"); + Objects.requireNonNull(listener, "listener shouldn't be null"); synchronized (mLock) { try { mOnMediaKeyEventDispatchedListeners.put(listener, executor); @@ -874,9 +881,7 @@ public final class MediaSessionManager { @RequiresPermission(value = android.Manifest.permission.MEDIA_CONTENT_CONTROL) public void removeOnMediaKeyEventDispatchedListener( @NonNull OnMediaKeyEventDispatchedListener listener) { - if (listener == null) { - throw new NullPointerException("listener shouldn't be null"); - } + Objects.requireNonNull(listener, "listener shouldn't be null"); synchronized (mLock) { try { mOnMediaKeyEventDispatchedListeners.remove(listener); @@ -902,12 +907,8 @@ public final class MediaSessionManager { public void addOnMediaKeyEventSessionChangedListener( @NonNull @CallbackExecutor Executor executor, @NonNull OnMediaKeyEventSessionChangedListener listener) { - if (executor == null) { - throw new NullPointerException("executor shouldn't be null"); - } - if (listener == null) { - throw new NullPointerException("listener shouldn't be null"); - } + Objects.requireNonNull(executor, "executor shouldn't be null"); + Objects.requireNonNull(listener, "listener shouldn't be null"); synchronized (mLock) { try { mMediaKeyEventSessionChangedCallbacks.put(listener, executor); @@ -934,9 +935,7 @@ public final class MediaSessionManager { @RequiresPermission(value = android.Manifest.permission.MEDIA_CONTENT_CONTROL) public void removeOnMediaKeyEventSessionChangedListener( @NonNull OnMediaKeyEventSessionChangedListener listener) { - if (listener == null) { - throw new NullPointerException("listener shouldn't be null"); - } + Objects.requireNonNull(listener, "listener shouldn't be null"); synchronized (mLock) { try { mMediaKeyEventSessionChangedCallbacks.remove(listener); diff --git a/media/java/android/media/tv/tuner/Lnb.java b/media/java/android/media/tv/tuner/Lnb.java index 5e9579430e11..59ef4b890320 100644 --- a/media/java/android/media/tv/tuner/Lnb.java +++ b/media/java/android/media/tv/tuner/Lnb.java @@ -145,7 +145,6 @@ public class Lnb implements AutoCloseable { private static final String TAG = "Lnb"; - int mId; LnbCallback mCallback; Executor mExecutor; Tuner mTuner; @@ -162,9 +161,7 @@ public class Lnb implements AutoCloseable { private Boolean mIsClosed = false; private final Object mLock = new Object(); - private Lnb(int id) { - mId = id; - } + private Lnb() {} void setCallback(Executor executor, @Nullable LnbCallback callback, Tuner tuner) { mCallback = callback; diff --git a/media/java/android/media/tv/tuner/Tuner.java b/media/java/android/media/tv/tuner/Tuner.java index 02b6571b0802..46b29f5bc90a 100644 --- a/media/java/android/media/tv/tuner/Tuner.java +++ b/media/java/android/media/tv/tuner/Tuner.java @@ -44,9 +44,9 @@ import android.media.tv.tuner.frontend.FrontendStatus.FrontendStatusType; import android.media.tv.tuner.frontend.OnTuneEventListener; import android.media.tv.tuner.frontend.ScanCallback; import android.media.tv.tunerresourcemanager.ResourceClientProfile; +import android.media.tv.tunerresourcemanager.TunerCiCamRequest; import android.media.tv.tunerresourcemanager.TunerDemuxRequest; import android.media.tv.tunerresourcemanager.TunerDescramblerRequest; -import android.media.tv.tunerresourcemanager.TunerFrontendInfo; import android.media.tv.tunerresourcemanager.TunerFrontendRequest; import android.media.tv.tunerresourcemanager.TunerLnbRequest; import android.media.tv.tunerresourcemanager.TunerResourceManager; @@ -298,6 +298,8 @@ public class Tuner implements AutoCloseable { private Executor mOnResourceLostListenerExecutor; private Integer mDemuxHandle; + private Integer mFrontendCiCamHandle; + private Integer mFrontendCiCamId; private Map<Integer, WeakReference<Descrambler>> mDescramblers = new HashMap<>(); private List<WeakReference<Filter>> mFilters = new ArrayList<WeakReference<Filter>>(); @@ -343,34 +345,14 @@ public class Tuner implements AutoCloseable { mHandler = createEventHandler(); int[] clientId = new int[1]; - ResourceClientProfile profile = new ResourceClientProfile(tvInputSessionId, useCase); + ResourceClientProfile profile = new ResourceClientProfile(); + profile.tvInputSessionId = tvInputSessionId; + profile.useCase = useCase; mTunerResourceManager.registerClientProfile( profile, new HandlerExecutor(mHandler), mResourceListener, clientId); mClientId = clientId[0]; mUserId = ActivityManager.getCurrentUser(); - - setFrontendInfoList(); - setLnbIds(); - } - - private void setFrontendInfoList() { - List<Integer> ids = getFrontendIds(); - if (ids == null) { - return; - } - TunerFrontendInfo[] infos = new TunerFrontendInfo[ids.size()]; - for (int i = 0; i < ids.size(); i++) { - int id = ids.get(i); - FrontendInfo frontendInfo = getFrontendInfoById(id); - if (frontendInfo == null) { - continue; - } - TunerFrontendInfo tunerFrontendInfo = new TunerFrontendInfo( - id, frontendInfo.getType(), frontendInfo.getExclusiveGroupId()); - infos[i] = tunerFrontendInfo; - } - mTunerResourceManager.setFrontendInfoList(infos); } /** @@ -405,14 +387,6 @@ public class Tuner implements AutoCloseable { return nativeGetFrontendIds(); } - private void setLnbIds() { - int[] ids = nativeGetLnbIds(); - if (ids == null) { - return; - } - mTunerResourceManager.setLnbInfoList(ids); - } - /** * Sets the listener for resource lost. * @@ -498,6 +472,14 @@ public class Tuner implements AutoCloseable { if (mLnb != null) { mLnb.close(); } + if (mFrontendCiCamHandle != null) { + int result = nativeUnlinkCiCam(mFrontendCiCamId); + if (result == RESULT_SUCCESS) { + mTunerResourceManager.releaseCiCam(mFrontendCiCamHandle, mClientId); + mFrontendCiCamId = null; + mFrontendCiCamHandle = null; + } + } synchronized (mDescramblers) { if (!mDescramblers.isEmpty()) { for (Map.Entry<Integer, WeakReference<Descrambler>> d : mDescramblers.entrySet()) { @@ -561,7 +543,7 @@ public class Tuner implements AutoCloseable { private native int nativeStopTune(); private native int nativeScan(int settingsType, FrontendSettings settings, int scanType); private native int nativeStopScan(); - private native int nativeSetLnb(int lnbId); + private native int nativeSetLnb(Lnb lnb); private native int nativeSetLna(boolean enable); private native FrontendStatus nativeGetFrontendStatus(int[] statusTypes); private native Integer nativeGetAvSyncHwId(Filter filter); @@ -574,7 +556,6 @@ public class Tuner implements AutoCloseable { private native Filter nativeOpenFilter(int type, int subType, long bufferSize); private native TimeFilter nativeOpenTimeFilter(); - private native int[] nativeGetLnbIds(); private native Lnb nativeOpenLnbByHandle(int handle); private native Lnb nativeOpenLnbByName(String name); @@ -814,7 +795,9 @@ public class Tuner implements AutoCloseable { private boolean requestFrontend() { int[] feHandle = new int[1]; - TunerFrontendRequest request = new TunerFrontendRequest(mClientId, mFrontendType); + TunerFrontendRequest request = new TunerFrontendRequest(); + request.clientId = mClientId; + request.frontendType = mFrontendType; boolean granted = mTunerResourceManager.requestFrontend(request, feHandle); if (granted) { mFrontendHandle = feHandle[0]; @@ -835,7 +818,7 @@ public class Tuner implements AutoCloseable { */ @Result private int setLnb(@NonNull Lnb lnb) { - return nativeSetLnb(lnb.mId); + return nativeSetLnb(lnb); } /** @@ -945,7 +928,8 @@ public class Tuner implements AutoCloseable { public int connectFrontendToCiCam(int ciCamId) { if (TunerVersionChecker.checkHigherOrEqualVersionTo(TunerVersionChecker.TUNER_VERSION_1_1, "linkFrontendToCiCam")) { - if (checkResource(TunerResourceManager.TUNER_RESOURCE_TYPE_FRONTEND)) { + if (checkResource(TunerResourceManager.TUNER_RESOURCE_TYPE_FRONTEND) + && checkCiCamResource(ciCamId)) { return nativeLinkCiCam(ciCamId); } } @@ -964,7 +948,7 @@ public class Tuner implements AutoCloseable { */ @Result public int disconnectCiCam() { - if (checkResource(TunerResourceManager.TUNER_RESOURCE_TYPE_DEMUX)) { + if (mDemuxHandle != null) { return nativeDisconnectCiCam(); } return RESULT_UNAVAILABLE; @@ -990,8 +974,14 @@ public class Tuner implements AutoCloseable { public int disconnectFrontendToCiCam(int ciCamId) { if (TunerVersionChecker.checkHigherOrEqualVersionTo(TunerVersionChecker.TUNER_VERSION_1_1, "unlinkFrontendToCiCam")) { - if (checkResource(TunerResourceManager.TUNER_RESOURCE_TYPE_FRONTEND)) { - return nativeUnlinkCiCam(ciCamId); + if (mFrontendCiCamHandle != null && mFrontendCiCamId == ciCamId) { + int result = nativeUnlinkCiCam(ciCamId); + if (result == RESULT_SUCCESS) { + mTunerResourceManager.releaseCiCam(mFrontendCiCamHandle, mClientId); + mFrontendCiCamId = null; + mFrontendCiCamHandle = null; + } + return result; } } return RESULT_UNAVAILABLE; @@ -1268,7 +1258,8 @@ public class Tuner implements AutoCloseable { private boolean requestLnb() { int[] lnbHandle = new int[1]; - TunerLnbRequest request = new TunerLnbRequest(mClientId); + TunerLnbRequest request = new TunerLnbRequest(); + request.clientId = mClientId; boolean granted = mTunerResourceManager.requestLnb(request, lnbHandle); if (granted) { mLnbHandle = lnbHandle[0]; @@ -1356,7 +1347,8 @@ public class Tuner implements AutoCloseable { private boolean requestDemux() { int[] demuxHandle = new int[1]; - TunerDemuxRequest request = new TunerDemuxRequest(mClientId); + TunerDemuxRequest request = new TunerDemuxRequest(); + request.clientId = mClientId; boolean granted = mTunerResourceManager.requestDemux(request, demuxHandle); if (granted) { mDemuxHandle = demuxHandle[0]; @@ -1367,7 +1359,8 @@ public class Tuner implements AutoCloseable { private Descrambler requestDescrambler() { int[] descramblerHandle = new int[1]; - TunerDescramblerRequest request = new TunerDescramblerRequest(mClientId); + TunerDescramblerRequest request = new TunerDescramblerRequest(); + request.clientId = mClientId; boolean granted = mTunerResourceManager.requestDescrambler(request, descramblerHandle); if (!granted) { return null; @@ -1385,6 +1378,18 @@ public class Tuner implements AutoCloseable { return descrambler; } + private boolean requestFrontendCiCam(int ciCamId) { + int[] ciCamHandle = new int[1]; + TunerCiCamRequest request = new TunerCiCamRequest(); + request.clientId = mClientId; + request.ciCamId = ciCamId; + boolean granted = mTunerResourceManager.requestCiCam(request, ciCamHandle); + if (granted) { + mFrontendCiCamHandle = ciCamHandle[0]; + } + return granted; + } + private boolean checkResource(int resourceType) { switch (resourceType) { case TunerResourceManager.TUNER_RESOURCE_TYPE_FRONTEND: { @@ -1411,6 +1416,13 @@ public class Tuner implements AutoCloseable { return true; } + private boolean checkCiCamResource(int ciCamId) { + if (mFrontendCiCamHandle == null && !requestFrontendCiCam(ciCamId)) { + return false; + } + return true; + } + /* package */ void releaseLnb() { if (mLnbHandle != null) { // LNB handle can be null if it's opened by name. diff --git a/media/java/android/media/tv/tuner/dvr/DvrRecorder.java b/media/java/android/media/tv/tuner/dvr/DvrRecorder.java index c4b622d0fba9..2f2cd964d6f3 100644 --- a/media/java/android/media/tv/tuner/dvr/DvrRecorder.java +++ b/media/java/android/media/tv/tuner/dvr/DvrRecorder.java @@ -47,7 +47,7 @@ public class DvrRecorder implements AutoCloseable { private static int sInstantId = 0; private int mSegmentId = 0; private int mOverflow; - private Boolean mIsStopped = null; + private Boolean mIsStopped = true; private native int nativeAttachFilter(Filter filter); private native int nativeDetachFilter(Filter filter); diff --git a/media/java/android/media/tv/tunerresourcemanager/Android.bp b/media/java/android/media/tv/tunerresourcemanager/Android.bp index c65d25a03813..02390bb7c31b 100644 --- a/media/java/android/media/tv/tunerresourcemanager/Android.bp +++ b/media/java/android/media/tv/tunerresourcemanager/Android.bp @@ -1,17 +1,36 @@ filegroup { - name: "framework-media-tv-tunerresourcemanager-sources", + name: "framework-media-tv-tunerresourcemanager-sources-aidl", srcs: [ - "*.java", - "*.aidl", + "aidl/android/media/tv/tunerresourcemanager/CasSessionRequest.aidl", + "aidl/android/media/tv/tunerresourcemanager/IResourcesReclaimListener.aidl", + "aidl/android/media/tv/tunerresourcemanager/ResourceClientProfile.aidl", + "aidl/android/media/tv/tunerresourcemanager/TunerCiCamRequest.aidl", + "aidl/android/media/tv/tunerresourcemanager/TunerDemuxRequest.aidl", + "aidl/android/media/tv/tunerresourcemanager/TunerDescramblerRequest.aidl", + "aidl/android/media/tv/tunerresourcemanager/TunerFrontendInfo.aidl", + "aidl/android/media/tv/tunerresourcemanager/TunerFrontendRequest.aidl", + "aidl/android/media/tv/tunerresourcemanager/TunerLnbRequest.aidl", + "aidl/android/media/tv/tunerresourcemanager/ITunerResourceManager.aidl", ], - path: ".", + path: "aidl", } -java_library { - name: "framework-media-tv-trm-sources", - srcs: [":framework-media-tv-tunerresourcemanager-sources"], - installable: true, - visibility: [ - "//frameworks/base", +aidl_interface { + name: "tv_tuner_resource_manager_aidl_interface", + unstable: true, + local_include_dir: "aidl", + backend: { + java: { + sdk_version: "current", + }, + cpp: { + enabled: true, + }, + ndk: { + enabled: true, + }, + }, + srcs: [ + ":framework-media-tv-tunerresourcemanager-sources-aidl", ], -}
\ No newline at end of file +} diff --git a/media/java/android/media/tv/tunerresourcemanager/CasSessionRequest.java b/media/java/android/media/tv/tunerresourcemanager/CasSessionRequest.java deleted file mode 100644 index 59802ff8c3f8..000000000000 --- a/media/java/android/media/tv/tunerresourcemanager/CasSessionRequest.java +++ /dev/null @@ -1,114 +0,0 @@ -/* - * Copyright 2020 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.tunerresourcemanager; - -import android.annotation.NonNull; -import android.os.Parcel; -import android.os.Parcelable; -import android.util.Log; - -/** - * Information required to request a Cas Session. - * - * @hide - */ -public final class CasSessionRequest implements Parcelable { - static final String TAG = "CasSessionRequest"; - - public static final - @NonNull - Parcelable.Creator<CasSessionRequest> CREATOR = - new Parcelable.Creator<CasSessionRequest>() { - @Override - public CasSessionRequest createFromParcel(Parcel source) { - try { - return new CasSessionRequest(source); - } catch (Exception e) { - Log.e(TAG, "Exception creating CasSessionRequest from parcel", e); - return null; - } - } - - @Override - public CasSessionRequest[] newArray(int size) { - return new CasSessionRequest[size]; - } - }; - - /** - * Client id of the client that sends the request. - */ - private final int mClientId; - - /** - * System id of the requested cas. - */ - private final int mCasSystemId; - - private CasSessionRequest(@NonNull Parcel source) { - mClientId = source.readInt(); - mCasSystemId = source.readInt(); - } - - /** - * Constructs a new {@link CasSessionRequest} with the given parameters. - * - * @param clientId id of the client. - * @param casSystemId the cas system id that the client is requesting. - */ - public CasSessionRequest(int clientId, - int casSystemId) { - mClientId = clientId; - mCasSystemId = casSystemId; - } - - /** - * Returns the id of the client. - */ - public int getClientId() { - return mClientId; - } - - /** - * Returns the cas system id requested. - */ - public int getCasSystemId() { - return mCasSystemId; - } - - // Parcelable - @Override - public int describeContents() { - return 0; - } - - @NonNull - @Override - public String toString() { - StringBuilder b = new StringBuilder(128); - b.append("CasSessionRequest {clientId=").append(mClientId); - b.append(", casSystemId=").append(mCasSystemId); - b.append("}"); - return b.toString(); - } - - @Override - public void writeToParcel(@NonNull Parcel dest, int flags) { - dest.writeInt(mClientId); - dest.writeInt(mCasSystemId); - } -} diff --git a/media/java/android/media/tv/tunerresourcemanager/ResourceClientProfile.java b/media/java/android/media/tv/tunerresourcemanager/ResourceClientProfile.java deleted file mode 100644 index 28f1ac916690..000000000000 --- a/media/java/android/media/tv/tunerresourcemanager/ResourceClientProfile.java +++ /dev/null @@ -1,131 +0,0 @@ -/* - * Copyright 2020 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.tunerresourcemanager; - -import android.annotation.NonNull; -import android.annotation.Nullable; -import android.os.Parcel; -import android.os.Parcelable; -import android.util.Log; - -/** - * A profile of a resource client. This profile is used to register the client info - * with the Tuner Resource Manager(TRM). - * - * @hide - */ -public final class ResourceClientProfile implements Parcelable { - static final String TAG = "ResourceClientProfile"; - - public static final - @NonNull - Parcelable.Creator<ResourceClientProfile> CREATOR = - new Parcelable.Creator<ResourceClientProfile>() { - @Override - public ResourceClientProfile createFromParcel(Parcel source) { - try { - return new ResourceClientProfile(source); - } catch (Exception e) { - Log.e(TAG, "Exception creating ResourceClientProfile from parcel", e); - return null; - } - } - - @Override - public ResourceClientProfile[] newArray(int size) { - return new ResourceClientProfile[size]; - } - }; - - /** - * This is used by TRM to get TV App’s processId from TIF. - * The processId will be used to identify foreground applications. - * - * <p>MediaCas, Tuner and TvInputHardwareManager get tvInputSessionId from TIS. - * If mTvInputSessionId is UNKNOWN, the client is always background. - */ - private final String mTvInputSessionId; - - /** - * Usage of the client. - */ - private final int mUseCase; - - private ResourceClientProfile(@NonNull Parcel source) { - mTvInputSessionId = source.readString(); - mUseCase = source.readInt(); - } - - /** - * Constructs a new {@link ResourceClientProfile} with the given parameters. - * - * @param tvInputSessionId the unique id of the session owned by the client. - * @param useCase the usage of the client. Suggested priority hints are - * {@link android.media.tv.TvInputService.PRIORITY_HINT_USE_CASE_TYPE_PLAYBACK} - * {@link android.media.tv.TvInputService.PRIORITY_HINT_USE_CASE_TYPE_LIVE} - * {@link android.media.tv.TvInputService.PRIORITY_HINT_USE_CASE_TYPE_RECORD}. - * New [use case : priority value] pair can be defined in the manifest by the - * OEM. The id of the useCaseVendor should be passed through this parameter. Any - * undefined use case would cause IllegalArgumentException. - */ - public ResourceClientProfile(@Nullable String tvInputSessionId, - int useCase) { - mTvInputSessionId = tvInputSessionId; - mUseCase = useCase; - } - - /** - * Returns the tv input session id of the client. - * - * @return the value of the tv input session id. - */ - @Nullable - public String getTvInputSessionId() { - return mTvInputSessionId; - } - - /** - * Returns the user usage of the client. - * - * @return the value of use case. - */ - public int getUseCase() { - return mUseCase; - } - - // Parcelable - @Override - public int describeContents() { - return 0; - } - - @NonNull - @Override - public String toString() { - StringBuilder b = new StringBuilder(128); - b.append("ResourceClientProfile {tvInputSessionId=").append(mTvInputSessionId); - b.append(", useCase=").append(mUseCase); - b.append("}"); - return b.toString(); - } - - @Override - public void writeToParcel(@NonNull Parcel dest, int flags) { - dest.writeString(mTvInputSessionId); - dest.writeInt(mUseCase); - } -} diff --git a/media/java/android/media/tv/tunerresourcemanager/TunerDemuxRequest.java b/media/java/android/media/tv/tunerresourcemanager/TunerDemuxRequest.java deleted file mode 100644 index 34a77616f62e..000000000000 --- a/media/java/android/media/tv/tunerresourcemanager/TunerDemuxRequest.java +++ /dev/null @@ -1,96 +0,0 @@ -/* - * Copyright 2020 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.tunerresourcemanager; - -import android.annotation.NonNull; -import android.os.Parcel; -import android.os.Parcelable; -import android.util.Log; - -/** - * Information required to request a Tuner Demux. - * - * @hide - */ -public final class TunerDemuxRequest implements Parcelable { - static final String TAG = "TunerDemuxRequest"; - - public static final - @NonNull - Parcelable.Creator<TunerDemuxRequest> CREATOR = - new Parcelable.Creator<TunerDemuxRequest>() { - @Override - public TunerDemuxRequest createFromParcel(Parcel source) { - try { - return new TunerDemuxRequest(source); - } catch (Exception e) { - Log.e(TAG, "Exception creating TunerDemuxRequest from parcel", e); - return null; - } - } - - @Override - public TunerDemuxRequest[] newArray(int size) { - return new TunerDemuxRequest[size]; - } - }; - - /** - * Client id of the client that sends the request. - */ - private final int mClientId; - - private TunerDemuxRequest(@NonNull Parcel source) { - mClientId = source.readInt(); - } - - /** - * Constructs a new {@link TunerDemuxRequest} with the given parameters. - * - * @param clientId id of the client. - */ - public TunerDemuxRequest(int clientId) { - mClientId = clientId; - } - - /** - * Returns the id of the client. - */ - public int getClientId() { - return mClientId; - } - - // Parcelable - @Override - public int describeContents() { - return 0; - } - - @NonNull - @Override - public String toString() { - StringBuilder b = new StringBuilder(128); - b.append("TunerDemuxRequest {clientId=").append(mClientId); - b.append("}"); - return b.toString(); - } - - @Override - public void writeToParcel(@NonNull Parcel dest, int flags) { - dest.writeInt(mClientId); - } -} diff --git a/media/java/android/media/tv/tunerresourcemanager/TunerDescramblerRequest.java b/media/java/android/media/tv/tunerresourcemanager/TunerDescramblerRequest.java deleted file mode 100644 index 58162879f8cf..000000000000 --- a/media/java/android/media/tv/tunerresourcemanager/TunerDescramblerRequest.java +++ /dev/null @@ -1,96 +0,0 @@ -/* - * Copyright 2020 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.tunerresourcemanager; - -import android.annotation.NonNull; -import android.os.Parcel; -import android.os.Parcelable; -import android.util.Log; - -/** - * Information required to request a Tuner Descrambler. - * - * @hide - */ -public final class TunerDescramblerRequest implements Parcelable { - static final String TAG = "TunerDescramblerRequest"; - - public static final - @NonNull - Parcelable.Creator<TunerDescramblerRequest> CREATOR = - new Parcelable.Creator<TunerDescramblerRequest>() { - @Override - public TunerDescramblerRequest createFromParcel(Parcel source) { - try { - return new TunerDescramblerRequest(source); - } catch (Exception e) { - Log.e(TAG, "Exception creating TunerDescramblerRequest from parcel", e); - return null; - } - } - - @Override - public TunerDescramblerRequest[] newArray(int size) { - return new TunerDescramblerRequest[size]; - } - }; - - /** - * Client id of the client that sends the request. - */ - private final int mClientId; - - private TunerDescramblerRequest(@NonNull Parcel source) { - mClientId = source.readInt(); - } - - /** - * Constructs a new {@link TunerDescramblerRequest} with the given parameters. - * - * @param clientId id of the client. - */ - public TunerDescramblerRequest(int clientId) { - mClientId = clientId; - } - - /** - * Returns the id of the client. - */ - public int getClientId() { - return mClientId; - } - - // Parcelable - @Override - public int describeContents() { - return 0; - } - - @NonNull - @Override - public String toString() { - StringBuilder b = new StringBuilder(128); - b.append("TunerDescramblerRequest {clientId=").append(mClientId); - b.append("}"); - return b.toString(); - } - - @Override - public void writeToParcel(@NonNull Parcel dest, int flags) { - dest.writeInt(mClientId); - } -} diff --git a/media/java/android/media/tv/tunerresourcemanager/TunerFrontendInfo.java b/media/java/android/media/tv/tunerresourcemanager/TunerFrontendInfo.java deleted file mode 100644 index ef50aacf43b5..000000000000 --- a/media/java/android/media/tv/tunerresourcemanager/TunerFrontendInfo.java +++ /dev/null @@ -1,142 +0,0 @@ -/* - * Copyright 2020 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.tunerresourcemanager; - -import android.annotation.NonNull; -import android.media.tv.tuner.frontend.FrontendSettings.Type; -import android.os.Parcel; -import android.os.Parcelable; -import android.util.Log; - -/** - * Simple container of the FrontendInfo struct defined in the TunerHAL 1.0 interface. - * - * <p>Note that this object is defined to pass necessary frontend info between the - * Tuner Resource Manager and the client. It includes partial information in - * {@link FrontendInfo}. - * - * @hide - */ -public final class TunerFrontendInfo implements Parcelable { - static final String TAG = "TunerFrontendInfo"; - - public static final - @NonNull - Parcelable.Creator<TunerFrontendInfo> CREATOR = - new Parcelable.Creator<TunerFrontendInfo>() { - @Override - public TunerFrontendInfo createFromParcel(Parcel source) { - try { - return new TunerFrontendInfo(source); - } catch (Exception e) { - Log.e(TAG, "Exception creating TunerFrontendInfo from parcel", e); - return null; - } - } - - @Override - public TunerFrontendInfo[] newArray(int size) { - return new TunerFrontendInfo[size]; - } - }; - - private final int mHandle; - - @Type - private final int mFrontendType; - - /** - * Frontends are assigned with the same exclusiveGroupId if they can't - * function at same time. For instance, they share same hardware module. - */ - private final int mExclusiveGroupId; - - private TunerFrontendInfo(@NonNull Parcel source) { - mHandle = source.readInt(); - mFrontendType = source.readInt(); - mExclusiveGroupId = source.readInt(); - } - - /** - * Constructs a new {@link TunerFrontendInfo} with the given parameters. - * - * @param handle frontend handle - * @param frontendType the type of the frontend. - * @param exclusiveGroupId the group id of the frontend. FE with the same - group id can't function at the same time. - */ - public TunerFrontendInfo(int handle, - @Type int frontendType, - int exclusiveGroupId) { - mHandle = handle; - mFrontendType = frontendType; - mExclusiveGroupId = exclusiveGroupId; - } - - /** - * Returns the frontend handle. - * - * @return the value of the frontend handle. - */ - public int getHandle() { - return mHandle; - } - - /** - * Returns the application id that requests the tuner frontend resource. - * - * @return the value of the frontend type. - */ - @Type - public int getFrontendType() { - return mFrontendType; - } - - /** - * Returns the exclusiveGroupId. Frontends with the same exclusiveGroupId - * can't function at same time. - * - * @return the value of the exclusive group id. - */ - public int getExclusiveGroupId() { - return mExclusiveGroupId; - } - - // Parcelable - @Override - public int describeContents() { - return 0; - } - - @NonNull - @Override - public String toString() { - StringBuilder b = new StringBuilder(128); - b.append("TunerFrontendInfo {handle=").append(mHandle); - b.append(", frontendType=").append(mFrontendType); - b.append(", exclusiveGroupId=").append(mExclusiveGroupId); - b.append("}"); - return b.toString(); - } - - @Override - public void writeToParcel(@NonNull Parcel dest, int flags) { - dest.writeInt(mHandle); - dest.writeInt(mFrontendType); - dest.writeInt(mExclusiveGroupId); - } -} diff --git a/media/java/android/media/tv/tunerresourcemanager/TunerFrontendRequest.java b/media/java/android/media/tv/tunerresourcemanager/TunerFrontendRequest.java deleted file mode 100644 index 12f8032ab99b..000000000000 --- a/media/java/android/media/tv/tunerresourcemanager/TunerFrontendRequest.java +++ /dev/null @@ -1,114 +0,0 @@ -/* - * Copyright 2020 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.tunerresourcemanager; - -import android.annotation.NonNull; -import android.media.tv.tuner.frontend.FrontendSettings.Type; -import android.os.Parcel; -import android.os.Parcelable; -import android.util.Log; - -/** - * Information required to request a Tuner Frontend. - * - * @hide - */ -public final class TunerFrontendRequest implements Parcelable { - static final String TAG = "TunerFrontendRequest"; - - public static final - @NonNull - Parcelable.Creator<TunerFrontendRequest> CREATOR = - new Parcelable.Creator<TunerFrontendRequest>() { - @Override - public TunerFrontendRequest createFromParcel(Parcel source) { - try { - return new TunerFrontendRequest(source); - } catch (Exception e) { - Log.e(TAG, "Exception creating TunerFrontendRequest from parcel", e); - return null; - } - } - - @Override - public TunerFrontendRequest[] newArray(int size) { - return new TunerFrontendRequest[size]; - } - }; - - private final int mClientId; - @Type - private final int mFrontendType; - - private TunerFrontendRequest(@NonNull Parcel source) { - mClientId = source.readInt(); - mFrontendType = source.readInt(); - } - - /** - * Constructs a new {@link TunerFrontendRequest} with the given parameters. - * - * @param clientId the unique id of the client returned when registering profile. - * @param frontendType the type of the requested frontend. - */ - public TunerFrontendRequest(int clientId, - @Type int frontendType) { - mClientId = clientId; - mFrontendType = frontendType; - } - - /** - * Returns the client id that requests the tuner frontend resource. - * - * @return the value of the client id. - */ - public int getClientId() { - return mClientId; - } - - /** - * Returns the frontend type that the client requests for. - * - * @return the value of the requested frontend type. - */ - @Type - public int getFrontendType() { - return mFrontendType; - } - - // Parcelable - @Override - public int describeContents() { - return 0; - } - - @NonNull - @Override - public String toString() { - StringBuilder b = new StringBuilder(128); - b.append("TunerFrontendRequest {clientId=").append(mClientId); - b.append(", frontendType=").append(mFrontendType); - b.append("}"); - return b.toString(); - } - - @Override - public void writeToParcel(@NonNull Parcel dest, int flags) { - dest.writeInt(mClientId); - dest.writeInt(mFrontendType); - } -} diff --git a/media/java/android/media/tv/tunerresourcemanager/TunerLnbRequest.java b/media/java/android/media/tv/tunerresourcemanager/TunerLnbRequest.java deleted file mode 100644 index 5ed7f3f546f4..000000000000 --- a/media/java/android/media/tv/tunerresourcemanager/TunerLnbRequest.java +++ /dev/null @@ -1,96 +0,0 @@ -/* - * Copyright 2020 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.tunerresourcemanager; - -import android.annotation.NonNull; -import android.os.Parcel; -import android.os.Parcelable; -import android.util.Log; - -/** - * Information required to request a Tuner Lnb. - * - * @hide - */ -public final class TunerLnbRequest implements Parcelable { - static final String TAG = "TunerLnbRequest"; - - public static final - @NonNull - Parcelable.Creator<TunerLnbRequest> CREATOR = - new Parcelable.Creator<TunerLnbRequest>() { - @Override - public TunerLnbRequest createFromParcel(Parcel source) { - try { - return new TunerLnbRequest(source); - } catch (Exception e) { - Log.e(TAG, "Exception creating TunerLnbRequest from parcel", e); - return null; - } - } - - @Override - public TunerLnbRequest[] newArray(int size) { - return new TunerLnbRequest[size]; - } - }; - - /** - * Client id of the client that sends the request. - */ - private final int mClientId; - - private TunerLnbRequest(@NonNull Parcel source) { - mClientId = source.readInt(); - } - - /** - * Constructs a new {@link TunerLnbRequest} with the given parameters. - * - * @param clientId the id of the client. - */ - public TunerLnbRequest(int clientId) { - mClientId = clientId; - } - - /** - * Returns the id of the client - */ - public int getClientId() { - return mClientId; - } - - // Parcelable - @Override - public int describeContents() { - return 0; - } - - @NonNull - @Override - public String toString() { - StringBuilder b = new StringBuilder(128); - b.append("TunerLnbRequest {clientId=").append(mClientId); - b.append("}"); - return b.toString(); - } - - @Override - public void writeToParcel(@NonNull Parcel dest, int flags) { - dest.writeInt(mClientId); - } -} diff --git a/media/java/android/media/tv/tunerresourcemanager/TunerResourceManager.java b/media/java/android/media/tv/tunerresourcemanager/TunerResourceManager.java index be102d8acc10..6f7adbc65318 100644 --- a/media/java/android/media/tv/tunerresourcemanager/TunerResourceManager.java +++ b/media/java/android/media/tv/tunerresourcemanager/TunerResourceManager.java @@ -74,6 +74,7 @@ public class TunerResourceManager { TUNER_RESOURCE_TYPE_DESCRAMBLER, TUNER_RESOURCE_TYPE_LNB, TUNER_RESOURCE_TYPE_CAS_SESSION, + TUNER_RESOURCE_TYPE_FRONTEND_CICAM, TUNER_RESOURCE_TYPE_MAX, }) @Retention(RetentionPolicy.SOURCE) @@ -84,7 +85,8 @@ public class TunerResourceManager { public static final int TUNER_RESOURCE_TYPE_DESCRAMBLER = 2; public static final int TUNER_RESOURCE_TYPE_LNB = 3; public static final int TUNER_RESOURCE_TYPE_CAS_SESSION = 4; - public static final int TUNER_RESOURCE_TYPE_MAX = 5; + public static final int TUNER_RESOURCE_TYPE_FRONTEND_CICAM = 5; + public static final int TUNER_RESOURCE_TYPE_MAX = 6; private final ITunerResourceManager mService; private final int mUserId; @@ -379,6 +381,38 @@ public class TunerResourceManager { } /** + * Requests a CiCam resource. + * + * <p>There are three possible scenarios: + * <ul> + * <li>If there is CiCam available, the API would send the id back. + * + * <li>If no CiCam is available but the current request info can show higher priority than + * other uses of the CiCam, the API will send + * {@link IResourcesReclaimListener#onReclaimResources()} to the {@link Tuner}. Tuner would + * handle the resource reclaim on the holder of lower priority and notify the holder of its + * resource loss. + * + * <p><strong>Note:</strong> {@link #updateCasInfo(int, int)} must be called before this + * request. + * + * @param request {@link TunerCiCamRequest} information of the current request. + * @param ciCamHandle a one-element array to return the granted ciCam handle. + * If no ciCam granted, this will return {@link #INVALID_RESOURCE_HANDLE}. + * + * @return true if there is ciCam granted. + */ + public boolean requestCiCam(TunerCiCamRequest request, int[] ciCamHandle) { + boolean result = false; + try { + result = mService.requestCiCam(request, ciCamHandle); + } catch (RemoteException e) { + throw e.rethrowFromSystemServer(); + } + return result; + } + + /** * Requests a Tuner Lnb resource. * * <p>There are three possible scenarios: @@ -482,6 +516,25 @@ public class TunerResourceManager { } /** + * Notifies the TRM that the given CiCam has been released. + * + * <p>Client must call this whenever it releases a CiCam. + * + * <p><strong>Note:</strong> {@link #updateCasInfo(int, int)} must be called before this + * release. + * + * @param ciCamHandle the handle of the releasing CiCam. + * @param clientId the id of the client that is releasing the CiCam. + */ + public void releaseCiCam(int ciCamHandle, int clientId) { + try { + mService.releaseCiCam(ciCamHandle, clientId); + } catch (RemoteException e) { + throw e.rethrowFromSystemServer(); + } + } + + /** * Notifies the TRM that the Lnb with the given id has been released. * * <p>Client must call this whenever it releases an Lnb. diff --git a/media/java/android/media/tv/tunerresourcemanager/CasSessionRequest.aidl b/media/java/android/media/tv/tunerresourcemanager/aidl/android/media/tv/tunerresourcemanager/CasSessionRequest.aidl index c918d88b479a..88f591551e8a 100644 --- a/media/java/android/media/tv/tunerresourcemanager/CasSessionRequest.aidl +++ b/media/java/android/media/tv/tunerresourcemanager/aidl/android/media/tv/tunerresourcemanager/CasSessionRequest.aidl @@ -21,4 +21,8 @@ package android.media.tv.tunerresourcemanager; * * @hide */ -parcelable CasSessionRequest;
\ No newline at end of file +parcelable CasSessionRequest { + int clientId; + + int casSystemId; +}
\ No newline at end of file diff --git a/media/java/android/media/tv/tunerresourcemanager/IResourcesReclaimListener.aidl b/media/java/android/media/tv/tunerresourcemanager/aidl/android/media/tv/tunerresourcemanager/IResourcesReclaimListener.aidl index 1a4eb2946b8c..1a4eb2946b8c 100644 --- a/media/java/android/media/tv/tunerresourcemanager/IResourcesReclaimListener.aidl +++ b/media/java/android/media/tv/tunerresourcemanager/aidl/android/media/tv/tunerresourcemanager/IResourcesReclaimListener.aidl diff --git a/media/java/android/media/tv/tunerresourcemanager/ITunerResourceManager.aidl b/media/java/android/media/tv/tunerresourcemanager/aidl/android/media/tv/tunerresourcemanager/ITunerResourceManager.aidl index 487b444eb627..a1f6687a1b81 100644 --- a/media/java/android/media/tv/tunerresourcemanager/ITunerResourceManager.aidl +++ b/media/java/android/media/tv/tunerresourcemanager/aidl/android/media/tv/tunerresourcemanager/ITunerResourceManager.aidl @@ -19,6 +19,7 @@ package android.media.tv.tunerresourcemanager; import android.media.tv.tunerresourcemanager.CasSessionRequest; import android.media.tv.tunerresourcemanager.IResourcesReclaimListener; import android.media.tv.tunerresourcemanager.ResourceClientProfile; +import android.media.tv.tunerresourcemanager.TunerCiCamRequest; import android.media.tv.tunerresourcemanager.TunerDemuxRequest; import android.media.tv.tunerresourcemanager.TunerDescramblerRequest; import android.media.tv.tunerresourcemanager.TunerFrontendInfo; @@ -225,6 +226,31 @@ interface ITunerResourceManager { boolean requestCasSession(in CasSessionRequest request, out int[] casSessionHandle); /* + * This API is used by the Tuner framework to request an available CuCam. + * + * <p>There are three possible scenarios: + * <ul> + * <li>If there is CiCam available, the API would send the handle back. + * + * <li>If no CiCma is available but the current request info can show higher priority than + * other uses of the ciCam, the API will send + * {@link ITunerResourceManagerCallback#onReclaimResources()} to the {@link Tuner}. Tuner would + * handle the resource reclaim on the holder of lower priority and notify the holder of its + * resource loss. + * + * <li>If no CiCam can be granted, the API would return false. + * <ul> + * + * <p><strong>Note:</strong> {@link #updateCasInfo(int, int)} must be called before this request. + * + * @param request {@link TunerCiCamRequest} information of the current request. + * @param ciCamHandle a one-element array to return the granted ciCam handle. + * + * @return true if there is CiCam granted. + */ + boolean requestCiCam(in TunerCiCamRequest request, out int[] ciCamHandle); + + /* * This API is used by the Tuner framework to request an available Lnb from the TunerHAL. * * <p>There are three possible scenarios: @@ -293,6 +319,19 @@ interface ITunerResourceManager { */ void releaseCasSession(in int casSessionHandle, int clientId); + /** + * Notifies the TRM that the given CiCam has been released. + * + * <p>Client must call this whenever it releases a CiCam. + * + * <p><strong>Note:</strong> {@link #updateCasInfo(int, int)} must be called before this + * release. + * + * @param ciCamHandle the handle of the releasing CiCam. + * @param clientId the id of the client that is releasing the CiCam. + */ + void releaseCiCam(in int ciCamHandle, int clientId); + /* * Notifies the TRM that the Lnb with the given handle was released. * diff --git a/media/java/android/media/tv/tunerresourcemanager/ResourceClientProfile.aidl b/media/java/android/media/tv/tunerresourcemanager/aidl/android/media/tv/tunerresourcemanager/ResourceClientProfile.aidl index ed90c1dc3996..08c2bb85c0c4 100644 --- a/media/java/android/media/tv/tunerresourcemanager/ResourceClientProfile.aidl +++ b/media/java/android/media/tv/tunerresourcemanager/aidl/android/media/tv/tunerresourcemanager/ResourceClientProfile.aidl @@ -22,4 +22,8 @@ package android.media.tv.tunerresourcemanager; * * @hide */ -parcelable ResourceClientProfile;
\ No newline at end of file +parcelable ResourceClientProfile { + String tvInputSessionId; + + int useCase; +}
\ No newline at end of file diff --git a/media/java/android/media/tv/tunerresourcemanager/aidl/android/media/tv/tunerresourcemanager/TunerCiCamRequest.aidl b/media/java/android/media/tv/tunerresourcemanager/aidl/android/media/tv/tunerresourcemanager/TunerCiCamRequest.aidl new file mode 100644 index 000000000000..76f9f83ead82 --- /dev/null +++ b/media/java/android/media/tv/tunerresourcemanager/aidl/android/media/tv/tunerresourcemanager/TunerCiCamRequest.aidl @@ -0,0 +1,28 @@ +/* + * Copyright 2020 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.tunerresourcemanager; + +/** + * A wrapper of a ciCam requests that contains all the request info of the client. + * + * @hide + */ +parcelable TunerCiCamRequest { + int clientId; + + int ciCamId; +}
\ No newline at end of file diff --git a/media/java/android/media/tv/tunerresourcemanager/TunerDemuxRequest.aidl b/media/java/android/media/tv/tunerresourcemanager/aidl/android/media/tv/tunerresourcemanager/TunerDemuxRequest.aidl index 919a215a9ce5..457f90ce866d 100644 --- a/media/java/android/media/tv/tunerresourcemanager/TunerDemuxRequest.aidl +++ b/media/java/android/media/tv/tunerresourcemanager/aidl/android/media/tv/tunerresourcemanager/TunerDemuxRequest.aidl @@ -21,4 +21,6 @@ package android.media.tv.tunerresourcemanager; * * @hide */ -parcelable TunerDemuxRequest;
\ No newline at end of file +parcelable TunerDemuxRequest { + int clientId; +}
\ No newline at end of file diff --git a/media/java/android/media/tv/tunerresourcemanager/TunerDescramblerRequest.aidl b/media/java/android/media/tv/tunerresourcemanager/aidl/android/media/tv/tunerresourcemanager/TunerDescramblerRequest.aidl index fbafb3bc010e..98ab7301bac9 100644 --- a/media/java/android/media/tv/tunerresourcemanager/TunerDescramblerRequest.aidl +++ b/media/java/android/media/tv/tunerresourcemanager/aidl/android/media/tv/tunerresourcemanager/TunerDescramblerRequest.aidl @@ -21,4 +21,6 @@ package android.media.tv.tunerresourcemanager; * * @hide */ -parcelable TunerDescramblerRequest;
\ No newline at end of file +parcelable TunerDescramblerRequest { + int clientId; +}
\ No newline at end of file diff --git a/media/java/android/media/tv/tunerresourcemanager/TunerFrontendInfo.aidl b/media/java/android/media/tv/tunerresourcemanager/aidl/android/media/tv/tunerresourcemanager/TunerFrontendInfo.aidl index e649c2aa3fd6..edf96ddd4e5a 100644 --- a/media/java/android/media/tv/tunerresourcemanager/TunerFrontendInfo.aidl +++ b/media/java/android/media/tv/tunerresourcemanager/aidl/android/media/tv/tunerresourcemanager/TunerFrontendInfo.aidl @@ -21,4 +21,10 @@ package android.media.tv.tunerresourcemanager; * * @hide */ -parcelable TunerFrontendInfo;
\ No newline at end of file +parcelable TunerFrontendInfo { + int handle; + + int frontendType; + + int exclusiveGroupId; +} diff --git a/media/java/android/media/tv/tunerresourcemanager/TunerFrontendRequest.aidl b/media/java/android/media/tv/tunerresourcemanager/aidl/android/media/tv/tunerresourcemanager/TunerFrontendRequest.aidl index 5e48adc075b8..4d9822215842 100644 --- a/media/java/android/media/tv/tunerresourcemanager/TunerFrontendRequest.aidl +++ b/media/java/android/media/tv/tunerresourcemanager/aidl/android/media/tv/tunerresourcemanager/TunerFrontendRequest.aidl @@ -21,4 +21,8 @@ package android.media.tv.tunerresourcemanager; * * @hide */ -parcelable TunerFrontendRequest;
\ No newline at end of file +parcelable TunerFrontendRequest { + int clientId; + + int frontendType; +}
\ No newline at end of file diff --git a/media/java/android/media/tv/tunerresourcemanager/TunerLnbRequest.aidl b/media/java/android/media/tv/tunerresourcemanager/aidl/android/media/tv/tunerresourcemanager/TunerLnbRequest.aidl index 0e6fcde51642..1a059ea632f2 100644 --- a/media/java/android/media/tv/tunerresourcemanager/TunerLnbRequest.aidl +++ b/media/java/android/media/tv/tunerresourcemanager/aidl/android/media/tv/tunerresourcemanager/TunerLnbRequest.aidl @@ -21,4 +21,6 @@ package android.media.tv.tunerresourcemanager; * * @hide */ -parcelable TunerLnbRequest;
\ No newline at end of file +parcelable TunerLnbRequest { + int clientId; +}
\ No newline at end of file diff --git a/media/jni/Android.bp b/media/jni/Android.bp index 25b1b4026265..decf68f26c0e 100644 --- a/media/jni/Android.bp +++ b/media/jni/Android.bp @@ -137,12 +137,16 @@ cc_library_shared { cc_library_shared { name: "libmedia_tv_tuner", + srcs: [ "android_media_tv_Tuner.cpp", "tuner/DemuxClient.cpp", + "tuner/DescramblerClient.cpp", "tuner/DvrClient.cpp", "tuner/FilterClient.cpp", "tuner/FrontendClient.cpp", + "tuner/LnbClient.cpp", + "tuner/TimeFilterClient.cpp", "tuner/TunerClient.cpp", ], @@ -160,6 +164,7 @@ cc_library_shared { "libnativehelper", "libutils", "tv_tuner_aidl_interface-ndk_platform", + "tv_tuner_resource_manager_aidl_interface-ndk_platform" ], defaults: [ "libcodec2-impl-defaults", diff --git a/media/jni/android_media_tv_Tuner.cpp b/media/jni/android_media_tv_Tuner.cpp index a4abf3693096..ee2d83b9ca77 100644 --- a/media/jni/android_media_tv_Tuner.cpp +++ b/media/jni/android_media_tv_Tuner.cpp @@ -211,59 +211,46 @@ void DestroyCallback(const C2Buffer * buf, void *arg) { } namespace android { -/////////////// LnbCallback /////////////////////// -LnbCallback::LnbCallback(jobject lnbObj, LnbId id) : mId(id) { - JNIEnv *env = AndroidRuntime::getJNIEnv(); - mLnb = env->NewWeakGlobalRef(lnbObj); -} -LnbCallback::~LnbCallback() { - JNIEnv *env = AndroidRuntime::getJNIEnv(); - env->DeleteWeakGlobalRef(mLnb); - mLnb = NULL; -} +/////////////// LnbClientCallbackImpl /////////////////////// -Return<void> LnbCallback::onEvent(LnbEventType lnbEventType) { - ALOGD("LnbCallback::onEvent, type=%d", lnbEventType); +void LnbClientCallbackImpl::onEvent(const LnbEventType lnbEventType) { + ALOGD("LnbClientCallbackImpl::onEvent, type=%d", lnbEventType); JNIEnv *env = AndroidRuntime::getJNIEnv(); env->CallVoidMethod( - mLnb, + mLnbObj, gFields.onLnbEventID, (jint)lnbEventType); - return Void(); } -Return<void> LnbCallback::onDiseqcMessage(const hidl_vec<uint8_t>& diseqcMessage) { - ALOGD("LnbCallback::onDiseqcMessage"); + +void LnbClientCallbackImpl::onDiseqcMessage(const hidl_vec<uint8_t>& diseqcMessage) { + ALOGD("LnbClientCallbackImpl::onDiseqcMessage"); JNIEnv *env = AndroidRuntime::getJNIEnv(); jbyteArray array = env->NewByteArray(diseqcMessage.size()); env->SetByteArrayRegion( array, 0, diseqcMessage.size(), reinterpret_cast<jbyte*>(diseqcMessage[0])); env->CallVoidMethod( - mLnb, + mLnbObj, gFields.onLnbDiseqcMessageID, array); - return Void(); } -/////////////// Lnb /////////////////////// - -Lnb::Lnb(sp<ILnb> sp, jobject obj) : mLnbSp(sp) { - JNIEnv *env = AndroidRuntime::getJNIEnv(); - mLnbObj = env->NewWeakGlobalRef(obj); +void LnbClientCallbackImpl::setLnb(jweak lnbObj) { + ALOGD("LnbClientCallbackImpl::setLnb"); + mLnbObj = lnbObj; } -Lnb::~Lnb() { +LnbClientCallbackImpl::~LnbClientCallbackImpl() { JNIEnv *env = AndroidRuntime::getJNIEnv(); - env->DeleteWeakGlobalRef(mLnbObj); - mLnbObj = NULL; -} - -sp<ILnb> Lnb::getILnb() { - return mLnbSp; + if (mLnbObj != NULL) { + env->DeleteWeakGlobalRef(mLnbObj); + mLnbObj = NULL; + } } /////////////// DvrClientCallbackImpl /////////////////////// + void DvrClientCallbackImpl::onRecordStatus(RecordStatus status) { ALOGD("DvrClientCallbackImpl::onRecordStatus"); JNIEnv *env = AndroidRuntime::getJNIEnv(); @@ -854,29 +841,9 @@ void FilterClientCallbackImpl::setFilter(jweak filterObj, sp<FilterClient> filte mFilterClient = filterClient; } -/////////////// TimeFilter /////////////////////// - -TimeFilter::TimeFilter(sp<ITimeFilter> sp, jobject obj) : mTimeFilterSp(sp) { - JNIEnv *env = AndroidRuntime::getJNIEnv(); - mTimeFilterObj = env->NewWeakGlobalRef(obj); -} - -TimeFilter::~TimeFilter() { - ALOGD("~TimeFilter"); - JNIEnv *env = AndroidRuntime::getJNIEnv(); - - env->DeleteWeakGlobalRef(mTimeFilterObj); - mTimeFilterObj = NULL; -} - -sp<ITimeFilter> TimeFilter::getITimeFilter() { - return mTimeFilterSp; -} - /////////////// FrontendClientCallbackImpl /////////////////////// -FrontendClientCallbackImpl::FrontendClientCallbackImpl( - jweak tunerObj, FrontendId id) : mObject(tunerObj), mId(id) {} +FrontendClientCallbackImpl::FrontendClientCallbackImpl(jweak tunerObj) : mObject(tunerObj) {} void FrontendClientCallbackImpl::onEvent(FrontendEventType frontendEventType) { ALOGD("FrontendClientCallbackImpl::onEvent, type=%d", frontendEventType); @@ -1118,8 +1085,6 @@ void FrontendClientCallbackImpl::onScanMessageExt1_1(FrontendScanMessageTypeExt1 /////////////// Tuner /////////////////////// -sp<ITuner> JTuner::mTuner; -sp<::android::hardware::tv::tuner::V1_1::ITuner> JTuner::mTuner_1_1; sp<TunerClient> JTuner::mTunerClient; JTuner::JTuner(JNIEnv *env, jobject thiz) @@ -1129,29 +1094,22 @@ JTuner::JTuner(JNIEnv *env, jobject thiz) mClass = (jclass)env->NewGlobalRef(clazz); mObject = env->NewWeakGlobalRef(thiz); - // TODO: remove after migrate to client lib - if (mTuner == NULL) { - mTuner = getTunerService(); - } if (mTunerClient == NULL) { mTunerClient = new TunerClient(); } } JTuner::~JTuner() { - if (mFe != NULL) { - mFe->close(); + if (mFeClient != NULL) { + mFeClient->close(); } - if (mDemux != NULL) { - mDemux->close(); + if (mDemuxClient != NULL) { + mDemuxClient->close(); } JNIEnv *env = AndroidRuntime::getJNIEnv(); env->DeleteWeakGlobalRef(mObject); env->DeleteGlobalRef(mClass); - mTuner = NULL; - mFe = NULL; - mDemux = NULL; mTunerClient = NULL; mFeClient = NULL; mDemuxClient = NULL; @@ -1159,23 +1117,6 @@ JTuner::~JTuner() { mObject = NULL; } -sp<ITuner> JTuner::getTunerService() { - if (mTuner == nullptr) { - mTuner_1_1 = ::android::hardware::tv::tuner::V1_1::ITuner::getService(); - - if (mTuner_1_1 == nullptr) { - ALOGW("Failed to get tuner 1.1 service."); - mTuner = ITuner::getService(); - if (mTuner == nullptr) { - ALOGW("Failed to get tuner 1.0 service."); - } - } else { - mTuner = static_cast<sp<ITuner>>(mTuner_1_1); - } - } - return mTuner; -} - jint JTuner::getTunerVersion() { ALOGD("JTuner::getTunerVersion()"); return (jint) mTunerClient->getHalTunerVersion(); @@ -1205,27 +1146,6 @@ jobject JTuner::getFrontendIds() { } jobject JTuner::openFrontendByHandle(int feHandle) { - sp<IFrontend> fe; - Result res; - uint32_t id = getResourceIdFromHandle(feHandle); - - mTuner->openFrontendById(id, [&](Result r, const sp<IFrontend>& frontend) { - fe = frontend; - res = r; - }); - if (res != Result::SUCCESS || fe == nullptr) { - ALOGE("Failed to open frontend"); - return NULL; - } - mFe = fe; - mFe_1_1 = ::android::hardware::tv::tuner::V1_1::IFrontend::castFrom(mFe); - mFeId = id; - if (mDemux != NULL) { - mDemux->setFrontendDataSource(mFeId); - } - - jint jId = (jint) id; - // TODO: Handle reopening frontend with different handle sp<FrontendClient> feClient = mTunerClient->openFrontend(feHandle); if (feClient == NULL) { @@ -1235,11 +1155,10 @@ jobject JTuner::openFrontendByHandle(int feHandle) { mFeClient = feClient; mFeId = mFeClient->getId(); - jId = (jint) id; if (mDemuxClient != NULL) { mDemuxClient->setFrontendDataSource(mFeClient); } - sp<FrontendClientCallbackImpl> feClientCb = new FrontendClientCallbackImpl(mObject, id); + sp<FrontendClientCallbackImpl> feClientCb = new FrontendClientCallbackImpl(mObject); mFeClient->setCallback(feClientCb); JNIEnv *env = AndroidRuntime::getJNIEnv(); @@ -1248,7 +1167,7 @@ jobject JTuner::openFrontendByHandle(int feHandle) { env->FindClass("android/media/tv/tuner/Tuner$Frontend"), gFields.frontendInitID, mObject, - (jint) jId); + (jint) mFeId); } jobject JTuner::getAnalogFrontendCaps(JNIEnv *env, FrontendInfo::FrontendCapabilities& caps) { @@ -1473,85 +1392,63 @@ jobject JTuner::getFrontendInfo(int id) { maxSymbolRate, acquireRange, exclusiveGroupId, statusCaps, jcaps); } -jintArray JTuner::getLnbIds() { - ALOGD("JTuner::getLnbIds()"); - Result res; - hidl_vec<LnbId> lnbIds; - mTuner->getLnbIds([&](Result r, const hidl_vec<LnbId>& ids) { - lnbIds = ids; - res = r; - }); - if (res != Result::SUCCESS || lnbIds.size() == 0) { - ALOGW("Lnb isn't available"); +jobject JTuner::openLnbByHandle(int handle) { + if (mTunerClient == NULL) { return NULL; } - mLnbIds = lnbIds; - JNIEnv *env = AndroidRuntime::getJNIEnv(); - - jintArray ids = env->NewIntArray(mLnbIds.size()); - env->SetIntArrayRegion(ids, 0, mLnbIds.size(), reinterpret_cast<jint*>(&mLnbIds[0])); - - return ids; -} + sp<LnbClient> lnbClient; + sp<LnbClientCallbackImpl> callback = new LnbClientCallbackImpl(); + lnbClient = mTunerClient->openLnb(handle); + if (lnbClient == NULL) { + ALOGD("Failed to open lnb, handle = %d", handle); + return NULL; + } -jobject JTuner::openLnbById(int id) { - sp<ILnb> iLnbSp; - Result r; - mTuner->openLnbById(id, [&](Result res, const sp<ILnb>& lnb) { - r = res; - iLnbSp = lnb; - }); - if (r != Result::SUCCESS || iLnbSp == nullptr) { - ALOGE("Failed to open lnb"); + if (lnbClient->setCallback(callback) != Result::SUCCESS) { + ALOGD("Failed to set lnb callback"); return NULL; } - mLnb = iLnbSp; JNIEnv *env = AndroidRuntime::getJNIEnv(); jobject lnbObj = env->NewObject( env->FindClass("android/media/tv/tuner/Lnb"), - gFields.lnbInitID, - (jint) id); - - sp<LnbCallback> lnbCb = new LnbCallback(lnbObj, id); - mLnb->setCallback(lnbCb); + gFields.lnbInitID); - sp<Lnb> lnbSp = new Lnb(iLnbSp, lnbObj); - lnbSp->incStrong(lnbObj); - env->SetLongField(lnbObj, gFields.lnbContext, (jlong) lnbSp.get()); + lnbClient->incStrong(lnbObj); + env->SetLongField(lnbObj, gFields.lnbContext, (jlong)lnbClient.get()); + callback->setLnb(env->NewWeakGlobalRef(lnbObj)); return lnbObj; } jobject JTuner::openLnbByName(jstring name) { + if (mTunerClient == NULL) { + return NULL; + } + JNIEnv *env = AndroidRuntime::getJNIEnv(); std::string lnbName(env->GetStringUTFChars(name, nullptr)); - sp<ILnb> iLnbSp; - Result res; - LnbId id; - mTuner->openLnbByName(lnbName, [&](Result r, LnbId lnbId, const sp<ILnb>& lnb) { - res = r; - iLnbSp = lnb; - id = lnbId; - }); - if (res != Result::SUCCESS || iLnbSp == nullptr) { - ALOGE("Failed to open lnb"); + sp<LnbClient> lnbClient; + sp<LnbClientCallbackImpl> callback = new LnbClientCallbackImpl(); + lnbClient = mTunerClient->openLnbByName(lnbName); + if (lnbClient == NULL) { + ALOGD("Failed to open lnb by name, name = %s", lnbName.c_str()); + return NULL; + } + + if (lnbClient->setCallback(callback) != Result::SUCCESS) { + ALOGD("Failed to set lnb callback"); return NULL; } - mLnb = iLnbSp; jobject lnbObj = env->NewObject( env->FindClass("android/media/tv/tuner/Lnb"), - gFields.lnbInitID, - id); - - sp<LnbCallback> lnbCb = new LnbCallback(lnbObj, id); - mLnb->setCallback(lnbCb); + gFields.lnbInitID); - sp<Lnb> lnbSp = new Lnb(iLnbSp, lnbObj); - lnbSp->incStrong(lnbObj); - env->SetLongField(lnbObj, gFields.lnbContext, (jlong) lnbSp.get()); + lnbClient->incStrong(lnbObj); + env->SetLongField(lnbObj, gFields.lnbContext, (jlong)lnbClient.get()); + callback->setLnb(env->NewWeakGlobalRef(lnbObj)); return lnbObj; } @@ -1574,109 +1471,66 @@ int JTuner::stopTune() { int JTuner::scan(const FrontendSettings& settings, FrontendScanType scanType, const FrontendSettingsExt1_1& settingsExt1_1) { - if (mFe == NULL) { - ALOGE("frontend is not initialized"); + if (mFeClient == NULL) { + ALOGE("frontend client is not initialized"); return (int)Result::INVALID_STATE; } - Result result; - sp<::android::hardware::tv::tuner::V1_1::IFrontend> fe_1_1 = - ::android::hardware::tv::tuner::V1_1::IFrontend::castFrom(mFe); - if (fe_1_1 == NULL) { - ALOGD("1.1 frontend is not found. Using 1.0 instead."); - result = mFe->scan(settings, scanType); - return (int)result; - } - - result = fe_1_1->scan_1_1(settings, scanType, settingsExt1_1); + Result result = mFeClient->scan(settings, scanType, settingsExt1_1); return (int)result; } int JTuner::stopScan() { - if (mFe == NULL) { - ALOGE("frontend is not initialized"); + if (mFeClient == NULL) { + ALOGE("frontend client is not initialized"); return (int)Result::INVALID_STATE; } - Result result = mFe->stopScan(); + Result result = mFeClient->stopScan(); return (int)result; } -int JTuner::setLnb(int id) { - if (mFe == NULL) { - ALOGE("frontend is not initialized"); +int JTuner::setLnb(sp<LnbClient> lnbClient) { + if (mFeClient == NULL) { + ALOGE("frontend client is not initialized"); return (int)Result::INVALID_STATE; } - Result result = mFe->setLnb(id); + if (lnbClient == NULL) { + ALOGE("lnb is not initialized"); + return (int)Result::INVALID_STATE; + } + Result result = mFeClient->setLnb(lnbClient); return (int)result; } int JTuner::setLna(bool enable) { - if (mFe == NULL) { - ALOGE("frontend is not initialized"); + if (mFeClient == NULL) { + ALOGE("frontend client is not initialized"); return (int)Result::INVALID_STATE; } - Result result = mFe->setLna(enable); + Result result = mFeClient->setLna(enable); return (int)result; } -Result JTuner::openDemux() { - if (mTuner == nullptr || mTunerClient == nullptr) { +Result JTuner::openDemux(int handle) { + if (mTunerClient == nullptr) { return Result::NOT_INITIALIZED; } - Result res = Result::SUCCESS; - - if (mDemux == nullptr) { - uint32_t id; - sp<IDemux> demuxSp; - mTuner->openDemux([&](Result r, uint32_t demuxId, const sp<IDemux>& demux) { - demuxSp = demux; - id = demuxId; - res = r; - ALOGD("open demux, id = %d", demuxId); - }); - if (res == Result::SUCCESS) { - mDemux = demuxSp; - mDemuxId = id; - if (mFe != NULL) { - mDemux->setFrontendDataSource(mFeId); - } - } else { - return res; - } - } - - // TODO: replace demux opening with mTunerClient->openDemux(handle) - // when DemuxClient is fully ready if (mDemuxClient == nullptr) { - sp<DemuxClient> demuxClient = new DemuxClient(); - if (demuxClient == NULL) { + mDemuxClient = mTunerClient->openDemux(handle); + if (mDemuxClient == NULL) { ALOGE("Failed to open demux"); return Result::UNKNOWN_ERROR; } - mDemuxClient = demuxClient; - mDemuxClient->setHidlDemux(mDemux); if (mFeClient != NULL) { mDemuxClient->setFrontendDataSource(mFeClient); } } - return res; + return Result::SUCCESS; } jint JTuner::close() { Result res = Result::SUCCESS; - if (mFe != NULL) { - res = mFe->close(); - if (res != Result::SUCCESS) { - return (jint) res; - } - } - if (mDemux != NULL) { - res = mDemux->close(); - if (res != Result::SUCCESS) { - return (jint) res; - } - } if (mFeClient != NULL) { res = mFeClient->close(); @@ -1726,42 +1580,23 @@ jobject JTuner::getAvSyncTime(jint id) { int JTuner::connectCiCam(jint id) { if (mDemuxClient == NULL) { - Result r = openDemux(); - if (r != Result::SUCCESS) { - return (int) r; - } + return (int)Result::NOT_INITIALIZED; } Result r = mDemuxClient->connectCiCam((int)id); return (int) r; } int JTuner::linkCiCam(int id) { - if (mFe_1_1 == NULL) { - ALOGE("frontend 1.1 is not initialized"); + if (mFeClient == NULL) { + ALOGE("frontend client is not initialized"); return (int)Constant::INVALID_LTS_ID; } - - Result res; - uint32_t ltsId; - mFe_1_1->linkCiCam(static_cast<uint32_t>(id), - [&](Result r, uint32_t id) { - res = r; - ltsId = id; - }); - - if (res != Result::SUCCESS) { - return (int)Constant::INVALID_LTS_ID; - } - - return (int) ltsId; + return mFeClient->linkCiCamToFrontend(id); } int JTuner::disconnectCiCam() { if (mDemuxClient == NULL) { - Result r = openDemux(); - if (r != Result::SUCCESS) { - return (int) r; - } + return (int)Result::NOT_INITIALIZED; } Result r = mDemuxClient->disconnectCiCam(); return (int) r; @@ -1769,33 +1604,29 @@ int JTuner::disconnectCiCam() { int JTuner::unlinkCiCam(int id) { - if (mFe_1_1 == NULL) { - ALOGE("frontend 1.1 is not initialized"); + if (mFeClient == NULL) { + ALOGE("frontend client is not initialized"); return (int)Result::INVALID_STATE; } - Result r = mFe_1_1->unlinkCiCam(static_cast<uint32_t>(id)); + Result r = mFeClient->unlinkCiCamToFrontend(id); return (int) r; } jobject JTuner::openDescrambler() { ALOGD("JTuner::openDescrambler"); - if (mTuner == nullptr || mDemux == nullptr) { + if (mTunerClient == nullptr || mDemuxClient == nullptr) { return NULL; } - sp<IDescrambler> descramblerSp; - Result res; - mTuner->openDescrambler([&](Result r, const sp<IDescrambler>& descrambler) { - res = r; - descramblerSp = descrambler; - }); + sp<DescramblerClient> descramblerClient = mTunerClient->openDescrambler(0/*unused*/); - if (res != Result::SUCCESS || descramblerSp == NULL) { + if (descramblerClient == NULL) { + ALOGD("Failed to open descrambler"); return NULL; } - descramblerSp->setDemuxSource(mDemuxId); + descramblerClient->setDemuxSource(mDemuxClient); JNIEnv *env = AndroidRuntime::getJNIEnv(); jobject descramblerObj = @@ -1803,14 +1634,14 @@ jobject JTuner::openDescrambler() { env->FindClass("android/media/tv/tuner/Descrambler"), gFields.descramblerInitID); - descramblerSp->incStrong(descramblerObj); - env->SetLongField(descramblerObj, gFields.descramblerContext, (jlong)descramblerSp.get()); + descramblerClient->incStrong(descramblerObj); + env->SetLongField(descramblerObj, gFields.descramblerContext, (jlong)descramblerClient.get()); return descramblerObj; } jobject JTuner::openFilter(DemuxFilterType type, int bufferSize) { - if (mDemux == NULL || mDemuxClient == NULL) { + if (mDemuxClient == NULL) { return NULL; } @@ -1844,20 +1675,7 @@ jobject JTuner::openFilter(DemuxFilterType type, int bufferSize) { } jobject JTuner::openTimeFilter() { - if (mDemux == NULL) { - if (openDemux() != Result::SUCCESS) { - return NULL; - } - } - sp<ITimeFilter> iTimeFilterSp; - Result res; - mDemux->openTimeFilter( - [&](Result r, const sp<ITimeFilter>& filter) { - iTimeFilterSp = filter; - res = r; - }); - - if (res != Result::SUCCESS || iTimeFilterSp == NULL) { + if (mDemuxClient == NULL) { return NULL; } @@ -1866,9 +1684,13 @@ jobject JTuner::openTimeFilter() { env->NewObject( env->FindClass("android/media/tv/tuner/filter/TimeFilter"), gFields.timeFilterInitID); - sp<TimeFilter> timeFilterSp = new TimeFilter(iTimeFilterSp, timeFilterObj); - timeFilterSp->incStrong(timeFilterObj); - env->SetLongField(timeFilterObj, gFields.timeFilterContext, (jlong)timeFilterSp.get()); + sp<TimeFilterClient> timeFilterClient = mDemuxClient->openTimeFilter(); + if (timeFilterClient == NULL) { + ALOGD("Failed to open time filter."); + return NULL; + } + timeFilterClient->incStrong(timeFilterObj); + env->SetLongField(timeFilterObj, gFields.timeFilterContext, (jlong)timeFilterClient.get()); return timeFilterObj; } @@ -1912,35 +1734,36 @@ jobject JTuner::openDvr(DvrType type, jlong bufferSize) { } jobject JTuner::getDemuxCaps() { - DemuxCapabilities caps; - Result res; - mTuner->getDemuxCaps([&](Result r, const DemuxCapabilities& demuxCaps) { - caps = demuxCaps; - res = r; - }); - if (res != Result::SUCCESS) { + if (mTunerClient == NULL) { return NULL; } + + shared_ptr<DemuxCapabilities> caps; + caps = mTunerClient->getDemuxCaps(); + if (caps == NULL) { + return NULL; + } + JNIEnv *env = AndroidRuntime::getJNIEnv(); jclass clazz = env->FindClass("android/media/tv/tuner/DemuxCapabilities"); jmethodID capsInit = env->GetMethodID(clazz, "<init>", "(IIIIIIIIIJI[IZ)V"); - jint numDemux = caps.numDemux; - jint numRecord = caps.numRecord; - jint numPlayback = caps.numPlayback; - jint numTsFilter = caps.numTsFilter; - jint numSectionFilter = caps.numSectionFilter; - jint numAudioFilter = caps.numAudioFilter; - jint numVideoFilter = caps.numVideoFilter; - jint numPesFilter = caps.numPesFilter; - jint numPcrFilter = caps.numPcrFilter; - jlong numBytesInSectionFilter = caps.numBytesInSectionFilter; - jint filterCaps = static_cast<jint>(caps.filterCaps); - jboolean bTimeFilter = caps.bTimeFilter; - - jintArray linkCaps = env->NewIntArray(caps.linkCaps.size()); + jint numDemux = caps->numDemux; + jint numRecord = caps->numRecord; + jint numPlayback = caps->numPlayback; + jint numTsFilter = caps->numTsFilter; + jint numSectionFilter = caps->numSectionFilter; + jint numAudioFilter = caps->numAudioFilter; + jint numVideoFilter = caps->numVideoFilter; + jint numPesFilter = caps->numPesFilter; + jint numPcrFilter = caps->numPcrFilter; + jlong numBytesInSectionFilter = caps->numBytesInSectionFilter; + jint filterCaps = static_cast<jint>(caps->filterCaps); + jboolean bTimeFilter = caps->bTimeFilter; + + jintArray linkCaps = env->NewIntArray(caps->linkCaps.size()); env->SetIntArrayRegion( - linkCaps, 0, caps.linkCaps.size(), reinterpret_cast<jint*>(&caps.linkCaps[0])); + linkCaps, 0, caps->linkCaps.size(), reinterpret_cast<jint*>(&caps->linkCaps[0])); return env->NewObject(clazz, capsInit, numDemux, numRecord, numPlayback, numTsFilter, numSectionFilter, numAudioFilter, numVideoFilter, numPesFilter, numPcrFilter, @@ -1948,7 +1771,7 @@ jobject JTuner::getDemuxCaps() { } jobject JTuner::getFrontendStatus(jintArray types) { - if (mFe == NULL) { + if (mFeClient == NULL) { return NULL; } JNIEnv *env = AndroidRuntime::getJNIEnv(); @@ -1965,38 +1788,8 @@ jobject JTuner::getFrontendStatus(jintArray types) { } } - Result res; - hidl_vec<FrontendStatus> status; - hidl_vec<FrontendStatusExt1_1> status_1_1; - - if (v.size() > 0) { - mFe->getStatus(v, - [&](Result r, const hidl_vec<FrontendStatus>& s) { - res = r; - status = s; - }); - if (res != Result::SUCCESS) { - return NULL; - } - } - - if (v_1_1.size() > 0) { - sp<::android::hardware::tv::tuner::V1_1::IFrontend> iFeSp_1_1; - iFeSp_1_1 = ::android::hardware::tv::tuner::V1_1::IFrontend::castFrom(mFe); - - if (iFeSp_1_1 != NULL) { - iFeSp_1_1->getStatusExt1_1(v_1_1, - [&](Result r, const hidl_vec<FrontendStatusExt1_1>& s) { - res = r; - status_1_1 = s; - }); - if (res != Result::SUCCESS) { - return NULL; - } - } else { - ALOGW("getStatusExt1_1 is not supported with the current HAL implementation."); - } - } + hidl_vec<FrontendStatus> status = mFeClient->getStatus(v); + hidl_vec<FrontendStatusExt1_1> status_1_1 = mFeClient->getStatusExtended_1_1(v_1_1); jclass clazz = env->FindClass("android/media/tv/tuner/frontend/FrontendStatus"); jmethodID init = env->GetMethodID(clazz, "<init>", "()V"); @@ -2542,15 +2335,6 @@ bool JTuner::isV1_1ExtendedStatusType(int type) { jint JTuner::closeFrontend() { Result r = Result::SUCCESS; - if (mFe != NULL) { - r = mFe->close(); - } - if (r == Result::SUCCESS) { - mFe = NULL; - mFe_1_1 = NULL; - } else { - return (jint) r; - } if (mFeClient != NULL) { r = mFeClient->close(); @@ -2563,14 +2347,6 @@ jint JTuner::closeFrontend() { jint JTuner::closeDemux() { Result r = Result::SUCCESS; - if (mDemux != NULL) { - r = mDemux->close(); - } - if (r == Result::SUCCESS) { - mDemux = NULL; - } else { - return (jint) r; - } if (mDemuxClient != NULL) { r = mDemuxClient->close(); @@ -2604,12 +2380,8 @@ static sp<JTuner> getTuner(JNIEnv *env, jobject thiz) { return (JTuner *)env->GetLongField(thiz, gFields.tunerContext); } -static sp<IDescrambler> getDescrambler(JNIEnv *env, jobject descrambler) { - return (IDescrambler *)env->GetLongField(descrambler, gFields.descramblerContext); -} - -static uint32_t getResourceIdFromHandle(jint handle) { - return (handle & 0x00ff0000) >> 16; +static sp<DescramblerClient> getDescramblerClient(JNIEnv *env, jobject descrambler) { + return (DescramblerClient *)env->GetLongField(descrambler, gFields.descramblerContext); } static DemuxPid getDemuxPid(int pidType, int pid) { @@ -3224,6 +2996,10 @@ static sp<FilterClient> getFilterClient(JNIEnv *env, jobject filter) { return (FilterClient *)env->GetLongField(filter, gFields.filterContext); } +static sp<LnbClient> getLnbClient(JNIEnv *env, jobject lnb) { + return (LnbClient *)env->GetLongField(lnb, gFields.lnbContext); +} + static DvrSettings getDvrSettings(JNIEnv *env, jobject settings, bool isRecorder) { DvrSettings dvrSettings; jclass clazz = env->FindClass("android/media/tv/tuner/dvr/DvrSettings"); @@ -3287,7 +3063,7 @@ static void android_media_tv_Tuner_native_init(JNIEnv *env) { jclass lnbClazz = env->FindClass("android/media/tv/tuner/Lnb"); gFields.lnbContext = env->GetFieldID(lnbClazz, "mNativeContext", "J"); - gFields.lnbInitID = env->GetMethodID(lnbClazz, "<init>", "(I)V"); + gFields.lnbInitID = env->GetMethodID(lnbClazz, "<init>", "()V"); gFields.onLnbEventID = env->GetMethodID(lnbClazz, "onEvent", "(I)V"); gFields.onLnbDiseqcMessageID = env->GetMethodID(lnbClazz, "onDiseqcMessage", "([B)V"); @@ -3376,9 +3152,14 @@ static int android_media_tv_Tuner_stop_scan(JNIEnv *env, jobject thiz) { return tuner->stopScan(); } -static int android_media_tv_Tuner_set_lnb(JNIEnv *env, jobject thiz, jint id) { +static int android_media_tv_Tuner_set_lnb(JNIEnv *env, jobject thiz, jobject lnb) { sp<JTuner> tuner = getTuner(env, thiz); - return tuner->setLnb(id); + sp<LnbClient> lnbClient = getLnbClient(env, lnb); + if (lnbClient == NULL) { + ALOGE("lnb is not initialized"); + return (int)Result::INVALID_STATE; + } + return tuner->setLnb(lnbClient); } static int android_media_tv_Tuner_set_lna(JNIEnv *env, jobject thiz, jboolean enable) { @@ -3433,15 +3214,9 @@ static jobject android_media_tv_Tuner_get_frontend_info(JNIEnv *env, jobject thi return tuner->getFrontendInfo(id); } -static jintArray android_media_tv_Tuner_get_lnb_ids(JNIEnv *env, jobject thiz) { - sp<JTuner> tuner = getTuner(env, thiz); - return tuner->getLnbIds(); -} - static jobject android_media_tv_Tuner_open_lnb_by_handle(JNIEnv *env, jobject thiz, jint handle) { sp<JTuner> tuner = getTuner(env, thiz); - uint32_t id = getResourceIdFromHandle(handle); - return tuner->openLnbById(id); + return tuner->openLnbByHandle(handle); } static jobject android_media_tv_Tuner_open_lnb_by_name(JNIEnv *env, jobject thiz, jstring name) { @@ -4027,49 +3802,39 @@ static jint android_media_tv_Tuner_close_filter(JNIEnv *env, jobject filter) { return (jint) filterClient->close(); } -static sp<TimeFilter> getTimeFilter(JNIEnv *env, jobject filter) { - return (TimeFilter *)env->GetLongField(filter, gFields.timeFilterContext); +static sp<TimeFilterClient> getTimeFilterClient(JNIEnv *env, jobject filter) { + return (TimeFilterClient *)env->GetLongField(filter, gFields.timeFilterContext); } static int android_media_tv_Tuner_time_filter_set_timestamp( JNIEnv *env, jobject filter, jlong timestamp) { - sp<TimeFilter> filterSp = getTimeFilter(env, filter); - if (filterSp == NULL) { - ALOGD("Failed set timestamp: time filter not found"); + sp<TimeFilterClient> timeFilterClient = getTimeFilterClient(env, filter); + if (timeFilterClient == NULL) { + ALOGD("Failed set timestamp: time filter client not found"); return (int) Result::INVALID_STATE; } - sp<ITimeFilter> iFilterSp = filterSp->getITimeFilter(); - Result r = iFilterSp->setTimeStamp(static_cast<uint64_t>(timestamp)); + Result r = timeFilterClient->setTimeStamp(static_cast<uint64_t>(timestamp)); return (int) r; } static int android_media_tv_Tuner_time_filter_clear_timestamp(JNIEnv *env, jobject filter) { - sp<TimeFilter> filterSp = getTimeFilter(env, filter); - if (filterSp == NULL) { - ALOGD("Failed clear timestamp: time filter not found"); + sp<TimeFilterClient> timeFilterClient = getTimeFilterClient(env, filter); + if (timeFilterClient == NULL) { + ALOGD("Failed clear timestamp: time filter client not found"); return (int) Result::INVALID_STATE; } - sp<ITimeFilter> iFilterSp = filterSp->getITimeFilter(); - Result r = iFilterSp->clearTimeStamp(); + Result r = timeFilterClient->clearTimeStamp(); return (int) r; } static jobject android_media_tv_Tuner_time_filter_get_timestamp(JNIEnv *env, jobject filter) { - sp<TimeFilter> filterSp = getTimeFilter(env, filter); - if (filterSp == NULL) { - ALOGD("Failed get timestamp: time filter not found"); + sp<TimeFilterClient> timeFilterClient = getTimeFilterClient(env, filter); + if (timeFilterClient == NULL) { + ALOGD("Failed get timestamp: time filter client not found"); return NULL; } - - sp<ITimeFilter> iFilterSp = filterSp->getITimeFilter(); - Result res; - uint64_t timestamp; - iFilterSp->getTimeStamp( - [&](Result r, uint64_t t) { - res = r; - timestamp = t; - }); - if (res != Result::SUCCESS) { + uint64_t timestamp = timeFilterClient->getTimeStamp(); + if (timestamp == (long)Constant64Bit::INVALID_PRESENTATION_TIME_STAMP) { return NULL; } @@ -4081,21 +3846,13 @@ static jobject android_media_tv_Tuner_time_filter_get_timestamp(JNIEnv *env, job } static jobject android_media_tv_Tuner_time_filter_get_source_time(JNIEnv *env, jobject filter) { - sp<TimeFilter> filterSp = getTimeFilter(env, filter); - if (filterSp == NULL) { - ALOGD("Failed get source time: time filter not found"); + sp<TimeFilterClient> timeFilterClient = getTimeFilterClient(env, filter); + if (timeFilterClient == NULL) { + ALOGD("Failed get source time: time filter client not found"); return NULL; } - - sp<ITimeFilter> iFilterSp = filterSp->getITimeFilter(); - Result res; - uint64_t timestamp; - iFilterSp->getSourceTime( - [&](Result r, uint64_t t) { - res = r; - timestamp = t; - }); - if (res != Result::SUCCESS) { + uint64_t timestamp = timeFilterClient->getSourceTime(); + if (timestamp == (long)Constant64Bit::INVALID_PRESENTATION_TIME_STAMP) { return NULL; } @@ -4107,15 +3864,15 @@ static jobject android_media_tv_Tuner_time_filter_get_source_time(JNIEnv *env, j } static int android_media_tv_Tuner_time_filter_close(JNIEnv *env, jobject filter) { - sp<TimeFilter> filterSp = getTimeFilter(env, filter); - if (filterSp == NULL) { - ALOGD("Failed close time filter: time filter not found"); + sp<TimeFilterClient> timeFilterClient = getTimeFilterClient(env, filter); + if (timeFilterClient == NULL) { + ALOGD("Failed close time filter: time filter client not found"); return (int) Result::INVALID_STATE; } - Result r = filterSp->getITimeFilter()->close(); + Result r = timeFilterClient->close(); if (r == Result::SUCCESS) { - filterSp->decStrong(filter); + timeFilterClient->decStrong(filter); env->SetLongField(filter, gFields.timeFilterContext, 0); } return (int) r; @@ -4128,49 +3885,47 @@ static jobject android_media_tv_Tuner_open_descrambler(JNIEnv *env, jobject thiz static jint android_media_tv_Tuner_descrambler_add_pid( JNIEnv *env, jobject descrambler, jint pidType, jint pid, jobject filter) { - sp<IDescrambler> descramblerSp = getDescrambler(env, descrambler); - if (descramblerSp == NULL) { + sp<DescramblerClient> descramblerClient = getDescramblerClient(env, descrambler); + if (descramblerClient == NULL) { return (jint) Result::NOT_INITIALIZED; } - // TODO: use filter client once descramblerClient is ready - sp<IFilter> iFilterSp = getFilterClient(env, filter)->getHalFilter(); - Result result = descramblerSp->addPid(getDemuxPid((int)pidType, (int)pid), iFilterSp); + sp<FilterClient> filterClient = getFilterClient(env, filter); + Result result = descramblerClient->addPid(getDemuxPid((int)pidType, (int)pid), filterClient); return (jint) result; } static jint android_media_tv_Tuner_descrambler_remove_pid( JNIEnv *env, jobject descrambler, jint pidType, jint pid, jobject filter) { - sp<IDescrambler> descramblerSp = getDescrambler(env, descrambler); - if (descramblerSp == NULL) { + sp<DescramblerClient> descramblerClient = getDescramblerClient(env, descrambler); + if (descramblerClient == NULL) { return (jint) Result::NOT_INITIALIZED; } - // TODO: use filter client once descramblerClient is ready - sp<IFilter> iFilterSp = getFilterClient(env, filter)->getHalFilter(); - Result result = descramblerSp->removePid(getDemuxPid((int)pidType, (int)pid), iFilterSp); + sp<FilterClient> filterClient = getFilterClient(env, filter); + Result result = descramblerClient->removePid(getDemuxPid((int)pidType, (int)pid), filterClient); return (jint) result; } static jint android_media_tv_Tuner_descrambler_set_key_token( JNIEnv* env, jobject descrambler, jbyteArray keyToken) { - sp<IDescrambler> descramblerSp = getDescrambler(env, descrambler); - if (descramblerSp == NULL) { + sp<DescramblerClient> descramblerClient = getDescramblerClient(env, descrambler); + if (descramblerClient == NULL) { return (jint) Result::NOT_INITIALIZED; } int size = env->GetArrayLength(keyToken); std::vector<uint8_t> v(size); env->GetByteArrayRegion(keyToken, 0, size, reinterpret_cast<jbyte*>(&v[0])); - Result result = descramblerSp->setKeyToken(v); + Result result = descramblerClient->setKeyToken(v); return (jint) result; } static jint android_media_tv_Tuner_close_descrambler(JNIEnv* env, jobject descrambler) { - sp<IDescrambler> descramblerSp = getDescrambler(env, descrambler); - if (descramblerSp == NULL) { + sp<DescramblerClient> descramblerClient = getDescramblerClient(env, descrambler); + if (descramblerClient == NULL) { return (jint) Result::NOT_INITIALIZED; } - Result r = descramblerSp->close(); + Result r = descramblerClient->close(); if (r == Result::SUCCESS) { - descramblerSp->decStrong(descrambler); + descramblerClient->decStrong(descrambler); } return (jint) r; } @@ -4192,9 +3947,9 @@ static jobject android_media_tv_Tuner_get_demux_caps(JNIEnv* env, jobject thiz) return tuner->getDemuxCaps(); } -static jint android_media_tv_Tuner_open_demux(JNIEnv* env, jobject thiz, jint /* handle */) { +static jint android_media_tv_Tuner_open_demux(JNIEnv* env, jobject thiz, jint handle) { sp<JTuner> tuner = getTuner(env, thiz); - return (jint) tuner->openDemux(); + return (jint) tuner->openDemux(handle); } static jint android_media_tv_Tuner_close_tuner(JNIEnv* env, jobject thiz) { @@ -4289,42 +4044,38 @@ static jint android_media_tv_Tuner_close_dvr(JNIEnv* env, jobject dvr) { return (jint) dvrClient->close(); } -static sp<Lnb> getLnb(JNIEnv *env, jobject lnb) { - return (Lnb *)env->GetLongField(lnb, gFields.lnbContext); -} - static jint android_media_tv_Tuner_lnb_set_voltage(JNIEnv* env, jobject lnb, jint voltage) { - sp<ILnb> iLnbSp = getLnb(env, lnb)->getILnb(); - Result r = iLnbSp->setVoltage(static_cast<LnbVoltage>(voltage)); + sp<LnbClient> lnbClient = getLnbClient(env, lnb); + Result r = lnbClient->setVoltage(static_cast<LnbVoltage>(voltage)); return (jint) r; } static int android_media_tv_Tuner_lnb_set_tone(JNIEnv* env, jobject lnb, jint tone) { - sp<ILnb> iLnbSp = getLnb(env, lnb)->getILnb(); - Result r = iLnbSp->setTone(static_cast<LnbTone>(tone)); + sp<LnbClient> lnbClient = getLnbClient(env, lnb); + Result r = lnbClient->setTone(static_cast<LnbTone>(tone)); return (jint) r; } static int android_media_tv_Tuner_lnb_set_position(JNIEnv* env, jobject lnb, jint position) { - sp<ILnb> iLnbSp = getLnb(env, lnb)->getILnb(); - Result r = iLnbSp->setSatellitePosition(static_cast<LnbPosition>(position)); + sp<LnbClient> lnbClient = getLnbClient(env, lnb); + Result r = lnbClient->setSatellitePosition(static_cast<LnbPosition>(position)); return (jint) r; } static int android_media_tv_Tuner_lnb_send_diseqc_msg(JNIEnv* env, jobject lnb, jbyteArray msg) { - sp<ILnb> iLnbSp = getLnb(env, lnb)->getILnb(); + sp<LnbClient> lnbClient = getLnbClient(env, lnb); int size = env->GetArrayLength(msg); std::vector<uint8_t> v(size); env->GetByteArrayRegion(msg, 0, size, reinterpret_cast<jbyte*>(&v[0])); - Result r = iLnbSp->sendDiseqcMessage(v); + Result r = lnbClient->sendDiseqcMessage(v); return (jint) r; } static int android_media_tv_Tuner_close_lnb(JNIEnv* env, jobject lnb) { - sp<Lnb> lnbSp = getLnb(env, lnb); - Result r = lnbSp->getILnb()->close(); + sp<LnbClient> lnbClient = getLnbClient(env, lnb); + Result r = lnbClient->close(); if (r == Result::SUCCESS) { - lnbSp->decStrong(lnb); + lnbClient->decStrong(lnb); env->SetLongField(lnb, gFields.lnbContext, 0); } return (jint) r; @@ -4464,7 +4215,7 @@ static const JNINativeMethod gTunerMethods[] = { { "nativeScan", "(ILandroid/media/tv/tuner/frontend/FrontendSettings;I)I", (void *)android_media_tv_Tuner_scan }, { "nativeStopScan", "()I", (void *)android_media_tv_Tuner_stop_scan }, - { "nativeSetLnb", "(I)I", (void *)android_media_tv_Tuner_set_lnb }, + { "nativeSetLnb", "(Landroid/media/tv/tuner/Lnb;)I", (void *)android_media_tv_Tuner_set_lnb }, { "nativeSetLna", "(Z)I", (void *)android_media_tv_Tuner_set_lna }, { "nativeGetFrontendStatus", "([I)Landroid/media/tv/tuner/frontend/FrontendStatus;", (void *)android_media_tv_Tuner_get_frontend_status }, @@ -4484,7 +4235,6 @@ static const JNINativeMethod gTunerMethods[] = { (void *)android_media_tv_Tuner_open_filter }, { "nativeOpenTimeFilter", "()Landroid/media/tv/tuner/filter/TimeFilter;", (void *)android_media_tv_Tuner_open_time_filter }, - { "nativeGetLnbIds", "()[I", (void *)android_media_tv_Tuner_get_lnb_ids }, { "nativeOpenLnbByHandle", "(I)Landroid/media/tv/tuner/Lnb;", (void *)android_media_tv_Tuner_open_lnb_by_handle }, { "nativeOpenLnbByName", "(Ljava/lang/String;)Landroid/media/tv/tuner/Lnb;", diff --git a/media/jni/android_media_tv_Tuner.h b/media/jni/android_media_tv_Tuner.h index 9dc4ddfa1b61..0e30b18eb2d4 100644 --- a/media/jni/android_media_tv_Tuner.h +++ b/media/jni/android_media_tv_Tuner.h @@ -17,11 +17,6 @@ #ifndef _ANDROID_MEDIA_TV_TUNER_H_ #define _ANDROID_MEDIA_TV_TUNER_H_ -#include <android/hardware/tv/tuner/1.1/IFilter.h> -#include <android/hardware/tv/tuner/1.1/IFilterCallback.h> -#include <android/hardware/tv/tuner/1.1/IFrontend.h> -#include <android/hardware/tv/tuner/1.1/IFrontendCallback.h> -#include <android/hardware/tv/tuner/1.1/ITuner.h> #include <android/hardware/tv/tuner/1.1/types.h> #include <C2BlockInternal.h> @@ -35,10 +30,14 @@ #include <utils/RefBase.h> #include "tuner/DemuxClient.h" +#include "tuner/DescramblerClient.h" #include "tuner/FilterClient.h" #include "tuner/FilterClientCallback.h" #include "tuner/FrontendClient.h" #include "tuner/FrontendClientCallback.h" +#include "tuner/LnbClient.h" +#include "tuner/LnbClientCallback.h" +#include "tuner/TimeFilterClient.h" #include "tuner/TunerClient.h" #include "jni.h" @@ -62,17 +61,6 @@ using ::android::hardware::tv::tuner::V1_0::FrontendScanMessageType; using ::android::hardware::tv::tuner::V1_0::FrontendScanType; using ::android::hardware::tv::tuner::V1_0::FrontendSettings; using ::android::hardware::tv::tuner::V1_1::FrontendSettingsExt1_1; -using ::android::hardware::tv::tuner::V1_0::IDemux; -using ::android::hardware::tv::tuner::V1_0::IDescrambler; -using ::android::hardware::tv::tuner::V1_0::IDvr; -using ::android::hardware::tv::tuner::V1_0::IDvrCallback; -using ::android::hardware::tv::tuner::V1_0::IFilter; -using ::android::hardware::tv::tuner::V1_1::IFilterCallback; -using ::android::hardware::tv::tuner::V1_0::IFrontend; -using ::android::hardware::tv::tuner::V1_0::ILnb; -using ::android::hardware::tv::tuner::V1_0::ILnbCallback; -using ::android::hardware::tv::tuner::V1_0::ITimeFilter; -using ::android::hardware::tv::tuner::V1_0::ITuner; using ::android::hardware::tv::tuner::V1_0::LnbEventType; using ::android::hardware::tv::tuner::V1_0::LnbId; using ::android::hardware::tv::tuner::V1_0::PlaybackStatus; @@ -87,21 +75,13 @@ using MQ = MessageQueue<uint8_t, kSynchronizedReadWrite>; namespace android { -struct LnbCallback : public ILnbCallback { - LnbCallback(jweak tunerObj, LnbId id); - ~LnbCallback(); - virtual Return<void> onEvent(LnbEventType lnbEventType); - virtual Return<void> onDiseqcMessage(const hidl_vec<uint8_t>& diseqcMessage); - jweak mLnb; - LnbId mId; -}; +struct LnbClientCallbackImpl : public LnbClientCallback { + ~LnbClientCallbackImpl(); + virtual void onEvent(LnbEventType lnbEventType); + virtual void onDiseqcMessage(const hidl_vec<uint8_t>& diseqcMessage); -struct Lnb : public RefBase { - Lnb(sp<ILnb> sp, jobject obj); - ~Lnb(); - sp<ILnb> getILnb(); - // TODO: remove after migrate to client lib - sp<ILnb> mLnbSp; + void setLnb(jweak lnbObj); +private: jweak mLnbObj; }; @@ -174,7 +154,7 @@ private: }; struct FrontendClientCallbackImpl : public FrontendClientCallback { - FrontendClientCallbackImpl(jweak tunerObj, FrontendId id); + FrontendClientCallbackImpl(jweak tunerObj); virtual void onEvent(FrontendEventType frontendEventType); virtual void onScanMessage( @@ -183,22 +163,10 @@ struct FrontendClientCallbackImpl : public FrontendClientCallback { FrontendScanMessageTypeExt1_1 type, const FrontendScanMessageExt1_1& messageExt); jweak mObject; - FrontendId mId; -}; - -struct TimeFilter : public RefBase { - TimeFilter(sp<ITimeFilter> sp, jweak obj); - ~TimeFilter(); - sp<ITimeFilter> getITimeFilter(); - // TODO: remove after migrate to client lib - sp<ITimeFilter> mTimeFilterSp; - jweak mTimeFilterObj; }; struct JTuner : public RefBase { JTuner(JNIEnv *env, jobject thiz); - // TODO: modify after migrate to client lib - sp<ITuner> getTunerService(); int getTunerVersion(); jobject getAvSyncHwId(sp<FilterClient> filter); jobject getAvSyncTime(jint id); @@ -215,10 +183,9 @@ struct JTuner : public RefBase { int scan(const FrontendSettings& settings, FrontendScanType scanType, const FrontendSettingsExt1_1& settingsExt1_1); int stopScan(); - int setLnb(int id); + int setLnb(sp<LnbClient> lnbClient); int setLna(bool enable); - jintArray getLnbIds(); - jobject openLnbById(int id); + jobject openLnbByHandle(int handle); jobject openLnbByName(jstring name); jobject openFilter(DemuxFilterType type, int bufferSize); jobject openTimeFilter(); @@ -226,7 +193,7 @@ struct JTuner : public RefBase { jobject openDvr(DvrType type, jlong bufferSize); jobject getDemuxCaps(); jobject getFrontendStatus(jintArray types); - Result openDemux(); + Result openDemux(int handle); jint close(); jint closeFrontend(); jint closeDemux(); @@ -237,23 +204,11 @@ protected: private: jclass mClass; jweak mObject; - // TODO: remove after migrate to client lib - static sp<ITuner> mTuner; - static sp<::android::hardware::tv::tuner::V1_1::ITuner> mTuner_1_1; static sp<TunerClient> mTunerClient; - // TODO: remove after migrate to client lib - sp<IFrontend> mFe; - // TODO: remove after migrate to client lib - sp<::android::hardware::tv::tuner::V1_1::IFrontend> mFe_1_1; sp<FrontendClient> mFeClient; int mFeId; - hidl_vec<LnbId> mLnbIds; - // TODO: remove after migrate to client lib - sp<ILnb> mLnb; - // TODO: remove after migrate to client lib - sp<IDemux> mDemux; + sp<LnbClient> mLnbClient; sp<DemuxClient> mDemuxClient; - uint32_t mDemuxId; static jobject getAnalogFrontendCaps(JNIEnv *env, FrontendInfo::FrontendCapabilities& caps); static jobject getAtsc3FrontendCaps(JNIEnv *env, FrontendInfo::FrontendCapabilities& caps); static jobject getAtscFrontendCaps(JNIEnv *env, FrontendInfo::FrontendCapabilities& caps); @@ -266,9 +221,6 @@ private: static jobject getDtmbFrontendCaps(JNIEnv *env, int id); bool isV1_1ExtendedStatusType(jint type); - static uint32_t getResourceIdFromHandle(jint handle) { - return (handle & 0x00ff0000) >> 16; - } }; class C2DataIdInfo : public C2Param { diff --git a/media/jni/tuner/DemuxClient.cpp b/media/jni/tuner/DemuxClient.cpp index 59dfd704179e..08b739854378 100644 --- a/media/jni/tuner/DemuxClient.cpp +++ b/media/jni/tuner/DemuxClient.cpp @@ -32,11 +32,13 @@ namespace android { // TODO: pending aidl interface DemuxClient::DemuxClient() { //mTunerDemux = tunerDemux; + mId = -1; } DemuxClient::~DemuxClient() { //mTunerDemux = NULL; mDemux = NULL; + mId = -1; } // TODO: remove after migration to Tuner Service is done. @@ -77,6 +79,21 @@ sp<FilterClient> DemuxClient::openFilter(DemuxFilterType type, int bufferSize, return NULL; } +sp<TimeFilterClient> DemuxClient::openTimeFilter() { + // TODO: pending aidl interface + + if (mDemux != NULL) { + sp<ITimeFilter> hidlTimeFilter = openHidlTimeFilter(); + if (hidlTimeFilter != NULL) { + sp<TimeFilterClient> timeFilterClient = new TimeFilterClient(); + timeFilterClient->setHidlTimeFilter(hidlTimeFilter); + return timeFilterClient; + } + } + + return NULL; +} + int DemuxClient::getAvSyncHwId(sp<FilterClient> filterClient) { // pending aidl interface @@ -188,6 +205,26 @@ sp<IFilter> DemuxClient::openHidlFilter(DemuxFilterType type, int bufferSize, return hidlFilter; } +sp<ITimeFilter> DemuxClient::openHidlTimeFilter() { + if (mDemux == NULL) { + return NULL; + } + + sp<ITimeFilter> timeFilter; + Result res; + mDemux->openTimeFilter( + [&](Result r, const sp<ITimeFilter>& timeFilterSp) { + timeFilter = timeFilterSp; + res = r; + }); + + if (res != Result::SUCCESS || timeFilter == NULL) { + return NULL; + } + + return timeFilter; +} + sp<IDvr> DemuxClient::openHidlDvr(DvrType dvrType, int bufferSize, sp<HidlDvrCallback> callback) { if (mDemux == NULL) { diff --git a/media/jni/tuner/DemuxClient.h b/media/jni/tuner/DemuxClient.h index f11f2c6cc22b..2950dd449a10 100644 --- a/media/jni/tuner/DemuxClient.h +++ b/media/jni/tuner/DemuxClient.h @@ -26,12 +26,14 @@ #include "FilterClient.h" #include "FilterClientCallback.h" #include "FrontendClient.h" +#include "TimeFilterClient.h" //using ::aidl::android::media::tv::tuner::ITunerDemux; using ::android::hardware::tv::tuner::V1_0::DemuxFilterType; using ::android::hardware::tv::tuner::V1_0::DvrType; using ::android::hardware::tv::tuner::V1_0::IDemux; +using ::android::hardware::tv::tuner::V1_0::ITimeFilter; using namespace std; @@ -56,7 +58,10 @@ public: */ sp<FilterClient> openFilter(DemuxFilterType type, int bufferSize, sp<FilterClientCallback> cb); - // TODO: handle TimeFilterClient + /** + * Open time filter of the demux. + */ + sp<TimeFilterClient> openTimeFilter(); /** * Get hardware sync ID for audio and video. @@ -88,8 +93,12 @@ public: */ Result close(); + void setId(int id) { mId = id; } + int getId() { return mId; } + private: sp<IFilter> openHidlFilter(DemuxFilterType type, int bufferSize, sp<HidlFilterCallback> cb); + sp<ITimeFilter> openHidlTimeFilter(); sp<IDvr> openHidlDvr(DvrType type, int bufferSize, sp<HidlDvrCallback> cb); /** @@ -105,6 +114,8 @@ private: * Default null when the HAL service does not exist. */ sp<IDemux> mDemux; + + int mId; }; } // namespace android diff --git a/media/jni/tuner/DescramblerClient.cpp b/media/jni/tuner/DescramblerClient.cpp new file mode 100644 index 000000000000..979beeac6b3a --- /dev/null +++ b/media/jni/tuner/DescramblerClient.cpp @@ -0,0 +1,98 @@ +/* + * Copyright 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. + */ + +#define LOG_TAG "DescramblerClient" + +#include <android-base/logging.h> +#include <utils/Log.h> + +#include "DescramblerClient.h" + +using ::android::hardware::tv::tuner::V1_0::Result; + +namespace android { + +/////////////// DescramblerClient /////////////////////// + +// TODO: pending aidl interface +DescramblerClient::DescramblerClient() { + //mTunerDescrambler = tunerDescrambler; +} + +DescramblerClient::~DescramblerClient() { + //mTunerDescrambler = NULL; + mDescrambler = NULL; +} + +// TODO: remove after migration to Tuner Service is done. +void DescramblerClient::setHidlDescrambler(sp<IDescrambler> descrambler) { + mDescrambler = descrambler; +} + +Result DescramblerClient::setDemuxSource(sp<DemuxClient> demuxClient) { + if (demuxClient == NULL) { + return Result::INVALID_ARGUMENT; + } + + // TODO: pending aidl interface + + if (mDescrambler != NULL) { + return mDescrambler->setDemuxSource(demuxClient->getId()); + } + + return Result::INVALID_STATE; +} + +Result DescramblerClient::setKeyToken(vector<uint8_t> keyToken) { + // TODO: pending aidl interface + + if (mDescrambler != NULL) { + return mDescrambler->setKeyToken(keyToken); + } + + return Result::INVALID_STATE; +} + +Result DescramblerClient::addPid(DemuxPid pid, sp<FilterClient> optionalSourceFilter) { + // TODO: pending aidl interface + + if (mDescrambler != NULL) { + return mDescrambler->addPid(pid, optionalSourceFilter->getHalFilter()); + } + + return Result::INVALID_STATE;} + +Result DescramblerClient::removePid(DemuxPid pid, sp<FilterClient> optionalSourceFilter) { + // TODO: pending aidl interface + + if (mDescrambler != NULL) { + return mDescrambler->addPid(pid, optionalSourceFilter->getHalFilter()); + } + + return Result::INVALID_STATE;} + +Result DescramblerClient::close() { + // TODO: pending aidl interface + + if (mDescrambler != NULL) { + return mDescrambler->close(); + } + + return Result::INVALID_STATE;} + +/////////////// DescramblerClient Helper Methods /////////////////////// + +} // namespace android diff --git a/media/jni/tuner/DescramblerClient.h b/media/jni/tuner/DescramblerClient.h new file mode 100644 index 000000000000..8af688314db1 --- /dev/null +++ b/media/jni/tuner/DescramblerClient.h @@ -0,0 +1,89 @@ +/* + * Copyright 2020 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. + */ + +#ifndef _ANDROID_MEDIA_TV_DESCRAMBLER_CLIENT_H_ +#define _ANDROID_MEDIA_TV_DESCRAMBLER_CLIENT_H_ + +//#include <aidl/android/media/tv/tuner/ITunerDescrambler.h> +#include <android/hardware/tv/tuner/1.0/IDescrambler.h> +#include <android/hardware/tv/tuner/1.1/types.h> + +#include "DemuxClient.h" +#include "FilterClient.h" + +//using ::aidl::android::media::tv::tuner::ITunerDescrambler; + +using ::android::hardware::tv::tuner::V1_0::IDescrambler; +using ::android::hardware::tv::tuner::V1_0::Result; +using ::android::hardware::tv::tuner::V1_0::DemuxPid; + +using namespace std; + +namespace android { + +struct DescramblerClient : public RefBase { + +public: + // TODO: pending hidl interface + DescramblerClient(); + ~DescramblerClient(); + + // TODO: remove after migration to Tuner Service is done. + void setHidlDescrambler(sp<IDescrambler> descrambler); + + /** + * Set a demux as source of the descrambler. + */ + Result setDemuxSource(sp<DemuxClient> demuxClient); + + /** + * Set a key token to link descrambler to a key slot. + */ + Result setKeyToken(vector<uint8_t> keyToken); + + /** + * Add packets' PID to the descrambler for descrambling. + */ + Result addPid(DemuxPid pid, sp<FilterClient> optionalSourceFilter); + + /** + * Remove packets' PID from the descrambler. + */ + Result removePid(DemuxPid pid, sp<FilterClient> optionalSourceFilter); + + /** + * Close a new interface of ITunerDescrambler. + */ + Result close(); + +private: + /** + * An AIDL Tuner Descrambler Singleton assigned at the first time the Tuner Client + * opens a descrambler. Default null when descrambler is not opened. + */ + // TODO: pending on aidl interface + //shared_ptr<ITunerDescrambler> mTunerDescrambler; + + /** + * A Descrambler HAL interface that is ready before migrating to the TunerDescrambler. + * This is a temprary interface before Tuner Framework migrates to use TunerService. + * Default null when the HAL service does not exist. + */ + sp<IDescrambler> mDescrambler; +}; +} // namespace android + +#endif // _ANDROID_MEDIA_TV_DESCRAMBLER_CLIENT_H_ diff --git a/media/jni/tuner/FrontendClient.cpp b/media/jni/tuner/FrontendClient.cpp index 44b46f0a0b47..14761a6f92a8 100644 --- a/media/jni/tuner/FrontendClient.cpp +++ b/media/jni/tuner/FrontendClient.cpp @@ -22,16 +22,17 @@ #include "FrontendClient.h" using ::aidl::android::media::tv::tuner::TunerFrontendSettings; +using ::android::hardware::tv::tuner::V1_1::Constant; namespace android { /////////////// FrontendClient /////////////////////// -FrontendClient::FrontendClient(shared_ptr<ITunerFrontend> tunerFrontend, int frontendHandle) { +FrontendClient::FrontendClient(shared_ptr<ITunerFrontend> tunerFrontend, int id) { mTunerFrontend = tunerFrontend; mAidlCallback = NULL; mHidlCallback = NULL; - mFrontendHandle = frontendHandle; + mId = id; } FrontendClient::~FrontendClient() { @@ -40,7 +41,7 @@ FrontendClient::~FrontendClient() { mFrontend_1_1 = NULL; mAidlCallback = NULL; mHidlCallback = NULL; - mFrontendHandle = -1; + mId = -1; } Result FrontendClient::setCallback(sp<FrontendClientCallback> frontendClientCallback) { @@ -99,6 +100,164 @@ Result FrontendClient::stopTune() { return Result::INVALID_STATE; } +Result FrontendClient::scan(const FrontendSettings& settings, FrontendScanType type, + const FrontendSettingsExt1_1& settingsExt1_1) { + if (mTunerFrontend != NULL) { + // TODO: parse hidl settings to aidl settings + // TODO: aidl frontend settings to include Tuner HAL 1.1 settings + TunerFrontendSettings settings; + // TODO: handle error message. + mTunerFrontend->scan(settings, (int)type); + return Result::SUCCESS; + } + + Result result; + if (mFrontend_1_1 != NULL) { + result = mFrontend_1_1->scan_1_1(settings, type, settingsExt1_1); + return result; + } + + if (mFrontend != NULL) { + result = mFrontend->scan(settings, type); + return result; + } + + return Result::INVALID_STATE; +} + +Result FrontendClient::stopScan() { + if (mTunerFrontend != NULL) { + // TODO: handle error message. + mTunerFrontend->stopScan(); + return Result::SUCCESS; + } + + if (mFrontend != NULL) { + Result result = mFrontend->stopScan(); + return result; + } + + return Result::INVALID_STATE; +} + +vector<FrontendStatus> FrontendClient::getStatus(vector<FrontendStatusType> statusTypes) { + vector<FrontendStatus> status; + + if (mTunerFrontend != NULL) { + // TODO: handle error message. + /*status = mTunerFrontend->getStatus(statusTypes); + return status;*/ + } + + if (mFrontend != NULL && statusTypes.size() > 0) { + Result res; + mFrontend->getStatus(statusTypes, + [&](Result r, const hidl_vec<FrontendStatus>& s) { + res = r; + status = s; + }); + if (res != Result::SUCCESS) { + status.clear(); + return status; + } + } + + return status; +} +vector<FrontendStatusExt1_1> FrontendClient::getStatusExtended_1_1( + vector<FrontendStatusTypeExt1_1> statusTypes) { + vector<FrontendStatusExt1_1> status; + + if (mTunerFrontend != NULL) { + // TODO: handle error message. + /*status = mTunerFrontend->getStatusExtended_1_1(statusTypes); + return status;*/ + } + + if (mFrontend_1_1 != NULL && statusTypes.size() > 0) { + Result res; + mFrontend_1_1->getStatusExt1_1(statusTypes, + [&](Result r, const hidl_vec<FrontendStatusExt1_1>& s) { + res = r; + status = s; + }); + if (res != Result::SUCCESS) { + status.clear(); + return status; + } + } + + return status; +} + +Result FrontendClient::setLnb(sp<LnbClient> lnbClient) { + if (mTunerFrontend != NULL) { + // TODO: handle error message. + /*mTunerFrontend->setLnb(lnbClient->getAidlLnb()); + return Result::SUCCESS;*/ + } + + if (mFrontend != NULL) { + Result result = mFrontend->setLnb(lnbClient->getId()); + return result; + } + + return Result::INVALID_STATE; +} + +Result FrontendClient::setLna(bool bEnable) { + if (mTunerFrontend != NULL) { + // TODO: handle error message. + /*mTunerFrontend->setLna(bEnable); + return Result::SUCCESS;*/ + } + + if (mFrontend != NULL) { + Result result = mFrontend->setLna(bEnable); + return result; + } + + return Result::INVALID_STATE; +} + +int FrontendClient::linkCiCamToFrontend(int ciCamId) { + int ltsId = (int)Constant::INVALID_LTS_ID; + + if (mTunerFrontend != NULL) { + // TODO: handle error message. + /*mTunerFrontend->linkCiCamToFrontend(ciCamId, ltsId); + return ltsId;*/ + } + + if (mFrontend_1_1 != NULL) { + Result res; + mFrontend_1_1->linkCiCam(static_cast<uint32_t>(ciCamId), + [&](Result r, uint32_t id) { + res = r; + ltsId = id; + }); + if (res != Result::SUCCESS) { + return (int)Constant::INVALID_LTS_ID; + } + } + + return ltsId; +} + +Result FrontendClient::unlinkCiCamToFrontend(int ciCamId) { + if (mTunerFrontend != NULL) { + // TODO: handle error message. + /*mTunerFrontend->unlinkCiCamToFrontend(ciCamId); + return Result::SUCCESS;*/ + } + + if (mFrontend_1_1 != NULL) { + return mFrontend_1_1->unlinkCiCam(static_cast<uint32_t>(ciCamId)); + } + + return Result::INVALID_STATE; +} + Result FrontendClient::close() { if (mTunerFrontend != NULL) { // TODO: handle error message. @@ -123,7 +282,7 @@ shared_ptr<ITunerFrontend> FrontendClient::getAidlFrontend() { } int FrontendClient::getId() { - return getResourceIdFromHandle(mFrontendHandle); + return mId; } /////////////// TunerFrontendCallback /////////////////////// diff --git a/media/jni/tuner/FrontendClient.h b/media/jni/tuner/FrontendClient.h index 7db572ba0987..32356744c839 100644 --- a/media/jni/tuner/FrontendClient.h +++ b/media/jni/tuner/FrontendClient.h @@ -24,6 +24,7 @@ #include <android/hardware/tv/tuner/1.1/types.h> #include "FrontendClientCallback.h" +#include "LnbClient.h" using Status = ::ndk::ScopedAStatus; @@ -37,13 +38,18 @@ using ::android::hardware::tv::tuner::V1_0::FrontendInfo; using ::android::hardware::tv::tuner::V1_0::FrontendEventType; using ::android::hardware::tv::tuner::V1_0::FrontendScanMessage; using ::android::hardware::tv::tuner::V1_0::FrontendScanMessageType; +using ::android::hardware::tv::tuner::V1_0::FrontendScanType; using ::android::hardware::tv::tuner::V1_0::FrontendSettings; +using ::android::hardware::tv::tuner::V1_0::FrontendStatus; +using ::android::hardware::tv::tuner::V1_0::FrontendStatusType; using ::android::hardware::tv::tuner::V1_0::IFrontend; using ::android::hardware::tv::tuner::V1_0::Result; using ::android::hardware::tv::tuner::V1_1::FrontendScanMessageExt1_1; using ::android::hardware::tv::tuner::V1_1::FrontendScanMessageTypeExt1_1; using ::android::hardware::tv::tuner::V1_1::FrontendSettingsExt1_1; +using ::android::hardware::tv::tuner::V1_1::FrontendStatusExt1_1; +using ::android::hardware::tv::tuner::V1_1::FrontendStatusTypeExt1_1; using ::android::hardware::tv::tuner::V1_1::IFrontendCallback; using namespace std; @@ -105,7 +111,7 @@ private: struct FrontendClient : public RefBase { public: - FrontendClient(shared_ptr<ITunerFrontend> tunerFrontend, int frontendHandle); + FrontendClient(shared_ptr<ITunerFrontend> tunerFrontend, int id); ~FrontendClient(); /** @@ -127,6 +133,50 @@ public: Result stopTune(); /** + * Scan the frontend to use the settings given. + */ + Result scan(const FrontendSettings& settings, FrontendScanType frontendScanType, + const FrontendSettingsExt1_1& settingsExt1_1); + + /** + * Stop the previous scanning. + */ + Result stopScan(); + + /** + * Gets the statuses of the frontend. + */ + vector<FrontendStatus> getStatus(vector<FrontendStatusType> statusTypes); + + /** + * Gets the 1.1 extended statuses of the frontend. + */ + vector<FrontendStatusExt1_1> getStatusExtended_1_1( + vector<FrontendStatusTypeExt1_1> statusTypes); + + /** + * Sets Low-Noise Block downconverter (LNB) for satellite frontend. + */ + Result setLnb(sp<LnbClient> lnbClient); + + /** + * Enable or Disable Low Noise Amplifier (LNA). + */ + Result setLna(bool bEnable); + + /** + * Link Frontend to the cicam with given id. + * + * @return lts id + */ + int linkCiCamToFrontend(int ciCamId); + + /** + * Unink Frontend to the cicam with given id. + */ + Result unlinkCiCamToFrontend(int ciCamId); + + /** * Close Frontend. */ Result close(); @@ -135,10 +185,6 @@ public: int getId(); - static int getResourceIdFromHandle(int handle) { - return (handle & 0x00ff0000) >> 16; - } - private: /** * An AIDL Tuner Frontend Singleton assigned at the first time when the Tuner Client @@ -163,7 +209,7 @@ private: shared_ptr<TunerFrontendCallback> mAidlCallback; sp<HidlFrontendCallback> mHidlCallback; - int mFrontendHandle; + int mId; }; } // namespace android diff --git a/media/jni/tuner/LnbClient.cpp b/media/jni/tuner/LnbClient.cpp new file mode 100644 index 000000000000..7f3916fe634d --- /dev/null +++ b/media/jni/tuner/LnbClient.cpp @@ -0,0 +1,130 @@ +/* + * Copyright 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. + */ + +#define LOG_TAG "LnbClient" + +#include <android-base/logging.h> +#include <utils/Log.h> + +#include "LnbClient.h" + +using ::android::hardware::tv::tuner::V1_0::Result; + +namespace android { + +/////////////// LnbClient /////////////////////// + +// TODO: pending aidl interface +LnbClient::LnbClient() { + //mTunerLnb = tunerLnb; + mId = -1; +} + +LnbClient::~LnbClient() { + //mTunerLnb = NULL; + mLnb = NULL; + mId = -1; +} + +// TODO: remove after migration to Tuner Service is done. +void LnbClient::setHidlLnb(sp<ILnb> lnb) { + mLnb = lnb; +} + +Result LnbClient::setCallback(sp<LnbClientCallback> cb) { + // TODO: pending aidl interface + /*if (mTunerFrontend != NULL) { + mAidlCallback = ::ndk::SharedRefBase::make<TunerLnbCallback>(cb); + mTunerLnb->setCallback(mAidlCallback); + return Result::SUCCESS; + }*/ + + mHidlCallback = new HidlLnbCallback(cb); + return mLnb->setCallback(mHidlCallback); +} + +Result LnbClient::setVoltage(LnbVoltage voltage) { + // TODO: pending aidl interface + + if (mLnb != NULL) { + return mLnb->setVoltage(voltage); + } + + return Result::INVALID_STATE; +} + +Result LnbClient::setTone(LnbTone tone) { + // TODO: pending aidl interface + + if (mLnb != NULL) { + return mLnb->setTone(tone); + } + + return Result::INVALID_STATE; +} + +Result LnbClient::setSatellitePosition(LnbPosition position) { + // TODO: pending aidl interface + + if (mLnb != NULL) { + return mLnb->setSatellitePosition(position); + } + + return Result::INVALID_STATE; +} + +Result LnbClient::sendDiseqcMessage(vector<uint8_t> diseqcMessage) { + // TODO: pending aidl interface + + if (mLnb != NULL) { + return mLnb->sendDiseqcMessage(diseqcMessage); + } + + return Result::INVALID_STATE; +} + +Result LnbClient::close() { + // TODO: pending aidl interface + + if (mLnb != NULL) { + return mLnb->close(); + } + + return Result::INVALID_STATE; +} + +/////////////// ILnbCallback /////////////////////// + +HidlLnbCallback::HidlLnbCallback(sp<LnbClientCallback> lnbClientCallback) + : mLnbClientCallback(lnbClientCallback) {} + +Return<void> HidlLnbCallback::onEvent(const LnbEventType lnbEventType) { + if (mLnbClientCallback != NULL) { + mLnbClientCallback->onEvent(lnbEventType); + } + return Void(); +} + +Return<void> HidlLnbCallback::onDiseqcMessage(const hidl_vec<uint8_t>& diseqcMessage) { + if (mLnbClientCallback != NULL) { + mLnbClientCallback->onDiseqcMessage(diseqcMessage); + } + return Void(); +} + +/////////////// LnbClient Helper Methods /////////////////////// + +} // namespace android diff --git a/media/jni/tuner/LnbClient.h b/media/jni/tuner/LnbClient.h new file mode 100644 index 000000000000..533a99678efe --- /dev/null +++ b/media/jni/tuner/LnbClient.h @@ -0,0 +1,134 @@ +/* + * Copyright 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. + */ + +#ifndef _ANDROID_MEDIA_TV_LNB_CLIENT_H_ +#define _ANDROID_MEDIA_TV_LNB_CLIENT_H_ + +//#include <aidl/android/media/tv/tuner/ITunerLnb.h> +#include <android/hardware/tv/tuner/1.0/ILnb.h> +#include <android/hardware/tv/tuner/1.0/ILnbCallback.h> +#include <android/hardware/tv/tuner/1.1/types.h> + +#include "LnbClientCallback.h" + +//using ::aidl::android::media::tv::tuner::ITunerLnb; + +using ::android::hardware::Return; +using ::android::hardware::Void; +using ::android::hardware::hidl_vec; +using ::android::hardware::tv::tuner::V1_0::ILnb; +using ::android::hardware::tv::tuner::V1_0::ILnbCallback; +using ::android::hardware::tv::tuner::V1_0::LnbId; +using ::android::hardware::tv::tuner::V1_0::LnbPosition; +using ::android::hardware::tv::tuner::V1_0::LnbTone; +using ::android::hardware::tv::tuner::V1_0::LnbVoltage; +using ::android::hardware::tv::tuner::V1_0::Result; + +using namespace std; + +namespace android { + +// TODO: pending aidl interface +/*class TunerLnbCallback : public BnTunerLnbCallback { + +public: + TunerLnbCallback(sp<LnbClientCallback> lnbClientCallback); + + Status onEvent(int lnbEventType); + Status onDiseqcMessage(vector<uint8_t> diseqcMessage); + +private: + sp<LnbClientCallback> mLnbClientCallback; +};*/ + +struct HidlLnbCallback : public ILnbCallback { + +public: + HidlLnbCallback(sp<LnbClientCallback> lnbClientCallback); + virtual Return<void> onEvent(const LnbEventType lnbEventType); + virtual Return<void> onDiseqcMessage(const hidl_vec<uint8_t>& diseqcMessage); + +private: + sp<LnbClientCallback> mLnbClientCallback; +}; + +struct LnbClient : public RefBase { + +public: + // TODO: add TunerLnb as parameter. + LnbClient(); + ~LnbClient(); + + // TODO: remove after migration to Tuner Service is done. + void setHidlLnb(sp<ILnb> lnb); + + /** + * Set the lnb callback. + */ + Result setCallback(sp<LnbClientCallback> cb); + + /** + * Set the lnb's power voltage. + */ + Result setVoltage(LnbVoltage voltage); + + /** + * Set the lnb's tone mode. + */ + Result setTone(LnbTone tone); + + /** + * Select the lnb's position. + */ + Result setSatellitePosition(LnbPosition position); + + /** + * Sends DiSEqC (Digital Satellite Equipment Control) message. + */ + Result sendDiseqcMessage(vector<uint8_t> diseqcMessage); + + /** + * Releases the LNB instance. + */ + Result close(); + + //shared_ptr<ITunerLnb> getAidlLnb() { return mTunerLnb; } + void setId(LnbId id) { mId = id; } + LnbId getId() { return mId; } + +private: + /** + * An AIDL Tuner Lnb Singleton assigned at the first time the Tuner Client + * opens an Lnb. Default null when lnb is not opened. + */ + // TODO: pending on aidl interface + //shared_ptr<ITunerLnb> mTunerLnb; + + /** + * A Lnb HAL interface that is ready before migrating to the TunerLnb. + * This is a temprary interface before Tuner Framework migrates to use TunerService. + * Default null when the HAL service does not exist. + */ + sp<ILnb> mLnb; + + //shared_ptr<TunerLnbCallback> mAidlCallback; + sp<HidlLnbCallback> mHidlCallback; + + LnbId mId; +}; +} // namespace android + +#endif // _ANDROID_MEDIA_TV_LNB_CLIENT_H_ diff --git a/media/jni/tuner/LnbClientCallback.h b/media/jni/tuner/LnbClientCallback.h new file mode 100644 index 000000000000..253d7ef110e3 --- /dev/null +++ b/media/jni/tuner/LnbClientCallback.h @@ -0,0 +1,33 @@ +/* + * Copyright 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. + */ + +#ifndef _ANDROID_MEDIA_TV_LNB_CLIENT_CALLBACK_H_ +#define _ANDROID_MEDIA_TV_LNB_CLIENT_CALLBACK_H_ + +using ::android::hardware::hidl_vec; +using ::android::hardware::tv::tuner::V1_0::LnbEventType; + +using namespace std; + +namespace android { + +struct LnbClientCallback : public RefBase { + virtual void onEvent(const LnbEventType lnbEventType); + virtual void onDiseqcMessage(const hidl_vec<uint8_t>& diseqcMessage); +}; +} // namespace android + +#endif // _ANDROID_MEDIA_TV_LNB_CLIENT_CALLBACK_H_
\ No newline at end of file diff --git a/media/jni/tuner/TimeFilterClient.cpp b/media/jni/tuner/TimeFilterClient.cpp new file mode 100644 index 000000000000..27ea6e596204 --- /dev/null +++ b/media/jni/tuner/TimeFilterClient.cpp @@ -0,0 +1,115 @@ +/* + * Copyright 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. + */ + +#define LOG_TAG "TimeFilterClient" + +#include <android-base/logging.h> +#include <utils/Log.h> + +#include "TimeFilterClient.h" + +using ::android::hardware::tv::tuner::V1_0::Result; +using ::android::hardware::tv::tuner::V1_1::Constant64Bit; + +namespace android { + +/////////////// TimeFilterClient /////////////////////// + +// TODO: pending aidl interface +TimeFilterClient::TimeFilterClient() { + //mTunerTimeFilter = tunerTimeFilter; +} + +TimeFilterClient::~TimeFilterClient() { + //mTunerTimeFilter = NULL; + mTimeFilter = NULL; +} + +// TODO: remove after migration to Tuner Service is done. +void TimeFilterClient::setHidlTimeFilter(sp<ITimeFilter> timeFilter) { + mTimeFilter = timeFilter; +} + +Result TimeFilterClient::setTimeStamp(long timeStamp) { + // TODO: pending aidl interface + + if (mTimeFilter != NULL) { + return mTimeFilter->setTimeStamp(timeStamp); + } + + return Result::INVALID_STATE; +} + +Result TimeFilterClient::clearTimeStamp() { + // TODO: pending aidl interface + + if (mTimeFilter != NULL) { + return mTimeFilter->clearTimeStamp(); + } + + return Result::INVALID_STATE; +} + +long TimeFilterClient::getTimeStamp() { + // TODO: pending aidl interface + + if (mTimeFilter != NULL) { + Result res; + long timestamp; + mTimeFilter->getTimeStamp( + [&](Result r, uint64_t t) { + res = r; + timestamp = t; + }); + if (res != Result::SUCCESS) { + return (long)Constant64Bit::INVALID_PRESENTATION_TIME_STAMP; + } + return timestamp; + } + + return (long)Constant64Bit::INVALID_PRESENTATION_TIME_STAMP; +} + +long TimeFilterClient::getSourceTime() { + // TODO: pending aidl interface + + if (mTimeFilter != NULL) { + Result res; + long timestamp; + mTimeFilter->getSourceTime( + [&](Result r, uint64_t t) { + res = r; + timestamp = t; + }); + if (res != Result::SUCCESS) { + return (long)Constant64Bit::INVALID_PRESENTATION_TIME_STAMP; + } + return timestamp; + } + + return (long)Constant64Bit::INVALID_PRESENTATION_TIME_STAMP; +} + +Result TimeFilterClient::close() { + // TODO: pending aidl interface + + if (mTimeFilter != NULL) { + return mTimeFilter->close(); + } + + return Result::INVALID_STATE; +} +} // namespace android diff --git a/media/jni/tuner/TimeFilterClient.h b/media/jni/tuner/TimeFilterClient.h new file mode 100644 index 000000000000..9a9d172665ec --- /dev/null +++ b/media/jni/tuner/TimeFilterClient.h @@ -0,0 +1,88 @@ +/* + * Copyright 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. + */ + +#ifndef _ANDROID_MEDIA_TV_TIME_FILTER_CLIENT_H_ +#define _ANDROID_MEDIA_TV_TIME_FILTER_CLIENT_H_ + +//#include <aidl/android/media/tv/tuner/ITunerTimeFilter.h> +#include <android/hardware/tv/tuner/1.0/ITimeFilter.h> +#include <android/hardware/tv/tuner/1.1/types.h> + +//using ::aidl::android::media::tv::tuner::ITunerTimeFilter; + +using ::android::hardware::Return; +using ::android::hardware::Void; +using ::android::hardware::hidl_vec; +using ::android::hardware::tv::tuner::V1_0::ITimeFilter; +using ::android::hardware::tv::tuner::V1_0::Result; + +using namespace std; + +namespace android { + +struct TimeFilterClient : public RefBase { + +public: + // TODO: add TunerTimeFilter as parameter. + TimeFilterClient(); + ~TimeFilterClient(); + + // TODO: remove after migration to Tuner Service is done. + void setHidlTimeFilter(sp<ITimeFilter> timeFilter); + + /** + * Set time stamp for time based filter. + */ + Result setTimeStamp(long timeStamp); + + /** + * Clear the time stamp in the time filter. + */ + Result clearTimeStamp(); + + /** + * Get the current time in the time filter. + */ + long getTimeStamp(); + + /** + * Get the time from the beginning of current data source. + */ + long getSourceTime(); + + /** + * Releases the Time Filter instance. + */ + Result close(); + +private: + /** + * An AIDL Tuner TimeFilter Singleton assigned at the first time the Tuner Client + * opens an TimeFilter. Default null when time filter is not opened. + */ + // TODO: pending on aidl interface + //shared_ptr<ITunerTimeFilter> mTunerTimeFilter; + + /** + * A TimeFilter HAL interface that is ready before migrating to the TunerTimeFilter. + * This is a temprary interface before Tuner Framework migrates to use TunerService. + * Default null when the HAL service does not exist. + */ + sp<ITimeFilter> mTimeFilter; +}; +} // namespace android + +#endif // _ANDROID_MEDIA_TV_TIME_FILTER_CLIENT_H_ diff --git a/media/jni/tuner/TunerClient.cpp b/media/jni/tuner/TunerClient.cpp index bd18c707f18b..39e6ba27e56f 100644 --- a/media/jni/tuner/TunerClient.cpp +++ b/media/jni/tuner/TunerClient.cpp @@ -25,6 +25,8 @@ using ::android::hardware::tv::tuner::V1_0::FrontendId; using ::android::hardware::tv::tuner::V1_0::FrontendType; +using ::aidl::android::media::tv::tunerresourcemanager::TunerFrontendInfo; + namespace android { sp<ITuner> TunerClient::mTuner; @@ -37,6 +39,7 @@ int TunerClient::mTunerVersion; TunerClient::TunerClient() { // Get HIDL Tuner in migration stage. getHidlTuner(); + updateTunerResources(); // Connect with Tuner Service. ::ndk::SpAIBinder binder(AServiceManager_getService("media.tuner")); mTunerService = ITunerService::fromBinder(binder); @@ -99,9 +102,10 @@ sp<FrontendClient> TunerClient::openFrontend(int frontendHandle) { } if (mTuner != NULL) { - sp<IFrontend> hidlFrontend = openHidlFrontendByHandle(frontendHandle); + int id = getResourceIdFromHandle(frontendHandle, FRONTEND); + sp<IFrontend> hidlFrontend = openHidlFrontendById(id); if (hidlFrontend != NULL) { - sp<FrontendClient> frontendClient = new FrontendClient(NULL, frontendHandle); + sp<FrontendClient> frontendClient = new FrontendClient(NULL, id); frontendClient->setHidlFrontend(hidlFrontend); return frontendClient; } @@ -160,9 +164,11 @@ sp<DemuxClient> TunerClient::openDemux(int /*demuxHandle*/) { if (mTuner != NULL) { // TODO: pending aidl interface sp<DemuxClient> demuxClient = new DemuxClient(); - sp<IDemux> hidlDemux = openHidlDemux(); + int demuxId; + sp<IDemux> hidlDemux = openHidlDemux(demuxId); if (hidlDemux != NULL) { demuxClient->setHidlDemux(hidlDemux); + demuxClient->setId(demuxId); return demuxClient; } } @@ -170,8 +176,135 @@ sp<DemuxClient> TunerClient::openDemux(int /*demuxHandle*/) { return NULL; } +shared_ptr<DemuxCapabilities> TunerClient::getDemuxCaps() { + // pending aidl interface + + if (mTuner != NULL) { + Result res; + DemuxCapabilities caps; + mTuner->getDemuxCaps([&](Result r, const DemuxCapabilities& demuxCaps) { + caps = demuxCaps; + res = r; + }); + if (res == Result::SUCCESS) { + return make_shared<DemuxCapabilities>(caps); + } + } + + return NULL; +} + +sp<DescramblerClient> TunerClient::openDescrambler(int /*descramblerHandle*/) { + if (mTunerService != NULL) { + // TODO: handle error code + /*shared_ptr<ITunerDescrambler> tunerDescrambler; + mTunerService->openDescrambler(demuxHandle, &tunerDescrambler); + return new DescramblerClient(tunerDescrambler);*/ + } + + if (mTuner != NULL) { + // TODO: pending aidl interface + sp<DescramblerClient> descramblerClient = new DescramblerClient(); + sp<IDescrambler> hidlDescrambler = openHidlDescrambler(); + if (hidlDescrambler != NULL) { + descramblerClient->setHidlDescrambler(hidlDescrambler); + return descramblerClient; + } + } + + return NULL;} + +sp<LnbClient> TunerClient::openLnb(int lnbHandle) { + if (mTunerService != NULL) { + // TODO: handle error code + /*shared_ptr<ITunerLnb> tunerLnb; + mTunerService->openLnb(demuxHandle, &tunerLnb); + return new LnbClient(tunerLnb);*/ + } + + if (mTuner != NULL) { + int id = getResourceIdFromHandle(lnbHandle, LNB); + // TODO: pending aidl interface + sp<LnbClient> lnbClient = new LnbClient(); + sp<ILnb> hidlLnb = openHidlLnbById(id); + if (hidlLnb != NULL) { + lnbClient->setHidlLnb(hidlLnb); + lnbClient->setId(id); + return lnbClient; + } + } + + return NULL; +} + +sp<LnbClient> TunerClient::openLnbByName(string lnbName) { + if (mTunerService != NULL) { + // TODO: handle error code + /*shared_ptr<ITunerLnb> tunerLnb; + mTunerService->openLnbByName(lnbName, &tunerLnb); + return new LnbClient(tunerLnb);*/ + } + + if (mTuner != NULL) { + // TODO: pending aidl interface + sp<LnbClient> lnbClient = new LnbClient(); + LnbId id; + sp<ILnb> hidlLnb = openHidlLnbByName(lnbName, id); + if (hidlLnb != NULL) { + lnbClient->setHidlLnb(hidlLnb); + lnbClient->setId(id); + return lnbClient; + } + } + + return NULL; +} + /////////////// TunerClient Helper Methods /////////////////////// +void TunerClient::updateTunerResources() { + if (mTuner == NULL) { + return; + } + + // Connect with Tuner Resource Manager. + ::ndk::SpAIBinder binder(AServiceManager_getService("tv_tuner_resource_mgr")); + mTunerResourceManager = ITunerResourceManager::fromBinder(binder); + + updateFrontendResources(); + updateLnbResources(); + // TODO: update Demux, Descrambler. +} + +void TunerClient::updateFrontendResources() { + vector<FrontendId> ids = getFrontendIds(); + if (ids.size() == 0) { + return; + } + vector<TunerFrontendInfo> infos; + for (int i = 0; i < ids.size(); i++) { + shared_ptr<FrontendInfo> frontendInfo = getFrontendInfo((int)ids[i]); + if (frontendInfo == NULL) { + continue; + } + TunerFrontendInfo tunerFrontendInfo{ + .handle = getResourceHandleFromId((int)ids[i], FRONTEND), + .frontendType = static_cast<int>(frontendInfo->type), + .exclusiveGroupId = static_cast<int>(frontendInfo->exclusiveGroupId), + }; + infos.push_back(tunerFrontendInfo); + } + mTunerResourceManager->setFrontendInfoList(infos); +} + +void TunerClient::updateLnbResources() { + vector<int> handles = getLnbHandles(); + if (handles.size() == 0) { + return; + } + mTunerResourceManager->setLnbInfoList(handles); +} + sp<ITuner> TunerClient::getHidlTuner() { if (mTuner == NULL) { mTunerVersion = 0; @@ -193,10 +326,9 @@ sp<ITuner> TunerClient::getHidlTuner() { return mTuner; } -sp<IFrontend> TunerClient::openHidlFrontendByHandle(int frontendHandle) { +sp<IFrontend> TunerClient::openHidlFrontendById(int id) { sp<IFrontend> fe; Result res; - uint32_t id = getResourceIdFromHandle(frontendHandle); mTuner->openFrontendById(id, [&](Result r, const sp<IFrontend>& frontend) { fe = frontend; res = r; @@ -217,12 +349,13 @@ Result TunerClient::getHidlFrontendInfo(int id, FrontendInfo& feInfo) { return res; } -sp<IDemux> TunerClient::openHidlDemux() { +sp<IDemux> TunerClient::openHidlDemux(int& demuxId) { sp<IDemux> demux; Result res; - mTuner->openDemux([&](Result result, uint32_t /*id*/, const sp<IDemux>& demuxSp) { + mTuner->openDemux([&](Result result, uint32_t id, const sp<IDemux>& demuxSp) { demux = demuxSp; + demuxId = id; res = result; }); if (res != Result::SUCCESS || demux == nullptr) { @@ -232,6 +365,79 @@ sp<IDemux> TunerClient::openHidlDemux() { return demux; } +sp<ILnb> TunerClient::openHidlLnbById(int id) { + sp<ILnb> lnb; + Result res; + + mTuner->openLnbById(id, [&](Result r, const sp<ILnb>& lnbSp) { + res = r; + lnb = lnbSp; + }); + if (res != Result::SUCCESS || lnb == nullptr) { + ALOGE("Failed to open lnb by id"); + return NULL; + } + return lnb; +} + +sp<ILnb> TunerClient::openHidlLnbByName(string name, LnbId& lnbId) { + sp<ILnb> lnb; + Result res; + + mTuner->openLnbByName(name, [&](Result r, LnbId id, const sp<ILnb>& lnbSp) { + res = r; + lnb = lnbSp; + lnbId = id; + }); + if (res != Result::SUCCESS || lnb == nullptr) { + ALOGE("Failed to open lnb by name"); + return NULL; + } + return lnb; +} + +sp<IDescrambler> TunerClient::openHidlDescrambler() { + sp<IDescrambler> descrambler; + Result res; + + mTuner->openDescrambler([&](Result r, const sp<IDescrambler>& descramblerSp) { + res = r; + descrambler = descramblerSp; + }); + + if (res != Result::SUCCESS || descrambler == NULL) { + return NULL; + } + + return descrambler; +} + +vector<int> TunerClient::getLnbHandles() { + vector<int> lnbHandles; + + if (mTunerService != NULL) { + // TODO: pending hidl interface + } + + if (mTuner != NULL) { + Result res; + vector<LnbId> lnbIds; + mTuner->getLnbIds([&](Result r, const hardware::hidl_vec<LnbId>& ids) { + lnbIds = ids; + res = r; + }); + if (res != Result::SUCCESS || lnbIds.size() == 0) { + ALOGW("Lnb isn't available"); + } else { + for (int i = 0; i < lnbIds.size(); i++) { + lnbHandles.push_back(getResourceHandleFromId((int)lnbIds[i], LNB)); + } + } + } + + return lnbHandles; +} + FrontendInfo TunerClient::FrontendInfoAidlToHidl(TunerServiceFrontendInfo aidlFrontendInfo) { FrontendInfo hidlFrontendInfo { .type = static_cast<FrontendType>(aidlFrontendInfo.type), @@ -246,4 +452,15 @@ FrontendInfo TunerClient::FrontendInfoAidlToHidl(TunerServiceFrontendInfo aidlFr return hidlFrontendInfo; } + +int TunerClient::getResourceIdFromHandle(int handle, int /*resourceType*/) { + return (handle & 0x00ff0000) >> 16; +} + +int TunerClient::getResourceHandleFromId(int id, int resourceType) { + // TODO: build up randomly generated id to handle mapping + return (resourceType & 0x000000ff) << 24 + | (id << 16) + | (mResourceRequestCount++ & 0xffff); +} } // namespace android diff --git a/media/jni/tuner/TunerClient.h b/media/jni/tuner/TunerClient.h index 197b1100acd6..a3d2d02c7ef6 100644 --- a/media/jni/tuner/TunerClient.h +++ b/media/jni/tuner/TunerClient.h @@ -17,19 +17,25 @@ #ifndef _ANDROID_MEDIA_TV_TUNER_CLIENT_H_ #define _ANDROID_MEDIA_TV_TUNER_CLIENT_H_ +#include <aidl/android/media/tv/tunerresourcemanager/ITunerResourceManager.h> +#include <aidl/android/media/tv/tunerresourcemanager/TunerFrontendInfo.h> #include <aidl/android/media/tv/tuner/ITunerService.h> #include <android/hardware/tv/tuner/1.1/ITuner.h> #include <android/hardware/tv/tuner/1.1/types.h> #include "FrontendClient.h" #include "DemuxClient.h" +#include "DescramblerClient.h" +#include "LnbClient.h" using ::aidl::android::media::tv::tuner::ITunerService; using ::aidl::android::media::tv::tuner::TunerServiceFrontendInfo; +using ::aidl::android::media::tv::tunerresourcemanager::ITunerResourceManager; using ::android::hardware::tv::tuner::V1_0::DemuxCapabilities; using ::android::hardware::tv::tuner::V1_0::FrontendId; using ::android::hardware::tv::tuner::V1_0::ITuner; +using ::android::hardware::tv::tuner::V1_0::LnbId; using ::android::hardware::tv::tuner::V1_0::Result; using ::android::hardware::tv::tuner::V1_1::FrontendDtmbCapabilities; @@ -37,6 +43,13 @@ using namespace std; namespace android { +typedef enum { + FRONTEND, + LNB, + DEMUX, + DESCRAMBLER, +} TunerResourceType; + struct TunerClient : public RefBase { public: @@ -87,7 +100,31 @@ public: * * @return the demux’s capabilities. */ - //DemuxCapabilities getDemuxCaps() {}; + shared_ptr<DemuxCapabilities> getDemuxCaps(); + + /** + * Open a new interface of DescramblerClient given a descramblerHandle. + * + * @param descramblerHandle the handle of the descrambler granted by TRM. + * @return a newly created DescramblerClient interface. + */ + sp<DescramblerClient> openDescrambler(int descramblerHandle); + + /** + * Open a new interface of LnbClient given an lnbHandle. + * + * @param lnbHandle the handle of the LNB granted by TRM. + * @return a newly created LnbClient interface. + */ + sp<LnbClient> openLnb(int lnbHandle); + + /** + * Open a new interface of LnbClient given a LNB name. + * + * @param lnbName the name for an external LNB to be opened. + * @return a newly created LnbClient interface. + */ + sp<LnbClient> openLnbByName(string lnbName); /** * Get the current Tuner HAL version. The high 16 bits are the major version number @@ -95,11 +132,24 @@ public: */ int getHalTunerVersion() { return mTunerVersion; } - static int getResourceIdFromHandle(int handle) { - return (handle & 0x00ff0000) >> 16; - } - private: + sp<ITuner> getHidlTuner(); + sp<IFrontend> openHidlFrontendById(int id); + sp<IDemux> openHidlDemux(int& demuxId); + Result getHidlFrontendInfo(int id, FrontendInfo& info); + sp<ILnb> openHidlLnbById(int id); + sp<ILnb> openHidlLnbByName(string name, LnbId& lnbId); + sp<IDescrambler> openHidlDescrambler(); + vector<int> getLnbHandles(); + FrontendInfo FrontendInfoAidlToHidl(TunerServiceFrontendInfo aidlFrontendInfo); + void updateTunerResources(); + void updateFrontendResources(); + void updateLnbResources(); + + int getResourceIdFromHandle(int handle, int resourceType); + + int getResourceHandleFromId(int id, int resourceType); + /** * An AIDL Tuner Service Singleton assigned at the first time the Tuner Client * connects with the Tuner Service. Default null when the service does not exist. @@ -124,11 +174,9 @@ private: // while the low 16 bits are the minor version. Default value is unknown version 0. static int mTunerVersion; - sp<ITuner> getHidlTuner(); - sp<IFrontend> openHidlFrontendByHandle(int frontendHandle); - sp<IDemux> openHidlDemux(); - Result getHidlFrontendInfo(int id, FrontendInfo& info); - FrontendInfo FrontendInfoAidlToHidl(TunerServiceFrontendInfo aidlFrontendInfo); + shared_ptr<ITunerResourceManager> mTunerResourceManager; + + int mResourceRequestCount = 0; }; } // namespace android diff --git a/packages/CompanionDeviceManager/res/values/strings.xml b/packages/CompanionDeviceManager/res/values/strings.xml index 1b96b0045ef0..731bdccb914f 100644 --- a/packages/CompanionDeviceManager/res/values/strings.xml +++ b/packages/CompanionDeviceManager/res/values/strings.xml @@ -25,11 +25,14 @@ <!-- The generic placeholder for a device type when nothing specific is known about it [CHAR LIMIT=30] --> <string name="profile_name_generic">device</string> + <!-- The name of the "watch" device type [CHAR LIMIT=30] --> + <string name="profile_name_watch">watch</string> + <!-- Title of the device association confirmation dialog. --> <string name="confirmation_title">Set <strong><xliff:g id="app_name" example="Android Wear">%1$s</xliff:g></strong> to manage your <xliff:g id="profile_name" example="watch">%2$s</xliff:g> - <strong><xliff:g id="device_name" example="ASUS ZenWatch 2">%3$s</xliff:g></strong></string> <!-- Text of the device profile permissions explanation in the association dialog. --> - <string name="profile_summary"><xliff:g id="app_name" example="Android Wear">%1$s</xliff:g> is needed to manage your <xliff:g id="profile_name" example="watch">%2$s</xliff:g>. <xliff:g id="app_name2" example="Android Wear">%3$s</xliff:g> will get access to <xliff:g id="permissions" example="Notifications, Calendar and Phone">%4$s</xliff:g> while the <xliff:g id="profile_name2" example="watch">%5$s</xliff:g> is connected.</string> + <string name="profile_summary"><xliff:g id="app_name" example="Android Wear">%1$s</xliff:g> is needed to manage your <xliff:g id="profile_name" example="watch">%2$s</xliff:g>. <xliff:g id="privileges_discplaimer" example="Android Wear will get access to your Notifications, Calendar and Contacts.">%3$s</xliff:g></string> <!-- Positive button for the device-app association consent dialog [CHAR LIMIT=30] --> <string name="consent_yes">Yes</string> diff --git a/packages/CompanionDeviceManager/src/com/android/companiondevicemanager/DeviceChooserActivity.java b/packages/CompanionDeviceManager/src/com/android/companiondevicemanager/DeviceChooserActivity.java index f42a51d6593a..620c7ae96ebf 100644 --- a/packages/CompanionDeviceManager/src/com/android/companiondevicemanager/DeviceChooserActivity.java +++ b/packages/CompanionDeviceManager/src/com/android/companiondevicemanager/DeviceChooserActivity.java @@ -17,11 +17,13 @@ package com.android.companiondevicemanager; import static android.companion.BluetoothDeviceFilterUtils.getDeviceMacAddress; +import static android.text.TextUtils.emptyIfNull; import static android.text.TextUtils.withoutPrefix; import static android.view.WindowManager.LayoutParams.SYSTEM_FLAG_HIDE_NON_SYSTEM_OVERLAY_WINDOWS; import static java.util.Objects.requireNonNull; +import android.annotation.Nullable; import android.app.Activity; import android.companion.AssociationRequest; import android.companion.CompanionDeviceManager; @@ -65,10 +67,7 @@ public class DeviceChooserActivity extends Activity { getWindow().addSystemFlags(SYSTEM_FLAG_HIDE_NON_SYSTEM_OVERLAY_WINDOWS); String deviceProfile = getRequest().getDeviceProfile(); - String profileName = deviceProfile == null - ? getString(R.string.profile_name_generic) - //TODO introduce PermissionController APIs to resolve UI values - : withoutPrefix("android.app.role.COMPANION_DEVICE_", deviceProfile).toLowerCase(); + String profileName = getDeviceProfileName(deviceProfile); if (getRequest().isSingleDevice()) { setContentView(R.layout.device_confirmation); @@ -112,15 +111,14 @@ public class DeviceChooserActivity extends Activity { TextView profileSummary = findViewById(R.id.profile_summary); if (deviceProfile != null) { - //TODO introduce PermissionController APIs to resolve UI values - String privileges = "Notifications, Phone, Contacts and Calendar"; + String privacyDisclaimer = emptyIfNull(getRequest() + .getDeviceProfilePrivilegesDescription()) + .replace("APP_NAME", getCallingAppName()); profileSummary.setVisibility(View.VISIBLE); profileSummary.setText(getString(R.string.profile_summary, getCallingAppName(), profileName, - getCallingAppName(), - privileges, - profileName)); + privacyDisclaimer)); } else { profileSummary.setVisibility(View.GONE); } @@ -135,6 +133,24 @@ public class DeviceChooserActivity extends Activity { return getService().mRequest; } + private String getDeviceProfileName(@Nullable String deviceProfile) { + if (deviceProfile == null) { + return getString(R.string.profile_name_generic); + } + switch (deviceProfile) { + case AssociationRequest.DEVICE_PROFILE_WATCH: { + return getString(R.string.profile_name_watch); + } + default: { + Log.wtf(LOG_TAG, + "No localized profile name found for device profile: " + deviceProfile); + return withoutPrefix("android.app.role.COMPANION_DEVICE_", deviceProfile) + .toLowerCase() + .replace('_', ' '); + } + } + } + private void cancel() { getService().onCancel(); setResult(RESULT_CANCELED); diff --git a/packages/SystemUI/AndroidManifest.xml b/packages/SystemUI/AndroidManifest.xml index a06bb931771f..e036d87f2492 100644 --- a/packages/SystemUI/AndroidManifest.xml +++ b/packages/SystemUI/AndroidManifest.xml @@ -78,6 +78,7 @@ <uses-permission android:name="android.permission.REQUEST_NETWORK_SCORES" /> <uses-permission android:name="android.permission.CONTROL_VPN" /> <uses-permission android:name="android.permission.PEERS_MAC_ADDRESS"/> + <uses-permission android:name="android.permission.READ_WIFI_CREDENTIAL"/> <!-- Physical hardware --> <uses-permission android:name="android.permission.MANAGE_USB" /> <uses-permission android:name="android.permission.CONTROL_DISPLAY_BRIGHTNESS" /> diff --git a/packages/SystemUI/res/layout/qs_paged_page_side_labels.xml b/packages/SystemUI/res/layout/qs_paged_page_side_labels.xml new file mode 100644 index 000000000000..c83077371bb0 --- /dev/null +++ b/packages/SystemUI/res/layout/qs_paged_page_side_labels.xml @@ -0,0 +1,23 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- + Copyright (C) 2020 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. +--> +<com.android.systemui.qs.SideLabelTileLayout + xmlns:android="http://schemas.android.com/apk/res/android" + android:id="@+id/tile_page" + android:layout_width="match_parent" + android:layout_height="match_parent" + android:clipChildren="false" + android:clipToPadding="false" /> diff --git a/packages/SystemUI/res/layout/qs_paged_tile_layout_side_labels.xml b/packages/SystemUI/res/layout/qs_paged_tile_layout_side_labels.xml new file mode 100644 index 000000000000..efa240362f67 --- /dev/null +++ b/packages/SystemUI/res/layout/qs_paged_tile_layout_side_labels.xml @@ -0,0 +1,27 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- + Copyright (C) 2020 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. +--> + +<com.android.systemui.qs.PagedTileLayout + xmlns:android="http://schemas.android.com/apk/res/android" + xmlns:systemui="http://schemas.android.com/apk/res-auto" + android:id="@+id/qs_pager" + android:layout_width="match_parent" + android:layout_height="0dp" + android:layout_weight="1" + android:clipChildren="true" + android:paddingBottom="@dimen/qs_paged_tile_layout_padding_bottom" + systemui:sideLabels="true" /> diff --git a/packages/SystemUI/res/layout/qs_tile_label.xml b/packages/SystemUI/res/layout/qs_tile_label.xml index 81d44cfa49dd..571cbbcc77db 100644 --- a/packages/SystemUI/res/layout/qs_tile_label.xml +++ b/packages/SystemUI/res/layout/qs_tile_label.xml @@ -34,7 +34,8 @@ <Space android:id="@+id/expand_space" android:layout_width="22dp" - android:layout_height="0dp" /> + android:layout_height="0dp" + android:visibility="gone" /> <TextView android:id="@+id/tile_label" diff --git a/packages/SystemUI/res/layout/qs_tile_label_divider.xml b/packages/SystemUI/res/layout/qs_tile_label_divider.xml new file mode 100644 index 000000000000..0d6460c22f0f --- /dev/null +++ b/packages/SystemUI/res/layout/qs_tile_label_divider.xml @@ -0,0 +1,27 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- + ~ Copyright (C) 2020 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. + --> + +<View xmlns:android="http://schemas.android.com/apk/res/android" + android:layout_width="1px" + android:layout_height="match_parent" + android:layout_gravity="center_vertical" + android:layout_marginBottom="10dp" + android:layout_marginTop="10dp" + android:layout_marginStart="0dp" + android:layout_marginEnd="0dp" + android:background="?android:attr/textColorSecondary" +/>
\ No newline at end of file diff --git a/packages/SystemUI/res/values/attrs.xml b/packages/SystemUI/res/values/attrs.xml index 897e3902b55c..4059b49ec486 100644 --- a/packages/SystemUI/res/values/attrs.xml +++ b/packages/SystemUI/res/values/attrs.xml @@ -167,5 +167,9 @@ <attr name="android:drawable" /> <attr name="android:alpha" /> </declare-styleable> + + <declare-styleable name="PagedTileLayout"> + <attr name="sideLabels" format="boolean"/> + </declare-styleable> </resources> diff --git a/packages/SystemUI/res/values/dimens.xml b/packages/SystemUI/res/values/dimens.xml index 8888ad6b0411..72dd72410af3 100644 --- a/packages/SystemUI/res/values/dimens.xml +++ b/packages/SystemUI/res/values/dimens.xml @@ -519,6 +519,7 @@ Scaled @dimen/qs_page_indicator-width by .4f. --> <dimen name="qs_page_indicator_dot_width">6.4dp</dimen> + <dimen name="qs_tile_side_label_padding">6dp</dimen> <dimen name="qs_tile_icon_size">24dp</dimen> <dimen name="qs_tile_text_size">12sp</dimen> <dimen name="qs_tile_divider_height">1dp</dimen> diff --git a/packages/SystemUI/src/com/android/systemui/qs/PageIndicator.java b/packages/SystemUI/src/com/android/systemui/qs/PageIndicator.java index 0053fea35262..e822dd58fb98 100644 --- a/packages/SystemUI/src/com/android/systemui/qs/PageIndicator.java +++ b/packages/SystemUI/src/com/android/systemui/qs/PageIndicator.java @@ -98,6 +98,7 @@ public class PageIndicator extends ViewGroup { } // Refresh state. setIndex(mPosition >> 1); + requestLayout(); } /** diff --git a/packages/SystemUI/src/com/android/systemui/qs/PagedTileLayout.java b/packages/SystemUI/src/com/android/systemui/qs/PagedTileLayout.java index 321f73295e2e..eaf212362320 100644 --- a/packages/SystemUI/src/com/android/systemui/qs/PagedTileLayout.java +++ b/packages/SystemUI/src/com/android/systemui/qs/PagedTileLayout.java @@ -8,6 +8,7 @@ import android.animation.PropertyValuesHolder; import android.content.Context; import android.content.res.Configuration; import android.content.res.Resources; +import android.content.res.TypedArray; import android.graphics.Rect; import android.os.Bundle; import android.util.AttributeSet; @@ -47,7 +48,7 @@ public class PagedTileLayout extends ViewPager implements QSTileLayout { }; private final ArrayList<TileRecord> mTiles = new ArrayList<>(); - private final ArrayList<TilePage> mPages = new ArrayList<>(); + private final ArrayList<TileLayout> mPages = new ArrayList<>(); private PageIndicator mPageIndicator; private float mPageIndicatorPosition; @@ -71,6 +72,7 @@ public class PagedTileLayout extends ViewPager implements QSTileLayout { private int mMaxColumns = TileLayout.NO_MAX_COLUMNS; private boolean mShowLabels = true; + private final boolean mSideLabels; public PagedTileLayout(Context context, AttributeSet attrs) { super(context, attrs); @@ -81,13 +83,18 @@ public class PagedTileLayout extends ViewPager implements QSTileLayout { mLayoutOrientation = getResources().getConfiguration().orientation; mLayoutDirection = getLayoutDirection(); mClippingRect = new Rect(); + + TypedArray t = context.getTheme().obtainStyledAttributes( + attrs, R.styleable.PagedTileLayout, 0, 0); + mSideLabels = t.getBoolean(R.styleable.PagedTileLayout_sideLabels, false); + t.recycle(); } private int mLastMaxHeight = -1; @Override public void setShowLabels(boolean show) { mShowLabels = show; - for (TilePage p : mPages) { + for (TileLayout p : mPages) { p.setShowLabels(show); } mDistributeTiles = true; @@ -145,7 +152,7 @@ public class PagedTileLayout extends ViewPager implements QSTileLayout { } // This will dump to the ui log all the tiles that are visible in this page - private void logVisibleTiles(TilePage page) { + private void logVisibleTiles(TileLayout page) { for (int i = 0; i < page.mRecords.size(); i++) { QSTile t = page.mRecords.get(i).tile; mUiEventLogger.logWithInstanceId(QSEvent.QS_TILE_VISIBLE, 0, t.getMetricsSpec(), @@ -161,7 +168,7 @@ public class PagedTileLayout extends ViewPager implements QSTileLayout { } private void updateListening() { - for (TilePage tilePage : mPages) { + for (TileLayout tilePage : mPages) { tilePage.setListening(tilePage.getParent() != null && mListening); } } @@ -222,13 +229,14 @@ public class PagedTileLayout extends ViewPager implements QSTileLayout { @Override protected void onFinishInflate() { super.onFinishInflate(); - mPages.add(createTilePage()); + mPages.add(createTileLayout()); mAdapter.notifyDataSetChanged(); } - private TilePage createTilePage() { - TilePage page = (TilePage) LayoutInflater.from(getContext()) - .inflate(R.layout.qs_paged_page, this, false); + private TileLayout createTileLayout() { + TileLayout page = (TileLayout) LayoutInflater.from(getContext()) + .inflate(mSideLabels ? R.layout.qs_paged_page_side_labels + : R.layout.qs_paged_page, this, false); page.setMinRows(mMinRows); page.setMaxColumns(mMaxColumns); page.setShowLabels(mShowLabels); @@ -283,7 +291,7 @@ public class PagedTileLayout extends ViewPager implements QSTileLayout { setImportantForAccessibility(View.IMPORTANT_FOR_ACCESSIBILITY_NO_HIDE_DESCENDANTS); int currentItem = getCurrentPageNumber(); for (int i = 0; i < mPages.size(); i++) { - TilePage page = mPages.get(i); + TileLayout page = mPages.get(i); page.setSelected(i == currentItem ? selected : false); if (page.isSelected()) { logVisibleTiles(page); @@ -325,7 +333,7 @@ public class PagedTileLayout extends ViewPager implements QSTileLayout { } while (mPages.size() < numPages) { if (DEBUG) Log.d(TAG, "Adding page"); - mPages.add(createTilePage()); + mPages.add(createTileLayout()); } while (mPages.size() > numPages) { if (DEBUG) Log.d(TAG, "Removing page"); @@ -422,7 +430,7 @@ public class PagedTileLayout extends ViewPager implements QSTileLayout { final int nRows = mPages.get(0).mRows; for (int i = 0; i < mPages.size(); i++) { - TilePage t = mPages.get(i); + TileLayout t = mPages.get(i); t.mRows = nRows; } } @@ -465,19 +473,19 @@ public class PagedTileLayout extends ViewPager implements QSTileLayout { public int getNumVisibleTiles() { if (mPages.size() == 0) return 0; - TilePage currentPage = mPages.get(getCurrentPageNumber()); + TileLayout currentPage = mPages.get(getCurrentPageNumber()); return currentPage.mRecords.size(); } public void startTileReveal(Set<String> tileSpecs, final Runnable postAnimation) { if (tileSpecs.isEmpty() || mPages.size() < 2 || getScrollX() != 0 || !beginFakeDrag()) { // Do not start the reveal animation unless there are tiles to animate, multiple - // TilePages available and the user has not already started dragging. + // TileLayouts available and the user has not already started dragging. return; } final int lastPageNumber = mPages.size() - 1; - final TilePage lastPage = mPages.get(lastPageNumber); + final TileLayout lastPage = mPages.get(lastPageNumber); final ArrayList<Animator> bounceAnims = new ArrayList<>(); for (TileRecord tr : lastPage.mRecords) { if (tileSpecs.contains(tr.tile.getTileSpec())) { @@ -557,12 +565,6 @@ public class PagedTileLayout extends ViewPager implements QSTileLayout { return mRecords.size() >= maxTiles(); } - public int maxTiles() { - // Each page should be able to hold at least one tile. If there's not enough room to - // show even 1 or there are no tiles, it probably means we are in the middle of setting - // up. - return Math.max(mColumns * mRows, 1); - } } private final PagerAdapter mAdapter = new PagerAdapter() { diff --git a/packages/SystemUI/src/com/android/systemui/qs/QSPanel.java b/packages/SystemUI/src/com/android/systemui/qs/QSPanel.java index 65f174c508e8..5eba147ab279 100644 --- a/packages/SystemUI/src/com/android/systemui/qs/QSPanel.java +++ b/packages/SystemUI/src/com/android/systemui/qs/QSPanel.java @@ -26,6 +26,7 @@ import android.content.res.Resources; import android.os.Bundle; import android.os.Handler; import android.os.Message; +import android.provider.Settings; import android.util.AttributeSet; import android.util.Pair; import android.view.Gravity; @@ -112,6 +113,7 @@ public class QSPanel extends LinearLayout implements Tunable { private int mMediaTotalBottomMargin; private int mFooterMarginStartHorizontal; private Consumer<Boolean> mMediaVisibilityChangedListener; + private final boolean mSideLabels; public QSPanel(Context context, AttributeSet attrs) { super(context, attrs); @@ -119,6 +121,8 @@ public class QSPanel extends LinearLayout implements Tunable { mMediaTotalBottomMargin = getResources().getDimensionPixelSize( R.dimen.quick_settings_bottom_margin_media); mContext = context; + mSideLabels = Settings.Secure.getInt( + mContext.getContentResolver(), "sysui_side_labels", 0) != 0; setOrientation(VERTICAL); @@ -174,8 +178,9 @@ public class QSPanel extends LinearLayout implements Tunable { /** */ public QSTileLayout createRegularTileLayout() { if (mRegularTileLayout == null) { - mRegularTileLayout = (QSTileLayout) LayoutInflater.from(mContext).inflate( - R.layout.qs_paged_tile_layout, this, false); + mRegularTileLayout = (QSTileLayout) LayoutInflater.from(mContext) + .inflate(mSideLabels ? R.layout.qs_paged_tile_layout_side_labels + : R.layout.qs_paged_tile_layout, this, false); } return mRegularTileLayout; } @@ -748,7 +753,13 @@ public class QSPanel extends LinearLayout implements Tunable { if (needsDynamicRowsAndColumns()) { newLayout.setMinRows(horizontal ? 2 : 1); // Let's use 3 columns to match the current layout - newLayout.setMaxColumns(horizontal ? 3 : TileLayout.NO_MAX_COLUMNS); + int columns; + if (mSideLabels) { + columns = horizontal ? 1 : 2; + } else { + columns = horizontal ? 3 : TileLayout.NO_MAX_COLUMNS; + } + newLayout.setMaxColumns(columns); } updateMargins(mediaHostView); } @@ -763,6 +774,10 @@ public class QSPanel extends LinearLayout implements Tunable { updatePadding(); } + boolean useSideLabels() { + return mSideLabels; + } + private class H extends Handler { private static final int SHOW_DETAIL = 1; private static final int SET_TILE_VISIBILITY = 2; diff --git a/packages/SystemUI/src/com/android/systemui/qs/QuickQSPanelController.java b/packages/SystemUI/src/com/android/systemui/qs/QuickQSPanelController.java index cca0e1b0c0f0..e2d7d201a5de 100644 --- a/packages/SystemUI/src/com/android/systemui/qs/QuickQSPanelController.java +++ b/packages/SystemUI/src/com/android/systemui/qs/QuickQSPanelController.java @@ -90,14 +90,26 @@ public class QuickQSPanelController extends QSPanelControllerBase<QuickQSPanel> @Override public void setTiles() { - List<QSTile> tiles = new ArrayList(); + List<QSTile> tiles = new ArrayList<>(); for (QSTile tile : mHost.getTiles()) { tiles.add(tile); if (tiles.size() == mView.getNumQuickTiles()) { break; } } - super.setTiles(tiles, true); + if (mView.useSideLabels()) { + List<QSTile> newTiles = new ArrayList<>(); + for (int i = 0; i < tiles.size(); i += 2) { + newTiles.add(tiles.get(i)); + } + for (int i = 1; i < tiles.size(); i += 2) { + newTiles.add(tiles.get(i)); + } + super.setTiles(newTiles, true); + + } else { + super.setTiles(tiles, true); + } } /** */ diff --git a/packages/SystemUI/src/com/android/systemui/qs/SideLabelTileLayout.kt b/packages/SystemUI/src/com/android/systemui/qs/SideLabelTileLayout.kt new file mode 100644 index 000000000000..74a7ac1cb6dd --- /dev/null +++ b/packages/SystemUI/src/com/android/systemui/qs/SideLabelTileLayout.kt @@ -0,0 +1,40 @@ +/* + * Copyright (C) 2020 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 + +import android.content.Context +import android.util.AttributeSet +import com.android.systemui.R + +open class SideLabelTileLayout(context: Context, attrs: AttributeSet) : TileLayout(context, attrs) { + + override fun updateResources(): Boolean { + return super.updateResources().also { + mResourceColumns = 2 + mMaxAllowedRows = 4 + mCellMarginHorizontal = (mCellMarginHorizontal * 1.2).toInt() + mCellMarginVertical = mCellMarginHorizontal + mMaxCellHeight = context.resources.getDimensionPixelSize(R.dimen.qs_quick_tile_size) + } + } + + override fun setShowLabels(show: Boolean) { } + + override fun isFull(): Boolean { + return mRecords.size >= maxTiles() + } +}
\ No newline at end of file diff --git a/packages/SystemUI/src/com/android/systemui/qs/TileLayout.java b/packages/SystemUI/src/com/android/systemui/qs/TileLayout.java index e38c931287b1..911261a01143 100644 --- a/packages/SystemUI/src/com/android/systemui/qs/TileLayout.java +++ b/packages/SystemUI/src/com/android/systemui/qs/TileLayout.java @@ -42,7 +42,7 @@ public class TileLayout extends ViewGroup implements QSTileLayout { private final boolean mLessRows; private int mMinRows = 1; private int mMaxColumns = NO_MAX_COLUMNS; - private int mResourceColumns; + protected int mResourceColumns; public TileLayout(Context context) { this(context, null); @@ -216,7 +216,7 @@ public class TileLayout extends ViewGroup implements QSTileLayout { return MeasureSpec.makeMeasureSpec(size, MeasureSpec.EXACTLY); } - private int getCellHeight() { + protected int getCellHeight() { return mShowLabels ? mMaxCellHeight : mMaxCellHeight / 2; } @@ -260,4 +260,18 @@ public class TileLayout extends ViewGroup implements QSTileLayout { public int getNumVisibleTiles() { return mRecords.size(); } + + public boolean isFull() { + return false; + } + + /** + * @return The maximum number of tiles this layout can hold + */ + public int maxTiles() { + // Each layout should be able to hold at least one tile. If there's not enough room to + // show even 1 or there are no tiles, it probably means we are in the middle of setting + // up. + return Math.max(mColumns * mRows, 1); + } } diff --git a/packages/SystemUI/src/com/android/systemui/qs/tileimpl/QSFactoryImpl.java b/packages/SystemUI/src/com/android/systemui/qs/tileimpl/QSFactoryImpl.java index e9d481b2e7f0..ba71fa6a8fb3 100644 --- a/packages/SystemUI/src/com/android/systemui/qs/tileimpl/QSFactoryImpl.java +++ b/packages/SystemUI/src/com/android/systemui/qs/tileimpl/QSFactoryImpl.java @@ -49,6 +49,7 @@ import com.android.systemui.qs.tiles.UserTile; import com.android.systemui.qs.tiles.WifiTile; import com.android.systemui.qs.tiles.WorkModeTile; import com.android.systemui.util.leak.GarbageMonitor; +import com.android.systemui.util.settings.SecureSettings; import javax.inject.Inject; import javax.inject.Provider; @@ -86,9 +87,12 @@ public class QSFactoryImpl implements QSFactory { private final Lazy<QSHost> mQsHostLazy; private final Provider<CustomTile.Builder> mCustomTileBuilderProvider; + private final boolean mSideLabels; + @Inject public QSFactoryImpl( Lazy<QSHost> qsHostLazy, + SecureSettings settings, Provider<CustomTile.Builder> customTileBuilderProvider, Provider<WifiTile> wifiTileProvider, Provider<InternetTile> internetTileProvider, @@ -115,6 +119,8 @@ public class QSFactoryImpl implements QSFactory { mQsHostLazy = qsHostLazy; mCustomTileBuilderProvider = customTileBuilderProvider; + mSideLabels = settings.getInt("sysui_side_labels", 0) != 0; + mWifiTileProvider = wifiTileProvider; mInternetTileProvider = internetTileProvider; mBluetoothTileProvider = bluetoothTileProvider; @@ -218,6 +224,8 @@ public class QSFactoryImpl implements QSFactory { QSIconView icon = tile.createTileView(context); if (collapsedView) { return new QSTileBaseView(context, icon, collapsedView); + } else if (mSideLabels) { + return new QSTileViewHorizontal(context, icon); } else { return new com.android.systemui.qs.tileimpl.QSTileView(context, icon); } diff --git a/packages/SystemUI/src/com/android/systemui/qs/tileimpl/QSTileBaseView.java b/packages/SystemUI/src/com/android/systemui/qs/tileimpl/QSTileBaseView.java index 655e4e2684e4..38e2ba4df79a 100644 --- a/packages/SystemUI/src/com/android/systemui/qs/tileimpl/QSTileBaseView.java +++ b/packages/SystemUI/src/com/android/systemui/qs/tileimpl/QSTileBaseView.java @@ -61,15 +61,15 @@ public class QSTileBaseView extends com.android.systemui.plugins.qs.QSTileView { private final FrameLayout mIconFrame; protected QSIconView mIcon; protected RippleDrawable mRipple; - private Drawable mTileBackground; + protected Drawable mTileBackground; private String mAccessibilityClass; private boolean mTileState; private boolean mCollapsedView; - private boolean mShowRippleEffect = true; + protected boolean mShowRippleEffect = true; private float mStrokeWidthActive; private float mStrokeWidthInactive; - private final ImageView mBg; + protected final ImageView mBg; private final int mColorActive; private final int mColorInactive; private final int mColorDisabled; @@ -162,7 +162,7 @@ public class QSTileBaseView extends com.android.systemui.plugins.qs.QSTileView { } } - private void updateRippleSize() { + protected void updateRippleSize() { // center the touch feedback on the center of the icon, and dial it down a bit final int cx = mIconFrame.getMeasuredWidth() / 2 + mIconFrame.getLeft(); final int cy = mIconFrame.getMeasuredHeight() / 2 + mIconFrame.getTop(); @@ -311,7 +311,7 @@ public class QSTileBaseView extends com.android.systemui.plugins.qs.QSTileView { return mLocInScreen[1] >= -getHeight(); } - private int getCircleColor(int state) { + protected int getCircleColor(int state) { switch (state) { case Tile.STATE_ACTIVE: return mColorActive; diff --git a/packages/SystemUI/src/com/android/systemui/qs/tileimpl/QSTileView.java b/packages/SystemUI/src/com/android/systemui/qs/tileimpl/QSTileView.java index 650206672c1e..2dbd2cfe9c10 100644 --- a/packages/SystemUI/src/com/android/systemui/qs/tileimpl/QSTileView.java +++ b/packages/SystemUI/src/com/android/systemui/qs/tileimpl/QSTileView.java @@ -37,7 +37,6 @@ import java.util.Objects; /** View that represents a standard quick settings tile. **/ public class QSTileView extends QSTileBaseView { private static final int MAX_LABEL_LINES = 2; - private static final boolean DUAL_TARGET_ALLOWED = false; private View mDivider; protected TextView mLabel; protected TextView mSecondLine; @@ -46,8 +45,10 @@ public class QSTileView extends QSTileBaseView { protected ViewGroup mLabelContainer; private View mExpandIndicator; private View mExpandSpace; - private ColorStateList mColorLabelDefault; + protected ColorStateList mColorLabelActive; + protected ColorStateList mColorLabelInactive; private ColorStateList mColorLabelUnavailable; + protected boolean mDualTargetAllowed = false; public QSTileView(Context context, QSIconView icon) { this(context, icon, false); @@ -64,7 +65,8 @@ public class QSTileView extends QSTileBaseView { createLabel(); setOrientation(VERTICAL); setGravity(Gravity.CENTER_HORIZONTAL | Gravity.TOP); - mColorLabelDefault = Utils.getColorAttr(getContext(), android.R.attr.textColorPrimary); + mColorLabelActive = Utils.getColorAttr(getContext(), android.R.attr.textColorPrimary); + mColorLabelInactive = mColorLabelActive; // The text color for unavailable tiles is textColorSecondary, same as secondaryLabel for // contrast purposes mColorLabelUnavailable = Utils.getColorAttr(getContext(), @@ -118,8 +120,15 @@ public class QSTileView extends QSTileBaseView { protected void handleStateChanged(QSTile.State state) { super.handleStateChanged(state); if (!Objects.equals(mLabel.getText(), state.label) || mState != state.state) { - mLabel.setTextColor(state.state == Tile.STATE_UNAVAILABLE ? mColorLabelUnavailable - : mColorLabelDefault); + ColorStateList labelColor; + if (state.state == Tile.STATE_ACTIVE) { + labelColor = mColorLabelActive; + } else if (state.state == Tile.STATE_INACTIVE) { + labelColor = mColorLabelInactive; + } else { + labelColor = mColorLabelUnavailable; + } + mLabel.setTextColor(labelColor); mState = state.state; mLabel.setText(state.label); } @@ -128,9 +137,8 @@ public class QSTileView extends QSTileBaseView { mSecondLine.setVisibility(TextUtils.isEmpty(state.secondaryLabel) ? View.GONE : View.VISIBLE); } - boolean dualTarget = DUAL_TARGET_ALLOWED && state.dualTarget; - mExpandIndicator.setVisibility(dualTarget ? View.VISIBLE : View.GONE); - mExpandSpace.setVisibility(dualTarget ? View.VISIBLE : View.GONE); + boolean dualTarget = mDualTargetAllowed && state.dualTarget; + handleExpand(dualTarget); mLabelContainer.setContentDescription(dualTarget ? state.dualLabelContentDescription : null); if (dualTarget != mLabelContainer.isClickable()) { @@ -142,6 +150,11 @@ public class QSTileView extends QSTileBaseView { mPadLock.setVisibility(state.disabledByPolicy ? View.VISIBLE : View.GONE); } + protected void handleExpand(boolean dualTarget) { + mExpandIndicator.setVisibility(dualTarget ? View.VISIBLE : View.GONE); + mExpandSpace.setVisibility(dualTarget ? View.VISIBLE : View.GONE); + } + @Override public void init(OnClickListener click, OnClickListener secondaryClick, OnLongClickListener longClick) { diff --git a/packages/SystemUI/src/com/android/systemui/qs/tileimpl/QSTileViewHorizontal.kt b/packages/SystemUI/src/com/android/systemui/qs/tileimpl/QSTileViewHorizontal.kt new file mode 100644 index 000000000000..2ef78c249246 --- /dev/null +++ b/packages/SystemUI/src/com/android/systemui/qs/tileimpl/QSTileViewHorizontal.kt @@ -0,0 +1,116 @@ +/* + * Copyright (C) 2020 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.tileimpl + +import android.content.Context +import android.content.res.ColorStateList +import android.graphics.Color +import android.graphics.drawable.Drawable +import android.graphics.drawable.PaintDrawable +import android.graphics.drawable.RippleDrawable +import android.service.quicksettings.Tile.STATE_ACTIVE +import android.view.Gravity +import android.view.LayoutInflater +import android.view.View +import android.widget.LinearLayout +import com.android.systemui.R +import com.android.systemui.plugins.qs.QSIconView +import com.android.systemui.plugins.qs.QSTile +import com.android.systemui.qs.tileimpl.QSTileImpl.getColorForState + +class QSTileViewHorizontal( + context: Context, + icon: QSIconView +) : QSTileView(context, icon, false) { + + private var paintDrawable: PaintDrawable? = null + private var divider: View? = null + + init { + orientation = HORIZONTAL + mDualTargetAllowed = true + mBg.setImageDrawable(null) + createDivider() + mColorLabelActive = ColorStateList.valueOf(getColorForState(getContext(), STATE_ACTIVE)) + } + + override fun createLabel() { + super.createLabel() + findViewById<LinearLayout>(R.id.label_group)?.gravity = Gravity.START + mLabel.gravity = Gravity.START + mSecondLine.gravity = Gravity.START + val padding = context.resources.getDimensionPixelSize(R.dimen.qs_tile_side_label_padding) + mLabelContainer.setPadding(padding, padding, padding, padding) + (mLabelContainer.layoutParams as LayoutParams).gravity = Gravity.CENTER_VERTICAL + } + + fun createDivider() { + divider = LayoutInflater.from(context).inflate(R.layout.qs_tile_label_divider, this, false) + val position = indexOfChild(mLabelContainer) + addView(divider, position) + } + + override fun init( + click: OnClickListener?, + secondaryClick: OnClickListener?, + longClick: OnLongClickListener? + ) { + super.init(click, secondaryClick, longClick) + mLabelContainer.setOnClickListener { + longClick?.onLongClick(it) + } + mLabelContainer.isClickable = false + } + + override fun updateRippleSize() { + } + + override fun newTileBackground(): Drawable? { + val d = super.newTileBackground() + if (paintDrawable == null) { + paintDrawable = PaintDrawable(Color.WHITE).apply { + setCornerRadius(30f) + } + } + if (d is RippleDrawable) { + d.addLayer(paintDrawable) + return d + } else { + return paintDrawable + } + } + + override fun setClickable(clickable: Boolean) { + super.setClickable(clickable) + background = mTileBackground + if (clickable && mShowRippleEffect) { + mRipple?.setHotspotBounds(left, top, right, bottom) + } else { + mRipple?.setHotspotBounds(0, 0, 0, 0) + } + } + + override fun handleStateChanged(state: QSTile.State) { + super.handleStateChanged(state) + paintDrawable?.setTint(getCircleColor(state.state)) + mSecondLine.setTextColor(mLabel.textColors) + mLabelContainer.background = null + divider?.backgroundTintList = mLabel.textColors + } + + override fun handleExpand(dualTarget: Boolean) {} +}
\ No newline at end of file diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/WifiTile.java b/packages/SystemUI/src/com/android/systemui/qs/tiles/WifiTile.java index 4d89dea7cb70..19eac77136c6 100644 --- a/packages/SystemUI/src/com/android/systemui/qs/tiles/WifiTile.java +++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/WifiTile.java @@ -54,6 +54,7 @@ import com.android.systemui.statusbar.policy.NetworkController.AccessPointContro import com.android.systemui.statusbar.policy.NetworkController.IconState; import com.android.systemui.statusbar.policy.NetworkController.SignalCallback; import com.android.systemui.statusbar.policy.WifiIcons; +import com.android.wifitrackerlib.WifiEntry; import java.util.List; @@ -80,12 +81,13 @@ public class WifiTile extends QSTileImpl<SignalState> { StatusBarStateController statusBarStateController, ActivityStarter activityStarter, QSLogger qsLogger, - NetworkController networkController + NetworkController networkController, + AccessPointController accessPointController ) { super(host, backgroundLooper, mainHandler, metricsLogger, statusBarStateController, activityStarter, qsLogger); mController = networkController; - mWifiController = mController.getAccessPointController(); + mWifiController = accessPointController; mDetailAdapter = (WifiDetailAdapter) createDetailAdapter(); mController.observe(getLifecycle(), mSignalCallback); } @@ -325,7 +327,7 @@ public class WifiTile extends QSTileImpl<SignalState> { NetworkController.AccessPointController.AccessPointCallback, QSDetailItems.Callback { private QSDetailItems mItems; - private AccessPoint[] mAccessPoints; + private WifiEntry[] mAccessPoints; @Override public CharSequence getTitle() { @@ -366,8 +368,8 @@ public class WifiTile extends QSTileImpl<SignalState> { } @Override - public void onAccessPointsChanged(final List<AccessPoint> accessPoints) { - mAccessPoints = accessPoints.toArray(new AccessPoint[accessPoints.size()]); + public void onAccessPointsChanged(final List<WifiEntry> accessPoints) { + mAccessPoints = accessPoints.toArray(new WifiEntry[accessPoints.size()]); filterUnreachableAPs(); updateItems(); @@ -376,15 +378,15 @@ public class WifiTile extends QSTileImpl<SignalState> { /** Filter unreachable APs from mAccessPoints */ private void filterUnreachableAPs() { int numReachable = 0; - for (AccessPoint ap : mAccessPoints) { - if (ap.isReachable()) numReachable++; + for (WifiEntry ap : mAccessPoints) { + if (isWifiEntryReachable(ap)) numReachable++; } if (numReachable != mAccessPoints.length) { - AccessPoint[] unfiltered = mAccessPoints; - mAccessPoints = new AccessPoint[numReachable]; + WifiEntry[] unfiltered = mAccessPoints; + mAccessPoints = new WifiEntry[numReachable]; int i = 0; - for (AccessPoint ap : unfiltered) { - if (ap.isReachable()) mAccessPoints[i++] = ap; + for (WifiEntry ap : unfiltered) { + if (isWifiEntryReachable(ap)) mAccessPoints[i++] = ap; } } } @@ -397,8 +399,8 @@ public class WifiTile extends QSTileImpl<SignalState> { @Override public void onDetailItemClick(Item item) { if (item == null || item.tag == null) return; - final AccessPoint ap = (AccessPoint) item.tag; - if (!ap.isActive()) { + final WifiEntry ap = (WifiEntry) item.tag; + if (ap.getConnectedState() == WifiEntry.CONNECTED_STATE_DISCONNECTED) { if (mWifiController.connect(ap)) { mHost.collapsePanels(); } @@ -442,12 +444,12 @@ public class WifiTile extends QSTileImpl<SignalState> { if (mAccessPoints != null) { items = new Item[mAccessPoints.length]; for (int i = 0; i < mAccessPoints.length; i++) { - final AccessPoint ap = mAccessPoints[i]; + final WifiEntry ap = mAccessPoints[i]; final Item item = new Item(); item.tag = ap; item.iconResId = mWifiController.getIcon(ap); item.line1 = ap.getSsid(); - item.line2 = ap.isActive() ? ap.getSummary() : null; + item.line2 = ap.getSummary(); item.icon2 = ap.getSecurity() != AccessPoint.SECURITY_NONE ? R.drawable.qs_ic_wifi_lock : -1; @@ -457,4 +459,8 @@ public class WifiTile extends QSTileImpl<SignalState> { mItems.setItems(items); } } + + private static boolean isWifiEntryReachable(WifiEntry ap) { + return ap.getLevel() != WifiEntry.WIFI_LEVEL_UNREACHABLE; + } } diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/FeatureFlags.java b/packages/SystemUI/src/com/android/systemui/statusbar/FeatureFlags.java index 3811ca929f6e..bbc4b780bc52 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/FeatureFlags.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/FeatureFlags.java @@ -50,18 +50,26 @@ public class FeatureFlags { @Inject public FeatureFlags(@Background Executor executor) { DeviceConfig.addOnPropertiesChangedListener( - "systemui", + /* namespace= */ "systemui", executor, this::onPropertiesChanged); } public boolean isNewNotifPipelineEnabled() { - return getDeviceConfigFlag("notification.newpipeline.enabled", true); + return getDeviceConfigFlag("notification.newpipeline.enabled", /* defaultValue= */ true); } public boolean isNewNotifPipelineRenderingEnabled() { return isNewNotifPipelineEnabled() - && getDeviceConfigFlag("notification.newpipeline.rendering", false); + && getDeviceConfigFlag("notification.newpipeline.rendering", /* defaultValue= */ + false); + } + + /** + * Flag used for guarding development of b/171917882. + */ + public boolean isTwoColumnNotificationShadeEnabled() { + return getDeviceConfigFlag("notification.twocolumn", /* defaultValue= */ false); } private void onPropertiesChanged(@NonNull DeviceConfig.Properties properties) { @@ -76,7 +84,7 @@ public class FeatureFlags { synchronized (mCachedDeviceConfigFlags) { Boolean flag = mCachedDeviceConfigFlags.get(key); if (flag == null) { - flag = DeviceConfig.getBoolean("systemui", key, defaultValue); + flag = DeviceConfig.getBoolean(/* namespace= */ "systemui", key, defaultValue); mCachedDeviceConfigFlags.put(key, flag); } return flag; diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationMediaManager.java b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationMediaManager.java index bb76ac0b26bd..ca3923f06a13 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationMediaManager.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationMediaManager.java @@ -504,7 +504,7 @@ public class NotificationMediaManager implements Dumpable { // TODO: Should this really be for all users? It appears that inactive users // can't have active sessions, which would mean it is fine. final List<MediaController> sessions = - mMediaSessionManager.getActiveSessionsForUser(null, UserHandle.USER_ALL); + mMediaSessionManager.getActiveSessionsForUser(null, UserHandle.ALL); for (MediaController aController : sessions) { // now to see if we have one like this diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/ScrimController.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/ScrimController.java index f8e361fb5b6c..ab96a6d26dfd 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/ScrimController.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/ScrimController.java @@ -1003,6 +1003,7 @@ public class ScrimController implements ViewTreeObserver.OnPreDrawListener, Dump } private void updateThemeColors() { + if (mScrimBehind == null) return; int background = Utils.getColorAttr(mScrimBehind.getContext(), android.R.attr.colorBackgroundFloating).getDefaultColor(); int accent = Utils.getColorAccent(mScrimBehind.getContext()).getDefaultColor(); diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/AccessPointControllerImpl.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/AccessPointControllerImpl.java index 53d02280a03b..ab58286859cc 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/AccessPointControllerImpl.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/AccessPointControllerImpl.java @@ -16,25 +16,47 @@ package com.android.systemui.statusbar.policy; -import android.app.ActivityManager; import android.content.Context; import android.content.Intent; -import android.net.wifi.WifiManager.ActionListener; +import android.net.ConnectivityManager; +import android.net.NetworkScoreManager; +import android.net.wifi.WifiManager; +import android.os.Handler; +import android.os.SimpleClock; +import android.os.SystemClock; import android.os.UserHandle; import android.os.UserManager; import android.provider.Settings; +import android.util.IndentingPrintWriter; import android.util.Log; -import com.android.settingslib.wifi.AccessPoint; -import com.android.settingslib.wifi.WifiTracker; -import com.android.settingslib.wifi.WifiTracker.WifiListener; +import androidx.annotation.NonNull; +import androidx.annotation.Nullable; +import androidx.lifecycle.Lifecycle; +import androidx.lifecycle.LifecycleOwner; +import androidx.lifecycle.LifecycleRegistry; + +import com.android.systemui.dagger.SysUISingleton; +import com.android.systemui.dagger.qualifiers.Background; +import com.android.systemui.dagger.qualifiers.Main; +import com.android.systemui.settings.UserTracker; +import com.android.wifitrackerlib.WifiEntry; +import com.android.wifitrackerlib.WifiPickerTracker; import java.io.PrintWriter; +import java.time.Clock; +import java.time.ZoneOffset; import java.util.ArrayList; +import java.util.Arrays; +import java.util.Collections; import java.util.List; +import java.util.concurrent.Executor; + +import javax.inject.Inject; public class AccessPointControllerImpl - implements NetworkController.AccessPointController, WifiListener { + implements NetworkController.AccessPointController, + WifiPickerTracker.WifiPickerTrackerCallback, LifecycleOwner { private static final String TAG = "AccessPointController"; private static final boolean DEBUG = Log.isLoggable(TAG, Log.DEBUG); @@ -44,24 +66,51 @@ public class AccessPointControllerImpl private static final int[] ICONS = WifiIcons.WIFI_FULL_ICONS; - private final Context mContext; private final ArrayList<AccessPointCallback> mCallbacks = new ArrayList<AccessPointCallback>(); - private final WifiTracker mWifiTracker; private final UserManager mUserManager; + private final Executor mMainExecutor; + + private @Nullable WifiPickerTracker mWifiPickerTracker; + private WifiPickerTrackerFactory mWifiPickerTrackerFactory; + + private final LifecycleRegistry mLifecycle = new LifecycleRegistry(this); private int mCurrentUser; - public AccessPointControllerImpl(Context context) { - mContext = context; - mUserManager = (UserManager) mContext.getSystemService(Context.USER_SERVICE); - mWifiTracker = new WifiTracker(context, this, false, true); - mCurrentUser = ActivityManager.getCurrentUser(); + public AccessPointControllerImpl( + UserManager userManager, + UserTracker userTracker, + Executor mainExecutor, + WifiPickerTrackerFactory wifiPickerTrackerFactory + ) { + mUserManager = userManager; + mCurrentUser = userTracker.getUserId(); + mMainExecutor = mainExecutor; + mWifiPickerTrackerFactory = wifiPickerTrackerFactory; + mMainExecutor.execute(() -> mLifecycle.setCurrentState(Lifecycle.State.CREATED)); + } + + /** + * Initializes the controller. + * + * Will create a WifiPickerTracker associated to this controller. + */ + public void init() { + if (mWifiPickerTracker == null) { + mWifiPickerTracker = mWifiPickerTrackerFactory.create(this.getLifecycle(), this); + } + } + + @NonNull + @Override + public Lifecycle getLifecycle() { + return mLifecycle; } @Override protected void finalize() throws Throwable { + mMainExecutor.execute(() -> mLifecycle.setCurrentState(Lifecycle.State.DESTROYED)); super.finalize(); - mWifiTracker.onDestroy(); } public boolean canConfigWifi() { @@ -79,7 +128,7 @@ public class AccessPointControllerImpl if (DEBUG) Log.d(TAG, "addCallback " + callback); mCallbacks.add(callback); if (mCallbacks.size() == 1) { - mWifiTracker.onStart(); + mMainExecutor.execute(() -> mLifecycle.setCurrentState(Lifecycle.State.STARTED)); } } @@ -89,37 +138,59 @@ public class AccessPointControllerImpl if (DEBUG) Log.d(TAG, "removeCallback " + callback); mCallbacks.remove(callback); if (mCallbacks.isEmpty()) { - mWifiTracker.onStop(); + mMainExecutor.execute(() -> mLifecycle.setCurrentState(Lifecycle.State.CREATED)); } } @Override public void scanForAccessPoints() { - fireAcccessPointsCallback(mWifiTracker.getAccessPoints()); + if (mWifiPickerTracker == null) { + fireAcccessPointsCallback(Collections.emptyList()); + return; + } + List<WifiEntry> entries = mWifiPickerTracker.getWifiEntries(); + WifiEntry connectedEntry = mWifiPickerTracker.getConnectedWifiEntry(); + if (connectedEntry != null) { + entries.add(0, connectedEntry); + } + fireAcccessPointsCallback(entries); } @Override - public int getIcon(AccessPoint ap) { + public int getIcon(WifiEntry ap) { int level = ap.getLevel(); - return ICONS[level >= 0 ? level : 0]; + return ICONS[Math.max(0, level)]; } - public boolean connect(AccessPoint ap) { + /** + * Connects to a {@link WifiEntry} if it's saved or does not require security. + * + * If the entry is not saved and requires security, will trigger + * {@link AccessPointCallback#onSettingsActivityTriggered}. + * @param ap + * @return {@code true} if {@link AccessPointCallback#onSettingsActivityTriggered} is triggered + */ + public boolean connect(WifiEntry ap) { if (ap == null) return false; - if (DEBUG) Log.d(TAG, "connect networkId=" + ap.getConfig().networkId); + if (DEBUG) { + if (ap.getWifiConfiguration() != null) { + Log.d(TAG, "connect networkId=" + ap.getWifiConfiguration().networkId); + } else { + Log.d(TAG, "connect to unsaved network " + ap.getTitle()); + } + } if (ap.isSaved()) { - mWifiTracker.getManager().connect(ap.getConfig().networkId, mConnectListener); + ap.connect(mConnectCallback); } else { // Unknown network, need to add it. - if (ap.getSecurity() != AccessPoint.SECURITY_NONE) { + if (ap.getSecurity() != WifiEntry.SECURITY_NONE) { Intent intent = new Intent(Settings.ACTION_WIFI_SETTINGS); - intent.putExtra(EXTRA_START_CONNECT_SSID, ap.getSsidStr()); + intent.putExtra(EXTRA_START_CONNECT_SSID, ap.getSsid()); intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK); fireSettingsIntentCallback(intent); return true; } else { - ap.generateOpenNetworkConfig(); - mWifiTracker.getManager().connect(ap.getConfig(), mConnectListener); + ap.connect(mConnectCallback); } } return false; @@ -131,39 +202,129 @@ public class AccessPointControllerImpl } } - private void fireAcccessPointsCallback(List<AccessPoint> aps) { + private void fireAcccessPointsCallback(List<WifiEntry> aps) { for (AccessPointCallback callback : mCallbacks) { callback.onAccessPointsChanged(aps); } } public void dump(PrintWriter pw) { - mWifiTracker.dump(pw); + IndentingPrintWriter ipw = new IndentingPrintWriter(pw); + ipw.println("AccessPointControllerImpl:"); + ipw.increaseIndent(); + ipw.println("Callbacks: " + Arrays.toString(mCallbacks.toArray())); + ipw.println("WifiPickerTracker: " + mWifiPickerTracker.toString()); + if (mWifiPickerTracker != null && !mCallbacks.isEmpty()) { + ipw.println("Connected: " + mWifiPickerTracker.getConnectedWifiEntry()); + ipw.println("Other wifi entries: " + + Arrays.toString(mWifiPickerTracker.getWifiEntries().toArray())); + } else if (mWifiPickerTracker != null) { + ipw.println("WifiPickerTracker not started, cannot get reliable entries"); + } + ipw.decreaseIndent(); } @Override - public void onWifiStateChanged(int state) { + public void onWifiStateChanged() { + scanForAccessPoints(); } @Override - public void onConnectedChanged() { - fireAcccessPointsCallback(mWifiTracker.getAccessPoints()); + public void onWifiEntriesChanged() { + scanForAccessPoints(); } @Override - public void onAccessPointsChanged() { - fireAcccessPointsCallback(mWifiTracker.getAccessPoints()); + public void onNumSavedNetworksChanged() { + // Do nothing } - private final ActionListener mConnectListener = new ActionListener() { - @Override - public void onSuccess() { - if (DEBUG) Log.d(TAG, "connect success"); - } + @Override + public void onNumSavedSubscriptionsChanged() { + // Do nothing + } + private final WifiEntry.ConnectCallback mConnectCallback = new WifiEntry.ConnectCallback() { @Override - public void onFailure(int reason) { - if (DEBUG) Log.d(TAG, "connect failure reason=" + reason); + public void onConnectResult(int status) { + if (status == CONNECT_STATUS_SUCCESS) { + if (DEBUG) Log.d(TAG, "connect success"); + } else { + if (DEBUG) Log.d(TAG, "connect failure reason=" + status); + } } }; + + /** + * Factory for creating {@link WifiPickerTracker}. + * + * Uses the same time intervals as the Settings page for Wifi. + */ + @SysUISingleton + public static class WifiPickerTrackerFactory { + + // Max age of tracked WifiEntries + private static final long MAX_SCAN_AGE_MILLIS = 15_000; + // Interval between initiating WifiPickerTracker scans + private static final long SCAN_INTERVAL_MILLIS = 10_000; + + private final Context mContext; + private final @Nullable WifiManager mWifiManager; + private final ConnectivityManager mConnectivityManager; + private final NetworkScoreManager mNetworkScoreManager; + private final Handler mMainHandler; + private final Handler mWorkerHandler; + private final Clock mClock = new SimpleClock(ZoneOffset.UTC) { + @Override + public long millis() { + return SystemClock.elapsedRealtime(); + } + }; + + @Inject + public WifiPickerTrackerFactory( + Context context, + @Nullable WifiManager wifiManager, + ConnectivityManager connectivityManager, + NetworkScoreManager networkScoreManager, + @Main Handler mainHandler, + @Background Handler workerHandler + ) { + mContext = context; + mWifiManager = wifiManager; + mConnectivityManager = connectivityManager; + mNetworkScoreManager = networkScoreManager; + mMainHandler = mainHandler; + mWorkerHandler = workerHandler; + } + + /** + * Create a {@link WifiPickerTracker} + * + * @param lifecycle + * @param listener + * @return a new {@link WifiPickerTracker} or {@code null} if {@link WifiManager} is null. + */ + public @Nullable WifiPickerTracker create( + Lifecycle lifecycle, + WifiPickerTracker.WifiPickerTrackerCallback listener + ) { + if (mWifiManager == null) { + return null; + } + return new WifiPickerTracker( + lifecycle, + mContext, + mWifiManager, + mConnectivityManager, + mNetworkScoreManager, + mMainHandler, + mWorkerHandler, + mClock, + MAX_SCAN_AGE_MILLIS, + SCAN_INTERVAL_MILLIS, + listener + ); + } + } } diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/NetworkController.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/NetworkController.java index f92860b70116..b012dc4c7159 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/NetworkController.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/NetworkController.java @@ -21,9 +21,9 @@ import android.content.Intent; import android.telephony.SubscriptionInfo; import com.android.settingslib.net.DataUsageController; -import com.android.settingslib.wifi.AccessPoint; import com.android.systemui.demomode.DemoMode; import com.android.systemui.statusbar.policy.NetworkController.SignalCallback; +import com.android.wifitrackerlib.WifiEntry; import java.util.List; @@ -123,12 +123,12 @@ public interface NetworkController extends CallbackController<SignalCallback>, D void addAccessPointCallback(AccessPointCallback callback); void removeAccessPointCallback(AccessPointCallback callback); void scanForAccessPoints(); - int getIcon(AccessPoint ap); - boolean connect(AccessPoint ap); + int getIcon(WifiEntry ap); + boolean connect(WifiEntry ap); boolean canConfigWifi(); public interface AccessPointCallback { - void onAccessPointsChanged(List<AccessPoint> accessPoints); + void onAccessPointsChanged(List<WifiEntry> accessPoints); void onSettingsActivityTriggered(Intent settingsIntent); } } diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/NetworkControllerImpl.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/NetworkControllerImpl.java index e41996604c99..5f5a83c5c50c 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/NetworkControllerImpl.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/NetworkControllerImpl.java @@ -187,6 +187,7 @@ public class NetworkControllerImpl extends BroadcastReceiver TelephonyManager telephonyManager, @Nullable WifiManager wifiManager, NetworkScoreManager networkScoreManager, + AccessPointControllerImpl accessPointController, DemoModeController demoModeController) { this(context, connectivityManager, telephonyManager, @@ -194,7 +195,7 @@ public class NetworkControllerImpl extends BroadcastReceiver networkScoreManager, SubscriptionManager.from(context), Config.readConfig(context), bgLooper, new CallbackHandler(), - new AccessPointControllerImpl(context), + accessPointController, new DataUsageController(context), new SubscriptionDefaults(), deviceProvisionedController, diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/dagger/StatusBarPolicyModule.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/dagger/StatusBarPolicyModule.java index 914105fdc4c4..069b4051af50 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/dagger/StatusBarPolicyModule.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/dagger/StatusBarPolicyModule.java @@ -16,6 +16,12 @@ package com.android.systemui.statusbar.policy.dagger; +import android.os.UserManager; + +import com.android.systemui.dagger.SysUISingleton; +import com.android.systemui.dagger.qualifiers.Main; +import com.android.systemui.settings.UserTracker; +import com.android.systemui.statusbar.policy.AccessPointControllerImpl; import com.android.systemui.statusbar.policy.BluetoothController; import com.android.systemui.statusbar.policy.BluetoothControllerImpl; import com.android.systemui.statusbar.policy.CastController; @@ -45,8 +51,11 @@ import com.android.systemui.statusbar.policy.UserInfoControllerImpl; import com.android.systemui.statusbar.policy.ZenModeController; import com.android.systemui.statusbar.policy.ZenModeControllerImpl; +import java.util.concurrent.Executor; + import dagger.Binds; import dagger.Module; +import dagger.Provides; /** Dagger Module for code in the statusbar.policy package. */ @@ -109,4 +118,27 @@ public interface StatusBarPolicyModule { @Binds ZenModeController provideZenModeController(ZenModeControllerImpl controllerImpl); + /** */ + @Binds + NetworkController.AccessPointController provideAccessPointController( + AccessPointControllerImpl accessPointControllerImpl); + + /** */ + @SysUISingleton + @Provides + static AccessPointControllerImpl provideAccessPointControllerImpl( + UserManager userManager, + UserTracker userTracker, + @Main Executor mainExecutor, + AccessPointControllerImpl.WifiPickerTrackerFactory wifiPickerTrackerFactory + ) { + AccessPointControllerImpl controller = new AccessPointControllerImpl( + userManager, + userTracker, + mainExecutor, + wifiPickerTrackerFactory + ); + controller.init(); + return controller; + } } diff --git a/packages/SystemUI/src/com/android/systemui/wmshell/TvWMShellModule.java b/packages/SystemUI/src/com/android/systemui/wmshell/TvWMShellModule.java index e20cd44974bb..f0ccc6d15661 100644 --- a/packages/SystemUI/src/com/android/systemui/wmshell/TvWMShellModule.java +++ b/packages/SystemUI/src/com/android/systemui/wmshell/TvWMShellModule.java @@ -45,9 +45,9 @@ public class TvWMShellModule { @WMSingleton @Provides static DisplayImeController provideDisplayImeController(IWindowManager wmService, - DisplayController displayController, @ShellMainThread ShellExecutor shellMainExecutor, + DisplayController displayController, @ShellMainThread ShellExecutor mainExecutor, TransactionPool transactionPool) { - return new DisplayImeController(wmService, displayController, shellMainExecutor, + return new DisplayImeController(wmService, displayController, mainExecutor, transactionPool); } diff --git a/packages/SystemUI/src/com/android/systemui/wmshell/WMShell.java b/packages/SystemUI/src/com/android/systemui/wmshell/WMShell.java index 08194291ce2f..715b0a25e461 100644 --- a/packages/SystemUI/src/com/android/systemui/wmshell/WMShell.java +++ b/packages/SystemUI/src/com/android/systemui/wmshell/WMShell.java @@ -43,6 +43,7 @@ import com.android.keyguard.KeyguardUpdateMonitorCallback; import com.android.systemui.Dependency; import com.android.systemui.SystemUI; import com.android.systemui.dagger.SysUISingleton; +import com.android.systemui.dagger.qualifiers.Main; import com.android.systemui.keyguard.ScreenLifecycle; import com.android.systemui.model.SysUiState; import com.android.systemui.navigationbar.NavigationModeController; @@ -67,6 +68,7 @@ import java.io.FileDescriptor; import java.io.PrintWriter; import java.util.Arrays; import java.util.Optional; +import java.util.concurrent.Executor; import javax.inject.Inject; @@ -86,18 +88,21 @@ public final class WMShell extends SystemUI | SYSUI_STATE_BUBBLES_EXPANDED | SYSUI_STATE_QUICK_SETTINGS_EXPANDED; + // Shell interfaces + private final Optional<Pip> mPipOptional; + private final Optional<LegacySplitScreen> mSplitScreenOptional; + private final Optional<OneHanded> mOneHandedOptional; + private final Optional<HideDisplayCutout> mHideDisplayCutoutOptional; + private final Optional<ShellCommandHandler> mShellCommandHandler; + private final CommandQueue mCommandQueue; private final ConfigurationController mConfigurationController; private final KeyguardUpdateMonitor mKeyguardUpdateMonitor; private final NavigationModeController mNavigationModeController; private final ScreenLifecycle mScreenLifecycle; private final SysUiState mSysUiState; - private final Optional<Pip> mPipOptional; - private final Optional<LegacySplitScreen> mSplitScreenOptional; - private final Optional<OneHanded> mOneHandedOptional; - private final Optional<HideDisplayCutout> mHideDisplayCutoutOptional; private final ProtoTracer mProtoTracer; - private final Optional<ShellCommandHandler> mShellCommandHandler; + private final Executor mSysUiMainExecutor; private boolean mIsSysUiStateValid; private KeyguardUpdateMonitorCallback mSplitScreenKeyguardCallback; @@ -105,18 +110,20 @@ public final class WMShell extends SystemUI private KeyguardUpdateMonitorCallback mOneHandedKeyguardCallback; @Inject - public WMShell(Context context, CommandQueue commandQueue, + public WMShell(Context context, + Optional<Pip> pipOptional, + Optional<LegacySplitScreen> splitScreenOptional, + Optional<OneHanded> oneHandedOptional, + Optional<HideDisplayCutout> hideDisplayCutoutOptional, + Optional<ShellCommandHandler> shellCommandHandler, + CommandQueue commandQueue, ConfigurationController configurationController, KeyguardUpdateMonitor keyguardUpdateMonitor, NavigationModeController navigationModeController, ScreenLifecycle screenLifecycle, SysUiState sysUiState, - Optional<Pip> pipOptional, - Optional<LegacySplitScreen> splitScreenOptional, - Optional<OneHanded> oneHandedOptional, - Optional<HideDisplayCutout> hideDisplayCutoutOptional, ProtoTracer protoTracer, - Optional<ShellCommandHandler> shellCommandHandler) { + @Main Executor sysUiMainExecutor) { super(context); mCommandQueue = commandQueue; mConfigurationController = configurationController; @@ -129,12 +136,13 @@ public final class WMShell extends SystemUI mOneHandedOptional = oneHandedOptional; mHideDisplayCutoutOptional = hideDisplayCutoutOptional; mProtoTracer = protoTracer; - mProtoTracer.add(this); mShellCommandHandler = shellCommandHandler; + mSysUiMainExecutor = sysUiMainExecutor; } @Override public void start() { + mProtoTracer.add(this); mCommandQueue.addCallback(this); mPipOptional.ifPresent(this::initPip); mSplitScreenOptional.ifPresent(this::initSplitScreen); @@ -213,34 +221,43 @@ public final class WMShell extends SystemUI oneHanded.registerTransitionCallback(new OneHandedTransitionCallback() { @Override public void onStartFinished(Rect bounds) { - mSysUiState.setFlag(SYSUI_STATE_ONE_HANDED_ACTIVE, - true).commitUpdate(DEFAULT_DISPLAY); + mSysUiMainExecutor.execute(() -> { + mSysUiState.setFlag(SYSUI_STATE_ONE_HANDED_ACTIVE, + true).commitUpdate(DEFAULT_DISPLAY); + }); } @Override public void onStopFinished(Rect bounds) { - mSysUiState.setFlag(SYSUI_STATE_ONE_HANDED_ACTIVE, - false).commitUpdate(DEFAULT_DISPLAY); + mSysUiMainExecutor.execute(() -> { + mSysUiState.setFlag(SYSUI_STATE_ONE_HANDED_ACTIVE, + false).commitUpdate(DEFAULT_DISPLAY); + }); } }); oneHanded.registerGestureCallback(new OneHandedGestureEventCallback() { @Override public void onStart() { - if (oneHanded.isOneHandedEnabled()) { - oneHanded.startOneHanded(); - } else if (oneHanded.isSwipeToNotificationEnabled()) { - mCommandQueue.handleSystemKey(KeyEvent.KEYCODE_SYSTEM_NAVIGATION_DOWN); - } + mSysUiMainExecutor.execute(() -> { + if (oneHanded.isOneHandedEnabled()) { + oneHanded.startOneHanded(); + } else if (oneHanded.isSwipeToNotificationEnabled()) { + mCommandQueue.handleSystemKey(KeyEvent.KEYCODE_SYSTEM_NAVIGATION_DOWN); + } + }); } @Override public void onStop() { - if (oneHanded.isOneHandedEnabled()) { - oneHanded.stopOneHanded(OneHandedEvents.EVENT_ONE_HANDED_TRIGGER_GESTURE_OUT); - } else if (oneHanded.isSwipeToNotificationEnabled()) { - mCommandQueue.handleSystemKey(KeyEvent.KEYCODE_SYSTEM_NAVIGATION_UP); - } + mSysUiMainExecutor.execute(() -> { + if (oneHanded.isOneHandedEnabled()) { + oneHanded.stopOneHanded( + OneHandedEvents.EVENT_ONE_HANDED_TRIGGER_GESTURE_OUT); + } else if (oneHanded.isSwipeToNotificationEnabled()) { + mCommandQueue.handleSystemKey(KeyEvent.KEYCODE_SYSTEM_NAVIGATION_UP); + } + }); } }); diff --git a/packages/SystemUI/src/com/android/systemui/wmshell/WMShellBaseModule.java b/packages/SystemUI/src/com/android/systemui/wmshell/WMShellBaseModule.java index 572b15d5215a..bcb4386c2ceb 100644 --- a/packages/SystemUI/src/com/android/systemui/wmshell/WMShellBaseModule.java +++ b/packages/SystemUI/src/com/android/systemui/wmshell/WMShellBaseModule.java @@ -110,9 +110,9 @@ public abstract class WMShellBaseModule { @ShellMainThread public static Handler provideShellMainHandler(@Main Handler sysuiMainHandler) { if (ENABLE_SHELL_MAIN_THREAD) { - HandlerThread shellMainThread = new HandlerThread("wmshell.main"); - shellMainThread.start(); - return shellMainThread.getThreadHandler(); + HandlerThread mainThread = new HandlerThread("wmshell.main"); + mainThread.start(); + return mainThread.getThreadHandler(); } return sysuiMainHandler; } @@ -123,10 +123,10 @@ public abstract class WMShellBaseModule { @WMSingleton @Provides @ShellMainThread - public static ShellExecutor provideShellMainExecutor(@ShellMainThread Handler shellMainHandler, + public static ShellExecutor provideShellMainExecutor(@ShellMainThread Handler mainHandler, @Main ShellExecutor sysuiMainExecutor) { if (ENABLE_SHELL_MAIN_THREAD) { - return new HandlerExecutor(shellMainHandler); + return new HandlerExecutor(mainHandler); } return sysuiMainExecutor; } @@ -177,7 +177,7 @@ public abstract class WMShellBaseModule { Optional<AppPairs> appPairsOptional, FullscreenTaskListener fullscreenTaskListener, Transitions transitions, - @ShellMainThread ShellExecutor shellMainExecutor) { + @ShellMainThread ShellExecutor mainExecutor) { return ShellInitImpl.create(displayImeController, dragAndDropController, shellTaskOrganizer, @@ -185,7 +185,7 @@ public abstract class WMShellBaseModule { appPairsOptional, fullscreenTaskListener, transitions, - shellMainExecutor); + mainExecutor); } /** @@ -201,10 +201,10 @@ public abstract class WMShellBaseModule { Optional<OneHanded> oneHandedOptional, Optional<HideDisplayCutout> hideDisplayCutout, Optional<AppPairs> appPairsOptional, - @ShellMainThread ShellExecutor shellMainExecutor) { + @ShellMainThread ShellExecutor mainExecutor) { return Optional.of(ShellCommandHandlerImpl.create(shellTaskOrganizer, legacySplitScreenOptional, pipOptional, oneHandedOptional, hideDisplayCutout, - appPairsOptional, shellMainExecutor)); + appPairsOptional, mainExecutor)); } @WMSingleton @@ -216,8 +216,8 @@ public abstract class WMShellBaseModule { @WMSingleton @Provides static DisplayController provideDisplayController(Context context, - IWindowManager wmService, @ShellMainThread ShellExecutor shellMainExecutor) { - return new DisplayController(context, wmService, shellMainExecutor); + IWindowManager wmService, @ShellMainThread ShellExecutor mainExecutor) { + return new DisplayController(context, wmService, mainExecutor); } @WMSingleton @@ -236,8 +236,8 @@ public abstract class WMShellBaseModule { @WMSingleton @Provides static WindowManagerShellWrapper provideWindowManagerShellWrapper( - @ShellMainThread ShellExecutor shellMainExecutor) { - return new WindowManagerShellWrapper(shellMainExecutor); + @ShellMainThread ShellExecutor mainExecutor) { + return new WindowManagerShellWrapper(mainExecutor); } @WMSingleton @@ -277,8 +277,8 @@ public abstract class WMShellBaseModule { @WMSingleton @Provides static SyncTransactionQueue provideSyncTransactionQueue(TransactionPool pool, - @ShellMainThread ShellExecutor shellMainExecutor) { - return new SyncTransactionQueue(pool, shellMainExecutor); + @ShellMainThread ShellExecutor mainExecutor) { + return new SyncTransactionQueue(pool, mainExecutor); } @WMSingleton @@ -299,8 +299,8 @@ public abstract class WMShellBaseModule { @WMSingleton @Provides static TaskStackListenerImpl providerTaskStackListenerImpl( - @ShellMainThread Handler shellMainHandler) { - return new TaskStackListenerImpl(shellMainHandler); + @ShellMainThread Handler mainHandler) { + return new TaskStackListenerImpl(mainHandler); } @BindsOptionalOf @@ -319,20 +319,22 @@ public abstract class WMShellBaseModule { LauncherApps launcherApps, UiEventLogger uiEventLogger, ShellTaskOrganizer organizer, - @ShellMainThread ShellExecutor shellMainExecutor) { + @ShellMainThread ShellExecutor mainExecutor) { return Optional.of(BubbleController.create(context, null /* synchronizer */, floatingContentCoordinator, statusBarService, windowManager, windowManagerShellWrapper, launcherApps, uiEventLogger, organizer, - shellMainExecutor)); + mainExecutor)); } + // Needs the shell main handler for ContentObserver callbacks @WMSingleton @Provides static Optional<OneHanded> provideOneHandedController(Context context, DisplayController displayController, TaskStackListenerImpl taskStackListener, - @ShellMainThread ShellExecutor mainExecutor) { + @ShellMainThread ShellExecutor mainExecutor, + @ShellMainThread Handler mainHandler) { return Optional.ofNullable(OneHandedController.create(context, displayController, - taskStackListener, mainExecutor)); + taskStackListener, mainExecutor, mainHandler)); } @WMSingleton diff --git a/packages/SystemUI/src/com/android/systemui/wmshell/WMShellModule.java b/packages/SystemUI/src/com/android/systemui/wmshell/WMShellModule.java index e635e1708841..88f6e1ffac0c 100644 --- a/packages/SystemUI/src/com/android/systemui/wmshell/WMShellModule.java +++ b/packages/SystemUI/src/com/android/systemui/wmshell/WMShellModule.java @@ -62,9 +62,9 @@ public class WMShellModule { @WMSingleton @Provides static DisplayImeController provideDisplayImeController(IWindowManager wmService, - DisplayController displayController, @ShellMainThread ShellExecutor shellMainExecutor, + DisplayController displayController, @ShellMainThread ShellExecutor mainExecutor, TransactionPool transactionPool) { - return new DisplayImeController(wmService, displayController, shellMainExecutor, + return new DisplayImeController(wmService, displayController, mainExecutor, transactionPool); } @@ -96,11 +96,11 @@ public class WMShellModule { PhonePipMenuController phonePipMenuController, PipTaskOrganizer pipTaskOrganizer, PipTouchHandler pipTouchHandler, WindowManagerShellWrapper windowManagerShellWrapper, TaskStackListenerImpl taskStackListener, - @ShellMainThread ShellExecutor shellMainExecutor) { + @ShellMainThread ShellExecutor mainExecutor) { return Optional.ofNullable(PipController.create(context, displayController, pipAppOpsListener, pipBoundsAlgorithm, pipBoundsState, pipMediaController, phonePipMenuController, pipTaskOrganizer, pipTouchHandler, - windowManagerShellWrapper, taskStackListener, shellMainExecutor)); + windowManagerShellWrapper, taskStackListener, mainExecutor)); } @WMSingleton @@ -131,10 +131,10 @@ public class WMShellModule { PipTaskOrganizer pipTaskOrganizer, FloatingContentCoordinator floatingContentCoordinator, PipUiEventLogger pipUiEventLogger, - @ShellMainThread ShellExecutor shellMainExecutor) { + @ShellMainThread ShellExecutor mainExecutor) { return new PipTouchHandler(context, menuPhoneController, pipBoundsAlgorithm, pipBoundsState, pipTaskOrganizer, floatingContentCoordinator, pipUiEventLogger, - shellMainExecutor); + mainExecutor); } @WMSingleton diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/AccessPointControllerImplTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/AccessPointControllerImplTest.kt new file mode 100644 index 000000000000..4068f93f5ee3 --- /dev/null +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/AccessPointControllerImplTest.kt @@ -0,0 +1,229 @@ +/* + * Copyright (C) 2020 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.systemui.statusbar.policy + +import android.os.UserManager +import android.test.suitebuilder.annotation.SmallTest +import android.testing.AndroidTestingRunner +import android.testing.TestableLooper.RunWithLooper +import androidx.lifecycle.Lifecycle +import com.android.systemui.SysuiTestCase +import com.android.systemui.settings.UserTracker +import com.android.systemui.util.mockito.capture +import com.android.wifitrackerlib.WifiEntry +import com.android.wifitrackerlib.WifiPickerTracker +import com.google.common.truth.Truth.assertThat +import org.junit.Before +import org.junit.Test +import org.junit.runner.RunWith +import org.mockito.ArgumentCaptor +import org.mockito.ArgumentMatchers.any +import org.mockito.ArgumentMatchers.anyList +import org.mockito.Captor +import org.mockito.Mock +import org.mockito.Mockito.`when` +import org.mockito.Mockito.never +import org.mockito.Mockito.verify +import org.mockito.MockitoAnnotations +import java.util.concurrent.Executor + +@SmallTest +@RunWith(AndroidTestingRunner::class) +@RunWithLooper(setAsMainLooper = true) +class AccessPointControllerImplTest : SysuiTestCase() { + + @Mock + private lateinit var userManager: UserManager + @Mock + private lateinit var userTracker: UserTracker + @Mock + private lateinit var wifiPickerTrackerFactory: + AccessPointControllerImpl.WifiPickerTrackerFactory + @Mock + private lateinit var wifiPickerTracker: WifiPickerTracker + @Mock + private lateinit var callback: NetworkController.AccessPointController.AccessPointCallback + @Mock + private lateinit var otherCallback: NetworkController.AccessPointController.AccessPointCallback + @Mock + private lateinit var wifiEntryConnected: WifiEntry + @Mock + private lateinit var wifiEntryOther: WifiEntry + @Captor + private lateinit var wifiEntryListCaptor: ArgumentCaptor<List<WifiEntry>> + + private val instantExecutor = Executor { it.run() } + private lateinit var controller: AccessPointControllerImpl + + @Before + fun setUp() { + MockitoAnnotations.initMocks(this) + `when`(wifiPickerTrackerFactory.create(any(), any())).thenReturn(wifiPickerTracker) + + `when`(wifiPickerTracker.connectedWifiEntry).thenReturn(wifiEntryConnected) + `when`(wifiPickerTracker.wifiEntries).thenReturn(ArrayList<WifiEntry>().apply { + add(wifiEntryOther) + }) + + controller = AccessPointControllerImpl( + userManager, + userTracker, + instantExecutor, + wifiPickerTrackerFactory + ) + + controller.init() + } + + @Test + fun testInitialLifecycleStateCreated() { + assertThat(controller.lifecycle.currentState).isEqualTo(Lifecycle.State.CREATED) + } + + @Test + fun testLifecycleStartedAfterFirstCallback() { + controller.addAccessPointCallback(callback) + assertThat(controller.lifecycle.currentState).isEqualTo(Lifecycle.State.STARTED) + } + + @Test + fun testLifecycleBackToCreatedAfterRemovingOnlyCallback() { + controller.addAccessPointCallback(callback) + controller.removeAccessPointCallback(callback) + + assertThat(controller.lifecycle.currentState).isEqualTo(Lifecycle.State.CREATED) + } + + @Test + fun testLifecycleStillStartedAfterRemovingSecondCallback() { + controller.addAccessPointCallback(callback) + controller.addAccessPointCallback(otherCallback) + controller.removeAccessPointCallback(callback) + + assertThat(controller.lifecycle.currentState).isEqualTo(Lifecycle.State.STARTED) + } + + @Test + fun testScanForAccessPointsTriggersCallbackWithEntriesInOrder() { + controller.addAccessPointCallback(callback) + controller.scanForAccessPoints() + + verify(callback).onAccessPointsChanged(capture(wifiEntryListCaptor)) + + assertThat(wifiEntryListCaptor.value).containsExactly(wifiEntryConnected, wifiEntryOther) + } + + @Test + fun testOnWifiStateChangedTriggersCallbackWithEntriesInOrder() { + controller.addAccessPointCallback(callback) + controller.onWifiStateChanged() + + verify(callback).onAccessPointsChanged(capture(wifiEntryListCaptor)) + + assertThat(wifiEntryListCaptor.value).containsExactly(wifiEntryConnected, wifiEntryOther) + } + + @Test + fun testOnWifiEntriesChangedTriggersCallbackWithEntriesInOrder() { + controller.addAccessPointCallback(callback) + controller.onWifiEntriesChanged() + + verify(callback).onAccessPointsChanged(capture(wifiEntryListCaptor)) + + assertThat(wifiEntryListCaptor.value).containsExactly(wifiEntryConnected, wifiEntryOther) + } + + @Test + fun testOnNumSavedNetworksChangedDoesntTriggerCallback() { + controller.addAccessPointCallback(callback) + controller.onNumSavedNetworksChanged() + + verify(callback, never()).onAccessPointsChanged(anyList()) + } + + @Test + fun testOnNumSavedSubscriptionsChangedDoesntTriggerCallback() { + controller.addAccessPointCallback(callback) + controller.onNumSavedSubscriptionsChanged() + + verify(callback, never()).onAccessPointsChanged(anyList()) + } + + @Test + fun testReturnEmptyListWhenNoWifiPickerTracker() { + `when`(wifiPickerTrackerFactory.create(any(), any())).thenReturn(null) + val otherController = AccessPointControllerImpl( + userManager, + userTracker, + instantExecutor, + wifiPickerTrackerFactory + ) + otherController.init() + + otherController.addAccessPointCallback(callback) + otherController.scanForAccessPoints() + + verify(callback).onAccessPointsChanged(capture(wifiEntryListCaptor)) + + assertThat(wifiEntryListCaptor.value).isEmpty() + } + + @Test + fun connectToNullEntry() { + controller.addAccessPointCallback(callback) + + assertThat(controller.connect(null)).isFalse() + + verify(callback, never()).onSettingsActivityTriggered(any()) + } + + @Test + fun connectToSavedWifiEntry() { + controller.addAccessPointCallback(callback) + `when`(wifiEntryOther.isSaved).thenReturn(true) + + assertThat(controller.connect(wifiEntryOther)).isFalse() + + verify(wifiEntryOther).connect(any()) + verify(callback, never()).onSettingsActivityTriggered(any()) + } + + @Test + fun connectToSecuredNotSavedWifiEntry() { + controller.addAccessPointCallback(callback) + `when`(wifiEntryOther.isSaved).thenReturn(false) + `when`(wifiEntryOther.security).thenReturn(WifiEntry.SECURITY_EAP) + + // True means we will launch WifiSettings + assertThat(controller.connect(wifiEntryOther)).isTrue() + + verify(wifiEntryOther, never()).connect(any()) + verify(callback).onSettingsActivityTriggered(any()) + } + + @Test + fun connectToNotSecuredNotSavedWifiEntry() { + controller.addAccessPointCallback(callback) + `when`(wifiEntryOther.isSaved).thenReturn(false) + `when`(wifiEntryOther.security).thenReturn(WifiEntry.SECURITY_NONE) + + assertThat(controller.connect(wifiEntryOther)).isFalse() + + verify(wifiEntryOther).connect(any()) + verify(callback, never()).onSettingsActivityTriggered(any()) + } +}
\ No newline at end of file diff --git a/packages/SystemUI/tests/src/com/android/systemui/wmshell/WMShellTest.java b/packages/SystemUI/tests/src/com/android/systemui/wmshell/WMShellTest.java index 31bf7120900f..446d3f2f72a0 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/wmshell/WMShellTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/wmshell/WMShellTest.java @@ -34,6 +34,7 @@ import com.android.systemui.statusbar.CommandQueue; import com.android.systemui.statusbar.policy.ConfigurationController; import com.android.systemui.tracing.ProtoTracer; import com.android.wm.shell.ShellCommandHandler; +import com.android.wm.shell.common.ShellExecutor; import com.android.wm.shell.hidedisplaycutout.HideDisplayCutout; import com.android.wm.shell.legacysplitscreen.LegacySplitScreen; import com.android.wm.shell.onehanded.OneHanded; @@ -63,22 +64,22 @@ public class WMShellTest extends SysuiTestCase { @Mock SysUiState mSysUiState; @Mock Pip mPip; @Mock PipTouchHandler mPipTouchHandler; - @Mock - LegacySplitScreen mLegacySplitScreen; + @Mock LegacySplitScreen mLegacySplitScreen; @Mock OneHanded mOneHanded; @Mock HideDisplayCutout mHideDisplayCutout; @Mock ProtoTracer mProtoTracer; @Mock ShellCommandHandler mShellCommandHandler; + @Mock ShellExecutor mSysUiMainExecutor; @Before public void setUp() { MockitoAnnotations.initMocks(this); - mWMShell = new WMShell(mContext, mCommandQueue, mConfigurationController, + mWMShell = new WMShell(mContext, Optional.of(mPip), Optional.of(mLegacySplitScreen), + Optional.of(mOneHanded), Optional.of(mHideDisplayCutout), + Optional.of(mShellCommandHandler), mCommandQueue, mConfigurationController, mKeyguardUpdateMonitor, mNavigationModeController, - mScreenLifecycle, mSysUiState, Optional.of(mPip), Optional.of(mLegacySplitScreen), - Optional.of(mOneHanded), Optional.of(mHideDisplayCutout), mProtoTracer, - Optional.of(mShellCommandHandler)); + mScreenLifecycle, mSysUiState, mProtoTracer, mSysUiMainExecutor); when(mPip.getPipTouchHandler()).thenReturn(mPipTouchHandler); } diff --git a/packages/overlays/IconPackCircularSettingsOverlay/res/drawable/ic_apps.xml b/packages/overlays/IconPackCircularSettingsOverlay/res/drawable/ic_apps.xml index 228e2ccfcfb9..95c08678ca9a 100644 --- a/packages/overlays/IconPackCircularSettingsOverlay/res/drawable/ic_apps.xml +++ b/packages/overlays/IconPackCircularSettingsOverlay/res/drawable/ic_apps.xml @@ -20,30 +20,30 @@ android:viewportWidth="24" android:width="24dp" > <path - android:fillColor="@android:color/white" + android:fillColor="?android:attr/colorPrimary" android:pathData="M 6 4 C 7.10456949966 4 8 4.89543050034 8 6 C 8 7.10456949966 7.10456949966 8 6 8 C 4.89543050034 8 4 7.10456949966 4 6 C 4 4.89543050034 4.89543050034 4 6 4 Z" /> <path - android:fillColor="@android:color/white" + android:fillColor="?android:attr/colorPrimary" android:pathData="M 12 4 C 13.1045694997 4 14 4.89543050034 14 6 C 14 7.10456949966 13.1045694997 8 12 8 C 10.8954305003 8 10 7.10456949966 10 6 C 10 4.89543050034 10.8954305003 4 12 4 Z" /> <path - android:fillColor="@android:color/white" + android:fillColor="?android:attr/colorPrimary" android:pathData="M 18 4 C 19.1045694997 4 20 4.89543050034 20 6 C 20 7.10456949966 19.1045694997 8 18 8 C 16.8954305003 8 16 7.10456949966 16 6 C 16 4.89543050034 16.8954305003 4 18 4 Z" /> <path - android:fillColor="@android:color/white" + android:fillColor="?android:attr/colorPrimary" android:pathData="M 6 10 C 7.10456949966 10 8 10.8954305003 8 12 C 8 13.1045694997 7.10456949966 14 6 14 C 4.89543050034 14 4 13.1045694997 4 12 C 4 10.8954305003 4.89543050034 10 6 10 Z" /> <path - android:fillColor="@android:color/white" + android:fillColor="?android:attr/colorPrimary" android:pathData="M 12 10 C 13.1045694997 10 14 10.8954305003 14 12 C 14 13.1045694997 13.1045694997 14 12 14 C 10.8954305003 14 10 13.1045694997 10 12 C 10 10.8954305003 10.8954305003 10 12 10 Z" /> <path - android:fillColor="@android:color/white" + android:fillColor="?android:attr/colorPrimary" android:pathData="M 18 10 C 19.1045694997 10 20 10.8954305003 20 12 C 20 13.1045694997 19.1045694997 14 18 14 C 16.8954305003 14 16 13.1045694997 16 12 C 16 10.8954305003 16.8954305003 10 18 10 Z" /> <path - android:fillColor="@android:color/white" + android:fillColor="?android:attr/colorPrimary" android:pathData="M 6 16 C 7.10456949966 16 8 16.8954305003 8 18 C 8 19.1045694997 7.10456949966 20 6 20 C 4.89543050034 20 4 19.1045694997 4 18 C 4 16.8954305003 4.89543050034 16 6 16 Z" /> <path - android:fillColor="@android:color/white" + android:fillColor="?android:attr/colorPrimary" android:pathData="M 12 16 C 13.1045694997 16 14 16.8954305003 14 18 C 14 19.1045694997 13.1045694997 20 12 20 C 10.8954305003 20 10 19.1045694997 10 18 C 10 16.8954305003 10.8954305003 16 12 16 Z" /> <path - android:fillColor="@android:color/white" + android:fillColor="?android:attr/colorPrimary" android:pathData="M 18 16 C 19.1045694997 16 20 16.8954305003 20 18 C 20 19.1045694997 19.1045694997 20 18 20 C 16.8954305003 20 16 19.1045694997 16 18 C 16 16.8954305003 16.8954305003 16 18 16 Z" /> </vector>
\ No newline at end of file diff --git a/packages/overlays/IconPackCircularSettingsOverlay/res/drawable/ic_devices_other.xml b/packages/overlays/IconPackCircularSettingsOverlay/res/drawable/ic_devices_other.xml index 14898c465a31..454b2e260334 100644 --- a/packages/overlays/IconPackCircularSettingsOverlay/res/drawable/ic_devices_other.xml +++ b/packages/overlays/IconPackCircularSettingsOverlay/res/drawable/ic_devices_other.xml @@ -21,12 +21,12 @@ android:viewportWidth="24" android:width="24dp" > <path - android:fillColor="@android:color/white" + android:fillColor="?android:attr/colorPrimary" android:pathData="M7.25,18.25c0-0.41-0.34-0.75-0.75-0.75H3V8.25C3,7.01,4.01,6,5.25,6h16C21.66,6,22,5.66,22,5.25S21.66,4.5,21.25,4.5h-16 C3.18,4.5,1.5,6.18,1.5,8.25V19h5C6.91,19,7.25,18.66,7.25,18.25z" /> <path - android:fillColor="@android:color/white" + android:fillColor="?android:attr/colorPrimary" android:pathData="M14,16c0-1.66-1.34-3-3-3s-3,1.34-3,3s1.34,3,3,3S14,17.66,14,16z M9.5,16c0-0.83,0.67-1.5,1.5-1.5s1.5,0.67,1.5,1.5 s-0.67,1.5-1.5,1.5S9.5,16.83,9.5,16z" /> <path - android:fillColor="@android:color/white" + android:fillColor="?android:attr/colorPrimary" android:pathData="M20,19c1.1,0,2-0.9,2-2v-7c0-1.1-0.9-2-2-2h-3c-1.1,0-2,0.9-2,2v7c0,1.1,0.9,2,2,2H20z M16.5,17v-7 c0-0.28,0.22-0.5,0.5-0.5h3c0.28,0,0.5,0.22,0.5,0.5v7c0,0.28-0.22,0.5-0.5,0.5h-3C16.72,17.5,16.5,17.28,16.5,17z" /> </vector>
\ No newline at end of file diff --git a/packages/overlays/IconPackCircularSettingsOverlay/res/drawable/ic_help.xml b/packages/overlays/IconPackCircularSettingsOverlay/res/drawable/ic_help.xml index 2fa1520df832..4e99add13424 100644 --- a/packages/overlays/IconPackCircularSettingsOverlay/res/drawable/ic_help.xml +++ b/packages/overlays/IconPackCircularSettingsOverlay/res/drawable/ic_help.xml @@ -20,12 +20,12 @@ android:viewportWidth="24" android:width="24dp" > <path - android:fillColor="@android:color/white" + android:fillColor="?android:attr/colorPrimary" android:pathData="M12,22c0,0,0.01,0,0.01,0c5.51,0,9.98-4.46,9.99-9.98c0-0.01,0-0.01,0-0.02c0-5.52-4.48-10-10-10S2,6.48,2,12 S6.48,22,12,22z M12,3.5c4.69,0,8.5,3.81,8.5,8.52c-0.01,4.68-3.81,8.48-8.5,8.48c-4.69,0-8.5-3.81-8.5-8.5 C3.5,7.31,7.31,3.5,12,3.5z" /> <path - android:fillColor="@android:color/white" + android:fillColor="?android:attr/colorPrimary" android:pathData="M8.57,9.89c0.4,0.1,0.81-0.15,0.9-0.56c0.12-0.5,0.36-0.94,0.71-1.29c1.06-1.06,2.78-1.06,3.84,0 c0.53,0.53,0.79,1.23,0.72,1.92c-0.06,0.62-0.39,1.15-0.92,1.5c-0.17,0.11-0.35,0.19-0.52,0.27c-0.7,0.33-1.67,0.78-1.93,2.37 c-0.07,0.41,0.21,0.8,0.61,0.86C12.02,14.99,12.06,15,12.1,15c0.36,0,0.68-0.26,0.74-0.62c0.14-0.82,0.48-0.98,1.09-1.26 c0.25-0.11,0.49-0.23,0.72-0.38c0.91-0.6,1.48-1.53,1.58-2.61c0.12-1.14-0.31-2.28-1.15-3.13c-1.64-1.64-4.32-1.64-5.96,0 c-0.54,0.54-0.93,1.24-1.11,2C7.92,9.39,8.16,9.8,8.57,9.89z" /> <path - android:fillColor="@android:color/white" + android:fillColor="?android:attr/colorPrimary" android:pathData="M 12 16.5 C 12.5522847498 16.5 13 16.9477152502 13 17.5 C 13 18.0522847498 12.5522847498 18.5 12 18.5 C 11.4477152502 18.5 11 18.0522847498 11 17.5 C 11 16.9477152502 11.4477152502 16.5 12 16.5 Z" /> </vector>
\ No newline at end of file diff --git a/packages/overlays/IconPackCircularSettingsOverlay/res/drawable/ic_phone_info.xml b/packages/overlays/IconPackCircularSettingsOverlay/res/drawable/ic_phone_info.xml index efc300ab8ac5..1cafbfe562c9 100644 --- a/packages/overlays/IconPackCircularSettingsOverlay/res/drawable/ic_phone_info.xml +++ b/packages/overlays/IconPackCircularSettingsOverlay/res/drawable/ic_phone_info.xml @@ -20,12 +20,12 @@ android:viewportWidth="24" android:width="24dp" > <path - android:fillColor="@android:color/white" + android:fillColor="?android:attr/colorPrimary" android:pathData="M15,22c1.66,0,3-1.34,3-3V5c0-1.66-1.34-3-3-3H9C7.34,2,6,3.34,6,5v14c0,1.66,1.34,3,3,3H15z M7.5,6.5h9v11h-9V6.5z M9,3.5 h6c0.83,0,1.5,0.67,1.5,1.5h-9C7.5,4.17,8.17,3.5,9,3.5z M7.5,19h9c0,0.83-0.67,1.5-1.5,1.5H9C8.17,20.5,7.5,19.83,7.5,19z" /> <path - android:fillColor="@android:color/white" + android:fillColor="?android:attr/colorPrimary" android:pathData="M12,11.5c-0.41,0-0.75,0.34-0.75,0.75v3c0,0.41,0.34,0.75,0.75,0.75s0.75-0.34,0.75-0.75v-3 C12.75,11.84,12.41,11.5,12,11.5z" /> <path - android:fillColor="@android:color/white" + android:fillColor="?android:attr/colorPrimary" android:pathData="M 12 8 C 12.5522847498 8 13 8.44771525017 13 9 C 13 9.55228474983 12.5522847498 10 12 10 C 11.4477152502 10 11 9.55228474983 11 9 C 11 8.44771525017 11.4477152502 8 12 8 Z" /> </vector>
\ No newline at end of file diff --git a/packages/overlays/IconPackCircularSettingsOverlay/res/drawable/ic_settings_accessibility.xml b/packages/overlays/IconPackCircularSettingsOverlay/res/drawable/ic_settings_accessibility.xml index 72814775e90a..4c57d8db3a2f 100644 --- a/packages/overlays/IconPackCircularSettingsOverlay/res/drawable/ic_settings_accessibility.xml +++ b/packages/overlays/IconPackCircularSettingsOverlay/res/drawable/ic_settings_accessibility.xml @@ -20,18 +20,18 @@ android:viewportWidth="24" android:width="24dp" > <path - android:fillColor="@android:color/white" + android:fillColor="?android:attr/colorPrimary" android:pathData="M3.03,5.54c-0.11,0.4,0.13,0.81,0.53,0.92C5.24,6.91,7.07,7.2,9,7.35v8.15v3.75C9,19.66,9.34,20,9.75,20 s0.75-0.34,0.75-0.75V15.5c0-0.83,0.67-1.5,1.5-1.5s1.5,0.67,1.5,1.5v3.75c0,0.41,0.34,0.75,0.75,0.75S15,19.66,15,19.25V15.5V7.35 c1.93-0.15,3.76-0.44,5.44-0.89c0.4-0.11,0.64-0.52,0.53-0.92c-0.11-0.4-0.51-0.64-0.92-0.53C17.64,5.66,14.93,5.98,12,5.98 S6.36,5.66,3.94,5.01C3.54,4.9,3.13,5.14,3.03,5.54z" /> <path - android:fillColor="@android:color/white" + android:fillColor="?android:attr/colorPrimary" android:pathData="M 8 22 C 8.55228474983 22 9 22.4477152502 9 23 C 9 23.5522847498 8.55228474983 24 8 24 C 7.44771525017 24 7 23.5522847498 7 23 C 7 22.4477152502 7.44771525017 22 8 22 Z" /> <path - android:fillColor="@android:color/white" + android:fillColor="?android:attr/colorPrimary" android:pathData="M 16 22 C 16.5522847498 22 17 22.4477152502 17 23 C 17 23.5522847498 16.5522847498 24 16 24 C 15.4477152502 24 15 23.5522847498 15 23 C 15 22.4477152502 15.4477152502 22 16 22 Z" /> <path - android:fillColor="@android:color/white" + android:fillColor="?android:attr/colorPrimary" android:pathData="M 12 22 C 12.5522847498 22 13 22.4477152502 13 23 C 13 23.5522847498 12.5522847498 24 12 24 C 11.4477152502 24 11 23.5522847498 11 23 C 11 22.4477152502 11.4477152502 22 12 22 Z" /> <path - android:fillColor="@android:color/white" + android:fillColor="?android:attr/colorPrimary" android:pathData="M 12 1 C 13.1045694997 1 14 1.89543050034 14 3 C 14 4.10456949966 13.1045694997 5 12 5 C 10.8954305003 5 10 4.10456949966 10 3 C 10 1.89543050034 10.8954305003 1 12 1 Z" /> </vector>
\ No newline at end of file diff --git a/packages/overlays/IconPackCircularSettingsOverlay/res/drawable/ic_settings_accounts.xml b/packages/overlays/IconPackCircularSettingsOverlay/res/drawable/ic_settings_accounts.xml index 6b5c4e4dbdff..c63ec5b91418 100644 --- a/packages/overlays/IconPackCircularSettingsOverlay/res/drawable/ic_settings_accounts.xml +++ b/packages/overlays/IconPackCircularSettingsOverlay/res/drawable/ic_settings_accounts.xml @@ -20,9 +20,9 @@ android:viewportWidth="24" android:width="24dp" > <path - android:fillColor="@android:color/white" + android:fillColor="?android:attr/colorPrimary" android:pathData="M17,3H7C4.79,3,3,4.79,3,7v10c0,2.21,1.79,4,4,4h10c2.21,0,4-1.79,4-4V7C21,4.79,19.21,3,17,3z M17,19.5H7 c-0.93,0-1.73-0.52-2.16-1.27C6.59,16.47,9.11,15.5,12,15.5s5.41,0.97,7.16,2.73C18.73,18.98,17.93,19.5,17,19.5z M19.5,16.51 C17.52,14.89,14.92,14,12,14s-5.52,0.89-7.5,2.51V7c0-1.38,1.12-2.5,2.5-2.5h10c1.38,0,2.5,1.12,2.5,2.5V16.51z" /> <path - android:fillColor="@android:color/white" + android:fillColor="?android:attr/colorPrimary" android:pathData="M12,6c-1.93,0-3.5,1.57-3.5,3.5S10.07,13,12,13s3.5-1.57,3.5-3.5S13.93,6,12,6z M12,11.5c-1.1,0-2-0.9-2-2 c0-1.1,0.9-2,2-2c1.1,0,2,0.9,2,2C14,10.6,13.1,11.5,12,11.5z" /> </vector>
\ No newline at end of file diff --git a/packages/overlays/IconPackCircularSettingsOverlay/res/drawable/ic_settings_battery_white.xml b/packages/overlays/IconPackCircularSettingsOverlay/res/drawable/ic_settings_battery_white.xml index d91afadb7f40..780fa2ee4241 100644 --- a/packages/overlays/IconPackCircularSettingsOverlay/res/drawable/ic_settings_battery_white.xml +++ b/packages/overlays/IconPackCircularSettingsOverlay/res/drawable/ic_settings_battery_white.xml @@ -20,6 +20,6 @@ android:viewportWidth="24" android:width="24dp" > <path - android:fillColor="@android:color/white" + android:fillColor="?android:attr/colorPrimary" android:pathData="M14,4c0-0.55-0.45-1-1-1h-2c-0.55,0-1,0.45-1,1H9C7.34,4,6,5.34,6,7v12c0,1.66,1.34,3,3,3h6c1.66,0,3-1.34,3-3V7 c0-1.66-1.34-3-3-3H14z" /> </vector>
\ No newline at end of file diff --git a/packages/overlays/IconPackCircularSettingsOverlay/res/drawable/ic_settings_display_white.xml b/packages/overlays/IconPackCircularSettingsOverlay/res/drawable/ic_settings_display_white.xml index f52653497e20..8dabc535d4e8 100644 --- a/packages/overlays/IconPackCircularSettingsOverlay/res/drawable/ic_settings_display_white.xml +++ b/packages/overlays/IconPackCircularSettingsOverlay/res/drawable/ic_settings_display_white.xml @@ -20,9 +20,9 @@ android:viewportWidth="24" android:width="24dp" > <path - android:fillColor="@android:color/white" + android:fillColor="?android:attr/colorPrimary" android:pathData="M17,12c0-2.76-2.24-5-5-5v10C14.76,17,17,14.76,17,12z" /> <path - android:fillColor="@android:color/white" + android:fillColor="?android:attr/colorPrimary" android:pathData="M4,15.31V18c0,1.1,0.9,2,2,2h2.69l1.9,1.9c0.39,0.39,0.9,0.59,1.41,0.59s1.02-0.2,1.41-0.59l1.9-1.9H18c1.1,0,2-0.9,2-2 v-2.69l1.9-1.9c0.78-0.78,0.78-2.05,0-2.83L20,8.69V6c0-1.1-0.9-2-2-2h-2.69l-1.9-1.9c-0.39-0.39-0.9-0.59-1.41-0.59 s-1.02,0.2-1.41,0.59L8.69,4H6C4.9,4,4,4.9,4,6v2.69l-1.9,1.9c-0.78,0.78-0.78,2.05,0,2.83L4,15.31z M3.16,11.65l1.9-1.9L5.5,9.31 V8.69V6c0-0.28,0.22-0.5,0.5-0.5h2.69h0.62l0.44-0.44l1.9-1.9c0.13-0.13,0.28-0.15,0.35-0.15c0.08,0,0.23,0.02,0.35,0.15l1.9,1.9 l0.44,0.44h0.62H18c0.28,0,0.5,0.22,0.5,0.5v2.69v0.62l0.44,0.44l1.9,1.9c0.13,0.13,0.15,0.28,0.15,0.35s-0.02,0.23-0.15,0.35 l-1.9,1.9l-0.44,0.44v0.62V18c0,0.28-0.22,0.5-0.5,0.5h-2.69h-0.62l-0.44,0.44l-1.9,1.9c-0.13,0.13-0.28,0.15-0.35,0.15 c-0.08,0-0.23-0.02-0.35-0.15l-1.9-1.9L9.31,18.5H8.69H6c-0.28,0-0.5-0.22-0.5-0.5v-2.69v-0.62l-0.44-0.44l-1.9-1.9 C3.04,12.23,3.02,12.08,3.02,12S3.04,11.77,3.16,11.65z" /> </vector>
\ No newline at end of file diff --git a/packages/overlays/IconPackCircularSettingsOverlay/res/drawable/ic_settings_location.xml b/packages/overlays/IconPackCircularSettingsOverlay/res/drawable/ic_settings_location.xml index 213b01b04eef..32234a1a28f1 100644 --- a/packages/overlays/IconPackCircularSettingsOverlay/res/drawable/ic_settings_location.xml +++ b/packages/overlays/IconPackCircularSettingsOverlay/res/drawable/ic_settings_location.xml @@ -20,9 +20,9 @@ android:viewportWidth="24" android:width="24dp" > <path - android:fillColor="@android:color/white" + android:fillColor="?android:attr/colorPrimary" android:pathData="M12,21.5c0,0,7-5.34,7-11.25c0-4-3.13-7.25-7-7.25c-3.87,0-7,3.25-7,7.25C5,16.16,12,21.5,12,21.5z M12,4.5 c3.03,0,5.5,2.58,5.5,5.75c0,3.91-3.74,7.72-5.51,9.29C9.9,17.68,6.5,13.89,6.5,10.25C6.5,7.08,8.97,4.5,12,4.5z" /> <path - android:fillColor="@android:color/white" + android:fillColor="?android:attr/colorPrimary" android:pathData="M15,10c0-1.66-1.34-3-3-3c-1.66,0-3,1.34-3,3c0,1.66,1.34,3,3,3C13.66,13,15,11.66,15,10z M10.5,10 c0-0.83,0.67-1.5,1.5-1.5s1.5,0.67,1.5,1.5s-0.67,1.5-1.5,1.5S10.5,10.83,10.5,10z" /> </vector>
\ No newline at end of file diff --git a/packages/overlays/IconPackCircularSettingsOverlay/res/drawable/ic_settings_privacy.xml b/packages/overlays/IconPackCircularSettingsOverlay/res/drawable/ic_settings_privacy.xml index ce4c1a45a66c..86b9a1d99f6a 100644 --- a/packages/overlays/IconPackCircularSettingsOverlay/res/drawable/ic_settings_privacy.xml +++ b/packages/overlays/IconPackCircularSettingsOverlay/res/drawable/ic_settings_privacy.xml @@ -20,12 +20,12 @@ android:viewportWidth="24" android:width="24dp" > <path - android:fillColor="@android:color/white" + android:fillColor="?android:attr/colorPrimary" android:pathData="M12,16.5c-3.74,0-6.89-1.9-8.37-5c1.47-3.1,4.62-5,8.37-5c3.53,0,6.52,1.71,8.08,4.5h1.7C20.09,7.3,16.35,5,12,5 C7.45,5,3.57,7.51,2,11.5C3.57,15.49,7.45,18,12,18c1.41,0,2.76-0.24,4-0.7v-1.62C14.79,16.21,13.44,16.5,12,16.5z" /> <path - android:fillColor="@android:color/white" + android:fillColor="?android:attr/colorPrimary" android:pathData="M12,8c-1.93,0-3.5,1.57-3.5,3.5S10.07,15,12,15s3.5-1.57,3.5-3.5S13.93,8,12,8z M12,13.5c-1.1,0-2-0.9-2-2s0.9-2,2-2 c1.1,0,2,0.9,2,2S13.1,13.5,12,13.5z" /> <path - android:fillColor="@android:color/white" + android:fillColor="?android:attr/colorPrimary" android:pathData="M22,14c0-1.1-0.9-2-2-2c-1.1,0-2,0.9-2,2c0,0.37,0,0.7,0,1h-1v4c0,0.55,0.45,1,1,1h4c0.55,0,1-0.45,1-1v-4h-1 C22,14.65,22,14.28,22,14z M19,14c0-0.55,0.45-1,1-1s1,0.45,1,1v1h-2V14z" /> </vector>
\ No newline at end of file diff --git a/packages/overlays/IconPackCircularSettingsOverlay/res/drawable/ic_settings_security_white.xml b/packages/overlays/IconPackCircularSettingsOverlay/res/drawable/ic_settings_security_white.xml index 612611593793..6fc58fa2533a 100644 --- a/packages/overlays/IconPackCircularSettingsOverlay/res/drawable/ic_settings_security_white.xml +++ b/packages/overlays/IconPackCircularSettingsOverlay/res/drawable/ic_settings_security_white.xml @@ -20,9 +20,9 @@ android:viewportWidth="24" android:width="24dp" > <path - android:fillColor="@android:color/white" + android:fillColor="?android:attr/colorPrimary" android:pathData="M11.25,14.79v1.46c0,0.41,0.34,0.75,0.75,0.75s0.75-0.34,0.75-0.75v-1.46c0.45-0.26,0.75-0.74,0.75-1.29 c0-0.83-0.67-1.5-1.5-1.5s-1.5,0.67-1.5,1.5C10.5,14.05,10.8,14.53,11.25,14.79z" /> <path - android:fillColor="@android:color/white" + android:fillColor="?android:attr/colorPrimary" android:pathData="M19,2.5c1.35,0,2.5,1.18,2.5,2.57c0,0.41,0.34,0.75,0.75,0.75S23,5.48,23,5.07C23,2.86,21.17,1,19,1s-4,1.86-4,4.07V8H5v10 c0,1.66,1.34,3,3,3h8c1.66,0,3-1.34,3-3V8h-2.5V5.07C16.5,3.68,17.65,2.5,19,2.5z M17.5,18c0,0.83-0.67,1.5-1.5,1.5H8 c-0.83,0-1.5-0.67-1.5-1.5V9.5h11V18z" /> </vector>
\ No newline at end of file diff --git a/packages/overlays/IconPackCircularSettingsOverlay/res/drawable/ic_settings_system_dashboard_white.xml b/packages/overlays/IconPackCircularSettingsOverlay/res/drawable/ic_settings_system_dashboard_white.xml index 4adc9ce2923f..67ddf46439e6 100644 --- a/packages/overlays/IconPackCircularSettingsOverlay/res/drawable/ic_settings_system_dashboard_white.xml +++ b/packages/overlays/IconPackCircularSettingsOverlay/res/drawable/ic_settings_system_dashboard_white.xml @@ -20,12 +20,12 @@ android:viewportWidth="24" android:width="24dp" > <path - android:fillColor="@android:color/white" + android:fillColor="?android:attr/colorPrimary" android:pathData="M11.99,2C6.47,2,2,6.48,2,12c0,5.52,4.47,10,9.99,10C17.52,22,22,17.52,22,12C22,6.48,17.52,2,11.99,2z M11.99,20.5 c-4.68,0-8.49-3.81-8.49-8.5c0-4.69,3.81-8.5,8.49-8.5c4.69,0,8.51,3.81,8.51,8.5C20.5,16.69,16.68,20.5,11.99,20.5z" /> <path - android:fillColor="@android:color/white" + android:fillColor="?android:attr/colorPrimary" android:pathData="M12,10.5c-0.41,0-0.75,0.34-0.75,0.75v5c0,0.41,0.34,0.75,0.75,0.75s0.75-0.34,0.75-0.75v-5 C12.75,10.84,12.41,10.5,12,10.5z" /> <path - android:fillColor="@android:color/white" + android:fillColor="?android:attr/colorPrimary" android:pathData="M 12 7 C 12.5522847498 7 13 7.44771525017 13 8 C 13 8.55228474983 12.5522847498 9 12 9 C 11.4477152502 9 11 8.55228474983 11 8 C 11 7.44771525017 11.4477152502 7 12 7 Z" /> </vector>
\ No newline at end of file diff --git a/packages/overlays/IconPackCircularSettingsOverlay/res/drawable/ic_settings_wireless.xml b/packages/overlays/IconPackCircularSettingsOverlay/res/drawable/ic_settings_wireless.xml index d9203d2d0af3..91670fca1be0 100644 --- a/packages/overlays/IconPackCircularSettingsOverlay/res/drawable/ic_settings_wireless.xml +++ b/packages/overlays/IconPackCircularSettingsOverlay/res/drawable/ic_settings_wireless.xml @@ -21,15 +21,15 @@ android:viewportWidth="24" android:width="24dp" > <path - android:fillColor="@android:color/white" + android:fillColor="?android:attr/colorPrimary" android:pathData="M 12 17 C 12.8284271247 17 13.5 17.6715728753 13.5 18.5 C 13.5 19.3284271247 12.8284271247 20 12 20 C 11.1715728753 20 10.5 19.3284271247 10.5 18.5 C 10.5 17.6715728753 11.1715728753 17 12 17 Z" /> <path - android:fillColor="@android:color/white" + android:fillColor="?android:attr/colorPrimary" android:pathData="M19.42,11.84c-0.19,0-0.38-0.07-0.53-0.22C17.05,9.77,14.6,8.75,12,8.75s-5.05,1.02-6.89,2.86 c-0.29,0.29-0.77,0.29-1.06,0c-0.29-0.29-0.29-0.77,0-1.06C6.17,8.43,9,7.25,12,7.25s5.83,1.17,7.95,3.3 c0.29,0.29,0.29,0.77,0,1.06C19.8,11.76,19.61,11.84,19.42,11.84z" /> <path - android:fillColor="@android:color/white" + android:fillColor="?android:attr/colorPrimary" android:pathData="M22.61,8.65c-0.19,0-0.38-0.07-0.53-0.22C19.38,5.74,15.81,4.25,12,4.25S4.62,5.74,1.92,8.43c-0.29,0.29-0.77,0.29-1.06,0 s-0.29-0.77,0-1.06C3.84,4.39,7.79,2.75,12,2.75s8.16,1.64,11.14,4.61c0.29,0.29,0.29,0.77,0,1.06 C22.99,8.57,22.8,8.65,22.61,8.65z" /> <path - android:fillColor="@android:color/white" + android:fillColor="?android:attr/colorPrimary" android:pathData="M16.25,15c-0.19,0-0.38-0.07-0.53-0.22c-1-0.99-2.32-1.53-3.73-1.53s-2.73,0.54-3.73,1.53c-0.29,0.29-0.77,0.29-1.06-0.01 s-0.29-0.77,0.01-1.06c1.28-1.27,2.98-1.96,4.78-1.96s3.5,0.7,4.78,1.96c0.29,0.29,0.3,0.77,0.01,1.06 C16.64,14.93,16.45,15,16.25,15z" /> </vector>
\ No newline at end of file diff --git a/packages/overlays/IconPackCircularSettingsOverlay/res/drawable/ic_storage_white.xml b/packages/overlays/IconPackCircularSettingsOverlay/res/drawable/ic_storage_white.xml index cf9db6817903..807c3bf3e533 100644 --- a/packages/overlays/IconPackCircularSettingsOverlay/res/drawable/ic_storage_white.xml +++ b/packages/overlays/IconPackCircularSettingsOverlay/res/drawable/ic_storage_white.xml @@ -20,21 +20,21 @@ android:viewportWidth="24" android:width="24dp" > <path - android:fillColor="@android:color/white" + android:fillColor="?android:attr/colorPrimary" android:pathData="M21,17c0-1.1-0.9-2-2-2H5c-1.1,0-2,0.9-2,2v3h18V17z M19.5,18.5h-15V17c0-0.28,0.22-0.5,0.5-0.5h14 c0.28,0,0.5,0.22,0.5,0.5V18.5z" /> <path - android:fillColor="@android:color/white" + android:fillColor="?android:attr/colorPrimary" android:pathData="M21,5c0-1.1-0.9-2-2-2H5C3.9,3,3,3.9,3,5v3h18V5z M19.5,6.5h-15V5c0-0.28,0.22-0.5,0.5-0.5h14c0.28,0,0.5,0.22,0.5,0.5V6.5 z" /> <path - android:fillColor="@android:color/white" + android:fillColor="?android:attr/colorPrimary" android:pathData="M21,11c0-1.1-0.9-2-2-2H5c-1.1,0-2,0.9-2,2v3h18V11z M19.5,12.5h-15V11c0-0.28,0.22-0.5,0.5-0.5h14 c0.28,0,0.5,0.22,0.5,0.5V12.5z" /> <path - android:fillColor="@android:color/white" + android:fillColor="?android:attr/colorPrimary" android:pathData="M 6.01 4.75 C 6.42421356237 4.75 6.76 5.08578643763 6.76 5.5 C 6.76 5.91421356237 6.42421356237 6.25 6.01 6.25 C 5.59578643763 6.25 5.26 5.91421356237 5.26 5.5 C 5.26 5.08578643763 5.59578643763 4.75 6.01 4.75 Z" /> <path - android:fillColor="@android:color/white" + android:fillColor="?android:attr/colorPrimary" android:pathData="M 6.01 10.75 C 6.42421356237 10.75 6.76 11.0857864376 6.76 11.5 C 6.76 11.9142135624 6.42421356237 12.25 6.01 12.25 C 5.59578643763 12.25 5.26 11.9142135624 5.26 11.5 C 5.26 11.0857864376 5.59578643763 10.75 6.01 10.75 Z" /> <path - android:fillColor="@android:color/white" + android:fillColor="?android:attr/colorPrimary" android:pathData="M 6.01 16.75 C 6.42421356237 16.75 6.76 17.0857864376 6.76 17.5 C 6.76 17.9142135624 6.42421356237 18.25 6.01 18.25 C 5.59578643763 18.25 5.26 17.9142135624 5.26 17.5 C 5.26 17.0857864376 5.59578643763 16.75 6.01 16.75 Z" /> </vector>
\ No newline at end of file diff --git a/packages/overlays/IconPackCircularSettingsOverlay/res/drawable/ic_volume_up_24dp.xml b/packages/overlays/IconPackCircularSettingsOverlay/res/drawable/ic_volume_up_24dp.xml index ace87e03c8c8..1a0613712df5 100644 --- a/packages/overlays/IconPackCircularSettingsOverlay/res/drawable/ic_volume_up_24dp.xml +++ b/packages/overlays/IconPackCircularSettingsOverlay/res/drawable/ic_volume_up_24dp.xml @@ -20,12 +20,12 @@ android:viewportWidth="24" android:width="24dp" > <path - android:fillColor="@android:color/white" + android:fillColor="?android:attr/colorPrimary" android:pathData="M14.44,13.56c-0.38,0.17-0.54,0.62-0.37,0.99c0.13,0.28,0.4,0.44,0.68,0.44c0.1,0,0.21-0.02,0.31-0.07 C16.26,14.37,17,13.25,17,12c0-1.25-0.74-2.37-1.93-2.93c-0.37-0.17-0.82-0.01-1,0.37c-0.17,0.38-0.01,0.82,0.36,1 c0.66,0.3,1.07,0.9,1.07,1.57S15.09,13.26,14.44,13.56z" /> <path - android:fillColor="@android:color/white" + android:fillColor="?android:attr/colorPrimary" android:pathData="M14.59,17.42c-0.4,0.09-0.66,0.49-0.57,0.9c0.08,0.35,0.39,0.59,0.73,0.59c0.05,0,0.11-0.01,0.16-0.02 c3.29-0.74,5.59-3.57,5.59-6.89s-2.3-6.15-5.59-6.89c-0.41-0.08-0.81,0.17-0.9,0.57s0.16,0.8,0.57,0.9C17.19,7.16,19,9.39,19,12 S17.19,16.84,14.59,17.42z" /> <path - android:fillColor="@android:color/white" + android:fillColor="?android:attr/colorPrimary" android:pathData="M7,15l4.15,4.15c0.1,0.1,0.23,0.15,0.35,0.15c0.26,0,0.5-0.2,0.5-0.5V5.21c0-0.3-0.25-0.5-0.5-0.5 c-0.12,0-0.25,0.05-0.35,0.15L7,9H5c-1.1,0-2,0.9-2,2v2c0,1.1,0.9,2,2,2H7z M4.5,13v-2c0-0.28,0.22-0.5,0.5-0.5h2.62l2.88-2.88 v8.76L7.62,13.5H5C4.72,13.5,4.5,13.28,4.5,13z" /> </vector>
\ No newline at end of file diff --git a/packages/overlays/IconPackFilledSettingsOverlay/res/drawable/ic_apps.xml b/packages/overlays/IconPackFilledSettingsOverlay/res/drawable/ic_apps.xml index 60b511622b0d..74b13fd4c1ec 100644 --- a/packages/overlays/IconPackFilledSettingsOverlay/res/drawable/ic_apps.xml +++ b/packages/overlays/IconPackFilledSettingsOverlay/res/drawable/ic_apps.xml @@ -20,30 +20,30 @@ android:viewportWidth="24" android:width="24dp" > <path - android:fillColor="@android:color/white" + android:fillColor="?android:attr/colorPrimary" android:pathData="M7.5,4h-3C4.22,4,4,4.22,4,4.5v3C4,7.78,4.22,8,4.5,8h3C7.78,8,8,7.78,8,7.5v-3C8,4.22,7.78,4,7.5,4z" /> <path - android:fillColor="@android:color/white" + android:fillColor="?android:attr/colorPrimary" android:pathData="M13.5,4h-3C10.22,4,10,4.22,10,4.5v3C10,7.78,10.22,8,10.5,8h3C13.78,8,14,7.78,14,7.5v-3C14,4.22,13.78,4,13.5,4z" /> <path - android:fillColor="@android:color/white" + android:fillColor="?android:attr/colorPrimary" android:pathData="M19.5,4h-3C16.22,4,16,4.22,16,4.5v3C16,7.78,16.22,8,16.5,8h3C19.78,8,20,7.78,20,7.5v-3C20,4.22,19.78,4,19.5,4z" /> <path - android:fillColor="@android:color/white" + android:fillColor="?android:attr/colorPrimary" android:pathData="M7.5,10h-3C4.22,10,4,10.22,4,10.5v3C4,13.78,4.22,14,4.5,14h3C7.78,14,8,13.78,8,13.5v-3C8,10.22,7.78,10,7.5,10z" /> <path - android:fillColor="@android:color/white" + android:fillColor="?android:attr/colorPrimary" android:pathData="M13.5,10h-3c-0.28,0-0.5,0.22-0.5,0.5v3c0,0.28,0.22,0.5,0.5,0.5h3c0.28,0,0.5-0.22,0.5-0.5v-3C14,10.22,13.78,10,13.5,10 z" /> <path - android:fillColor="@android:color/white" + android:fillColor="?android:attr/colorPrimary" android:pathData="M19.5,10h-3c-0.28,0-0.5,0.22-0.5,0.5v3c0,0.28,0.22,0.5,0.5,0.5h3c0.28,0,0.5-0.22,0.5-0.5v-3C20,10.22,19.78,10,19.5,10 z" /> <path - android:fillColor="@android:color/white" + android:fillColor="?android:attr/colorPrimary" android:pathData="M7.5,16h-3C4.22,16,4,16.22,4,16.5v3C4,19.78,4.22,20,4.5,20h3C7.78,20,8,19.78,8,19.5v-3C8,16.22,7.78,16,7.5,16z" /> <path - android:fillColor="@android:color/white" + android:fillColor="?android:attr/colorPrimary" android:pathData="M13.5,16h-3c-0.28,0-0.5,0.22-0.5,0.5v3c0,0.28,0.22,0.5,0.5,0.5h3c0.28,0,0.5-0.22,0.5-0.5v-3C14,16.22,13.78,16,13.5,16 z" /> <path - android:fillColor="@android:color/white" + android:fillColor="?android:attr/colorPrimary" android:pathData="M19.5,16h-3c-0.28,0-0.5,0.22-0.5,0.5v3c0,0.28,0.22,0.5,0.5,0.5h3c0.28,0,0.5-0.22,0.5-0.5v-3C20,16.22,19.78,16,19.5,16 z" /> </vector>
\ No newline at end of file diff --git a/packages/overlays/IconPackFilledSettingsOverlay/res/drawable/ic_devices_other.xml b/packages/overlays/IconPackFilledSettingsOverlay/res/drawable/ic_devices_other.xml index a451ef831e78..33a4b29aba00 100644 --- a/packages/overlays/IconPackFilledSettingsOverlay/res/drawable/ic_devices_other.xml +++ b/packages/overlays/IconPackFilledSettingsOverlay/res/drawable/ic_devices_other.xml @@ -21,12 +21,12 @@ android:viewportWidth="24" android:width="24dp" > <path - android:fillColor="@android:color/white" + android:fillColor="?android:attr/colorPrimary" android:pathData="M6,18H3V6h17c0.55,0,1-0.45,1-1s-0.45-1-1-1H3C1.9,4,1,4.9,1,6v12c0,1.1,0.9,2,2,2h3c0.55,0,1-0.45,1-1S6.55,18,6,18z" /> <path - android:fillColor="@android:color/white" + android:fillColor="?android:attr/colorPrimary" android:pathData="M13,12H9v1.78C8.39,14.33,8,15.11,8,16s0.39,1.67,1,2.22V20h4v-1.78c0.61-0.55,1-1.34,1-2.22s-0.39-1.67-1-2.22V12z M11,17.5c-0.83,0-1.5-0.67-1.5-1.5c0-0.83,0.67-1.5,1.5-1.5s1.5,0.67,1.5,1.5C12.5,16.83,11.83,17.5,11,17.5z" /> <path - android:fillColor="@android:color/white" + android:fillColor="?android:attr/colorPrimary" android:pathData="M22,8h-6c-0.5,0-1,0.5-1,1v10c0,0.5,0.5,1,1,1h6c0.5,0,1-0.5,1-1V9C23,8.5,22.5,8,22,8z M21,18h-4v-8h4V18z" /> </vector>
\ No newline at end of file diff --git a/packages/overlays/IconPackFilledSettingsOverlay/res/drawable/ic_help.xml b/packages/overlays/IconPackFilledSettingsOverlay/res/drawable/ic_help.xml index 25bb10306d25..42854a4777d2 100644 --- a/packages/overlays/IconPackFilledSettingsOverlay/res/drawable/ic_help.xml +++ b/packages/overlays/IconPackFilledSettingsOverlay/res/drawable/ic_help.xml @@ -20,6 +20,6 @@ android:viewportWidth="24" android:width="24dp" > <path - android:fillColor="@android:color/white" + android:fillColor="?android:attr/colorPrimary" android:pathData="M12,22c5.52,0,10-4.48,10-10c0-5.52-4.48-10-10-10S2,6.48,2,12C2,17.52,6.48,22,12,22z M12,18.96 c-0.69,0-1.25-0.56-1.25-1.25c0-0.69,0.56-1.25,1.25-1.25s1.25,0.56,1.25,1.25C13.25,18.4,12.69,18.96,12,18.96z M8.16,7.92 c0.63-2.25,2.91-3.38,5.05-2.74c1.71,0.51,2.84,2.16,2.78,3.95c-0.07,2.44-2.49,2.61-2.92,5.06c-0.09,0.52-0.59,0.87-1.13,0.79 c-0.57-0.08-0.94-0.66-0.83-1.23c0.52-2.61,2.66-2.84,2.87-4.5c0.12-0.96-0.42-1.87-1.34-2.17c-1.04-0.33-2.21,0.16-2.55,1.37 C9.97,8.9,9.57,9.19,9.12,9.19C8.46,9.19,7.99,8.56,8.16,7.92z" /> </vector>
\ No newline at end of file diff --git a/packages/overlays/IconPackFilledSettingsOverlay/res/drawable/ic_phone_info.xml b/packages/overlays/IconPackFilledSettingsOverlay/res/drawable/ic_phone_info.xml index 682125974fab..e932b9b88a0f 100644 --- a/packages/overlays/IconPackFilledSettingsOverlay/res/drawable/ic_phone_info.xml +++ b/packages/overlays/IconPackFilledSettingsOverlay/res/drawable/ic_phone_info.xml @@ -20,12 +20,12 @@ android:viewportWidth="24" android:width="24dp" > <path - android:fillColor="@android:color/white" + android:fillColor="?android:attr/colorPrimary" android:pathData="M17,1.01L7,1C5.9,1,5,1.9,5,3v18c0,1.1,0.9,2,2,2h10c1.1,0,2-0.9,2-2V3C19,1.9,18.1,1.01,17,1.01z M17,19H7V5h10V19z" /> <path - android:fillColor="@android:color/white" + android:fillColor="?android:attr/colorPrimary" android:pathData="M 12 7 C 12.6903559373 7 13.25 7.55964406271 13.25 8.25 C 13.25 8.94035593729 12.6903559373 9.5 12 9.5 C 11.3096440627 9.5 10.75 8.94035593729 10.75 8.25 C 10.75 7.55964406271 11.3096440627 7 12 7 Z" /> <path - android:fillColor="@android:color/white" + android:fillColor="?android:attr/colorPrimary" android:pathData="M12,11c-0.55,0-1,0.4-1,0.9v4.21c0,0.5,0.45,0.9,1,0.9s1-0.4,1-0.9V11.9C13,11.4,12.55,11,12,11z" /> </vector>
\ No newline at end of file diff --git a/packages/overlays/IconPackFilledSettingsOverlay/res/drawable/ic_settings_accessibility.xml b/packages/overlays/IconPackFilledSettingsOverlay/res/drawable/ic_settings_accessibility.xml index 762b7d4979bf..db456387f369 100644 --- a/packages/overlays/IconPackFilledSettingsOverlay/res/drawable/ic_settings_accessibility.xml +++ b/packages/overlays/IconPackFilledSettingsOverlay/res/drawable/ic_settings_accessibility.xml @@ -20,18 +20,18 @@ android:viewportWidth="24" android:width="24dp" > <path - android:fillColor="@android:color/white" + android:fillColor="?android:attr/colorPrimary" android:pathData="M20.76,5.02l-0.02-0.06c-0.13-0.53-0.67-0.85-1.2-0.73C17.16,4.77,14.49,5,12,5S6.84,4.77,4.46,4.24 c-0.54-0.12-1.07,0.19-1.2,0.73L3.24,5.02C3.11,5.56,3.43,6.12,3.97,6.24C5.59,6.61,7.34,6.86,9,7v12c0,0.55,0.45,1,1,1 s1-0.45,1-1v-5h2v5c0,0.55,0.45,1,1,1s1-0.45,1-1V7c1.66-0.14,3.41-0.39,5.03-0.76C20.57,6.12,20.89,5.56,20.76,5.02z" /> <path - android:fillColor="@android:color/white" + android:fillColor="?android:attr/colorPrimary" android:pathData="M 12 0 C 13.1045694997 0 14 0.895430500338 14 2 C 14 3.10456949966 13.1045694997 4 12 4 C 10.8954305003 4 10 3.10456949966 10 2 C 10 0.895430500338 10.8954305003 0 12 0 Z" /> <path - android:fillColor="@android:color/white" + android:fillColor="?android:attr/colorPrimary" android:pathData="M 12 22 C 12.5522847498 22 13 22.4477152502 13 23 C 13 23.5522847498 12.5522847498 24 12 24 C 11.4477152502 24 11 23.5522847498 11 23 C 11 22.4477152502 11.4477152502 22 12 22 Z" /> <path - android:fillColor="@android:color/white" + android:fillColor="?android:attr/colorPrimary" android:pathData="M 16 22 C 16.5522847498 22 17 22.4477152502 17 23 C 17 23.5522847498 16.5522847498 24 16 24 C 15.4477152502 24 15 23.5522847498 15 23 C 15 22.4477152502 15.4477152502 22 16 22 Z" /> <path - android:fillColor="@android:color/white" + android:fillColor="?android:attr/colorPrimary" android:pathData="M 8 22 C 8.55228474983 22 9 22.4477152502 9 23 C 9 23.5522847498 8.55228474983 24 8 24 C 7.44771525017 24 7 23.5522847498 7 23 C 7 22.4477152502 7.44771525017 22 8 22 Z" /> </vector>
\ No newline at end of file diff --git a/packages/overlays/IconPackFilledSettingsOverlay/res/drawable/ic_settings_accounts.xml b/packages/overlays/IconPackFilledSettingsOverlay/res/drawable/ic_settings_accounts.xml index 5409d0da8316..0d4a244e7040 100644 --- a/packages/overlays/IconPackFilledSettingsOverlay/res/drawable/ic_settings_accounts.xml +++ b/packages/overlays/IconPackFilledSettingsOverlay/res/drawable/ic_settings_accounts.xml @@ -20,6 +20,6 @@ android:viewportWidth="24" android:width="24dp" > <path - android:fillColor="@android:color/white" + android:fillColor="?android:attr/colorPrimary" android:pathData="M19,3H5C3.9,3,3,3.9,3,5v14c0,1.1,0.9,2,2,2h14c1.1,0,2-0.9,2-2V5C21,3.9,20.1,3,19,3z M12,6c1.93,0,3.5,1.57,3.5,3.5 S13.93,13,12,13s-3.5-1.57-3.5-3.5S10.07,6,12,6z M19,19H5v-1.36c0-0.74,0.41-1.44,1.07-1.77C7.24,15.28,9.3,14.5,12,14.5 s4.76,0.78,5.93,1.37C18.59,16.2,19,16.9,19,17.64V19z" /> </vector>
\ No newline at end of file diff --git a/packages/overlays/IconPackFilledSettingsOverlay/res/drawable/ic_settings_battery_white.xml b/packages/overlays/IconPackFilledSettingsOverlay/res/drawable/ic_settings_battery_white.xml index 0fea7ae6ee31..bb1138831db7 100644 --- a/packages/overlays/IconPackFilledSettingsOverlay/res/drawable/ic_settings_battery_white.xml +++ b/packages/overlays/IconPackFilledSettingsOverlay/res/drawable/ic_settings_battery_white.xml @@ -20,6 +20,6 @@ android:viewportWidth="24" android:width="24dp" > <path - android:fillColor="@android:color/white" + android:fillColor="?android:attr/colorPrimary" android:pathData="M10,2v2H8.33C7.6,4,7,4.6,7,5.33v15.33C7,21.4,7.6,22,8.33,22h7.33C16.4,22,17,21.4,17,20.67V5.33C17,4.6,16.4,4,15.67,4 H14V2H10z" /> </vector>
\ No newline at end of file diff --git a/packages/overlays/IconPackFilledSettingsOverlay/res/drawable/ic_settings_display_white.xml b/packages/overlays/IconPackFilledSettingsOverlay/res/drawable/ic_settings_display_white.xml index b75787b3b745..2c931e48fa15 100644 --- a/packages/overlays/IconPackFilledSettingsOverlay/res/drawable/ic_settings_display_white.xml +++ b/packages/overlays/IconPackFilledSettingsOverlay/res/drawable/ic_settings_display_white.xml @@ -20,6 +20,6 @@ android:viewportWidth="24" android:width="24dp" > <path - android:fillColor="@android:color/white" + android:fillColor="?android:attr/colorPrimary" android:pathData="M4,15.3V19c0,0.55,0.45,1,1,1h3.69l2.6,2.6c0.39,0.39,1.02,0.39,1.41,0l2.6-2.6H19c0.55,0,1-0.45,1-1v-3.69l2.6-2.6 c0.39-0.39,0.39-1.02,0-1.41L20,8.69V5c0-0.55-0.45-1-1-1h-3.69l-2.6-2.6c-0.39-0.39-1.02-0.39-1.41,0L8.69,4H5C4.45,4,4,4.45,4,5 v3.69l-2.6,2.6c-0.39,0.39-0.39,1.02,0,1.41L4,15.3z M12,6c3.31,0,6,2.69,6,6s-2.69,6-6,6V6z" /> </vector>
\ No newline at end of file diff --git a/packages/overlays/IconPackFilledSettingsOverlay/res/drawable/ic_settings_location.xml b/packages/overlays/IconPackFilledSettingsOverlay/res/drawable/ic_settings_location.xml index ecab3a3d9b21..8732ea5cb99d 100644 --- a/packages/overlays/IconPackFilledSettingsOverlay/res/drawable/ic_settings_location.xml +++ b/packages/overlays/IconPackFilledSettingsOverlay/res/drawable/ic_settings_location.xml @@ -20,6 +20,6 @@ android:viewportWidth="24" android:width="24dp" > <path - android:fillColor="@android:color/white" + android:fillColor="?android:attr/colorPrimary" android:pathData="M12.77,21.11C14.58,18.92,19,13.17,19,9c0-3.87-3.13-7-7-7S5,5.13,5,9c0,4.17,4.42,9.92,6.24,12.11 C11.64,21.59,12.37,21.59,12.77,21.11z M9.5,9c0-1.38,1.12-2.5,2.5-2.5s2.5,1.12,2.5,2.5c0,1.38-1.12,2.5-2.5,2.5S9.5,10.38,9.5,9z" /> </vector>
\ No newline at end of file diff --git a/packages/overlays/IconPackFilledSettingsOverlay/res/drawable/ic_settings_privacy.xml b/packages/overlays/IconPackFilledSettingsOverlay/res/drawable/ic_settings_privacy.xml index 4404530a9580..2ebdc8f3fc96 100644 --- a/packages/overlays/IconPackFilledSettingsOverlay/res/drawable/ic_settings_privacy.xml +++ b/packages/overlays/IconPackFilledSettingsOverlay/res/drawable/ic_settings_privacy.xml @@ -20,12 +20,12 @@ android:viewportWidth="24" android:width="24dp" > <path - android:fillColor="@android:color/white" + android:fillColor="?android:attr/colorPrimary" android:pathData="M16.48,11.7c0.74-0.44,1.6-0.7,2.52-0.7h3.78C20.93,6.88,16.81,4,12,4C7,4,2.73,7.11,1,11.5C2.73,15.89,7,19,12,19 c0.68,0,1.35-0.06,2-0.17V16c0-0.18,0.03-0.34,0.05-0.51C13.43,15.8,12.74,16,12,16c-2.49,0-4.5-2.01-4.5-4.5 C7.5,9.01,9.51,7,12,7s4.5,2.01,4.5,4.5C16.5,11.57,16.48,11.64,16.48,11.7z" /> <path - android:fillColor="@android:color/white" + android:fillColor="?android:attr/colorPrimary" android:pathData="M 12 9 C 13.3807118746 9 14.5 10.1192881254 14.5 11.5 C 14.5 12.8807118746 13.3807118746 14 12 14 C 10.6192881254 14 9.5 12.8807118746 9.5 11.5 C 9.5 10.1192881254 10.6192881254 9 12 9 Z" /> <path - android:fillColor="@android:color/white" + android:fillColor="?android:attr/colorPrimary" android:pathData="M22,16v-0.5c0-1.4-1.1-2.5-2.5-2.5S17,14.1,17,15.5V16c-0.5,0-1,0.5-1,1v4c0,0.5,0.5,1,1,1h5c0.5,0,1-0.5,1-1v-4 C23,16.5,22.5,16,22,16z M20.5,16h-2v-0.5c0-0.53,0.47-1,1-1s1,0.47,1,1V16z" /> </vector>
\ No newline at end of file diff --git a/packages/overlays/IconPackFilledSettingsOverlay/res/drawable/ic_settings_security_white.xml b/packages/overlays/IconPackFilledSettingsOverlay/res/drawable/ic_settings_security_white.xml index 86147c26c469..ecaed01a945f 100644 --- a/packages/overlays/IconPackFilledSettingsOverlay/res/drawable/ic_settings_security_white.xml +++ b/packages/overlays/IconPackFilledSettingsOverlay/res/drawable/ic_settings_security_white.xml @@ -20,6 +20,6 @@ android:viewportWidth="24" android:width="24dp" > <path - android:fillColor="@android:color/white" + android:fillColor="?android:attr/colorPrimary" android:pathData="M18,1c-2.21,0-4,1.79-4,4v3H6c-1.1,0-2,0.9-2,2v10c0,1.1,0.9,2,2,2h12c1.1,0,2-0.9,2-2V10c0-1.1-0.9-2-2-2h-2V5 c0-1.1,0.9-2,2-2s2,0.9,2,2c0,0.55,0.45,1,1,1s1-0.45,1-1C22,2.79,20.21,1,18,1z M12,17c-1.1,0-2-0.9-2-2c0-1.1,0.9-2,2-2 s2,0.9,2,2C14,16.1,13.1,17,12,17z" /> </vector>
\ No newline at end of file diff --git a/packages/overlays/IconPackFilledSettingsOverlay/res/drawable/ic_settings_system_dashboard_white.xml b/packages/overlays/IconPackFilledSettingsOverlay/res/drawable/ic_settings_system_dashboard_white.xml index ce233b7f2614..659a926bf7a3 100644 --- a/packages/overlays/IconPackFilledSettingsOverlay/res/drawable/ic_settings_system_dashboard_white.xml +++ b/packages/overlays/IconPackFilledSettingsOverlay/res/drawable/ic_settings_system_dashboard_white.xml @@ -20,6 +20,6 @@ android:viewportWidth="24" android:width="24dp" > <path - android:fillColor="@android:color/white" + android:fillColor="?android:attr/colorPrimary" android:pathData="M12,2C6.48,2,2,6.48,2,12c0,5.52,4.48,10,10,10s10-4.48,10-10C22,6.48,17.52,2,12,2z M13,17c0,0.55-0.45,1-1,1s-1-0.45-1-1 v-5c0-0.55,0.45-1,1-1s1,0.45,1,1V17z M12,9.25c-0.69,0-1.25-0.56-1.25-1.25S11.31,6.75,12,6.75S13.25,7.31,13.25,8 S12.69,9.25,12,9.25z" /> </vector>
\ No newline at end of file diff --git a/packages/overlays/IconPackFilledSettingsOverlay/res/drawable/ic_settings_wireless.xml b/packages/overlays/IconPackFilledSettingsOverlay/res/drawable/ic_settings_wireless.xml index 92dbd29cad7e..6e80d1349fc2 100644 --- a/packages/overlays/IconPackFilledSettingsOverlay/res/drawable/ic_settings_wireless.xml +++ b/packages/overlays/IconPackFilledSettingsOverlay/res/drawable/ic_settings_wireless.xml @@ -21,12 +21,12 @@ android:viewportWidth="24" android:width="24dp" > <path - android:fillColor="@android:color/white" + android:fillColor="?android:attr/colorPrimary" android:pathData="M11.29,19.29c0.39,0.39,1.03,0.4,1.42,0L14,18c0.47-0.47,0.38-1.28-0.22-1.58C13.25,16.15,12.64,16,12,16 c-0.64,0-1.24,0.15-1.77,0.41c-0.59,0.29-0.69,1.11-0.22,1.58L11.29,19.29z" /> <path - android:fillColor="@android:color/white" + android:fillColor="?android:attr/colorPrimary" android:pathData="M17.6,14.39l0.71-0.71c0.42-0.42,0.39-1.12-0.08-1.5C16.52,10.82,14.35,10,12,10c-2.34,0-4.5,0.81-6.21,2.17 c-0.47,0.37-0.51,1.07-0.09,1.49l0.71,0.71c0.35,0.36,0.92,0.39,1.32,0.08C8.91,13.54,10.39,13,12,13c1.61,0,3.1,0.55,4.29,1.47 C16.69,14.78,17.25,14.75,17.6,14.39z" /> <path - android:fillColor="@android:color/white" + android:fillColor="?android:attr/colorPrimary" android:pathData="M21.83,10.16l0.71-0.71c0.42-0.42,0.38-1.09-0.06-1.48C19.68,5.5,16.01,4,12,4C8.01,4,4.36,5.49,1.56,7.94 C1.12,8.33,1.08,9,1.49,9.41l0.71,0.71c0.37,0.37,0.96,0.4,1.35,0.06C5.81,8.2,8.77,7,12,7c3.25,0,6.22,1.22,8.49,3.22 C20.88,10.56,21.47,10.53,21.83,10.16z" /> </vector>
\ No newline at end of file diff --git a/packages/overlays/IconPackFilledSettingsOverlay/res/drawable/ic_storage_white.xml b/packages/overlays/IconPackFilledSettingsOverlay/res/drawable/ic_storage_white.xml index 03780db829d9..9eb336c44141 100644 --- a/packages/overlays/IconPackFilledSettingsOverlay/res/drawable/ic_storage_white.xml +++ b/packages/overlays/IconPackFilledSettingsOverlay/res/drawable/ic_storage_white.xml @@ -20,12 +20,12 @@ android:viewportWidth="24" android:width="24dp" > <path - android:fillColor="@android:color/white" + android:fillColor="?android:attr/colorPrimary" android:pathData="M19,10H5c-1.1,0-2,0.9-2,2c0,1.1,0.9,2,2,2h14c1.1,0,2-0.9,2-2C21,10.9,20.1,10,19,10z M6,13.1c-0.61,0-1.1-0.49-1.1-1.1 s0.49-1.1,1.1-1.1s1.1,0.49,1.1,1.1S6.61,13.1,6,13.1z" /> <path - android:fillColor="@android:color/white" + android:fillColor="?android:attr/colorPrimary" android:pathData="M21,18c0-1.1-0.9-2-2-2H5c-1.1,0-2,0.9-2,2c0,1.1,0.9,2,2,2h14C20.1,20,21,19.1,21,18z M6,19.1c-0.61,0-1.1-0.49-1.1-1.1 s0.49-1.1,1.1-1.1s1.1,0.49,1.1,1.1S6.61,19.1,6,19.1z" /> <path - android:fillColor="@android:color/white" + android:fillColor="?android:attr/colorPrimary" android:pathData="M19,4H5C3.9,4,3,4.9,3,6c0,1.1,0.9,2,2,2h14c1.1,0,2-0.9,2-2C21,4.9,20.1,4,19,4z M6,7.1C5.39,7.1,4.9,6.61,4.9,6 S5.39,4.9,6,4.9S7.1,5.39,7.1,6S6.61,7.1,6,7.1z" /> </vector>
\ No newline at end of file diff --git a/packages/overlays/IconPackFilledSettingsOverlay/res/drawable/ic_volume_up_24dp.xml b/packages/overlays/IconPackFilledSettingsOverlay/res/drawable/ic_volume_up_24dp.xml index 863df7173c1f..b94035100e40 100644 --- a/packages/overlays/IconPackFilledSettingsOverlay/res/drawable/ic_volume_up_24dp.xml +++ b/packages/overlays/IconPackFilledSettingsOverlay/res/drawable/ic_volume_up_24dp.xml @@ -20,12 +20,12 @@ android:viewportWidth="24" android:width="24dp" > <path - android:fillColor="@android:color/white" + android:fillColor="?android:attr/colorPrimary" android:pathData="M20.4,8.78c-0.91-2.39-2.8-4.27-5.18-5.18C14.63,3.37,14,3.83,14,4.46v0.19c0,0.38,0.25,0.71,0.61,0.85 C17.18,6.54,19,9.06,19,12s-1.82,5.46-4.39,6.5C14.25,18.64,14,18.97,14,19.35v0.19c0,0.63,0.63,1.08,1.22,0.86 C19.86,18.62,22.18,13.42,20.4,8.78z" /> <path - android:fillColor="@android:color/white" + android:fillColor="?android:attr/colorPrimary" android:pathData="M16.5,12c0-1.71-0.97-3.27-2.5-4.03v8.05C15.48,15.29,16.5,13.77,16.5,12z" /> <path - android:fillColor="@android:color/white" + android:fillColor="?android:attr/colorPrimary" android:pathData="M10.29,5.7L7,9H4c-0.55,0-1,0.45-1,1v4c0,0.55,0.45,1,1,1h3l3.29,3.29c0.63,0.63,1.71,0.18,1.71-0.71V6.41 C12,5.52,10.92,5.07,10.29,5.7z" /> </vector>
\ No newline at end of file diff --git a/packages/overlays/IconPackKaiSettingsOverlay/res/drawable/ic_apps.xml b/packages/overlays/IconPackKaiSettingsOverlay/res/drawable/ic_apps.xml index 58999d0925c2..5b1850f5f15d 100644 --- a/packages/overlays/IconPackKaiSettingsOverlay/res/drawable/ic_apps.xml +++ b/packages/overlays/IconPackKaiSettingsOverlay/res/drawable/ic_apps.xml @@ -14,13 +14,13 @@ limitations under the License. --> <vector android:height="24dp" android:viewportHeight="24" android:viewportWidth="24" android:width="24dp" xmlns:android="http://schemas.android.com/apk/res/android"> - <path android:fillColor="@android:color/white" android:pathData="M 6 4 C 7.10456949966 4 8 4.89543050034 8 6 C 8 7.10456949966 7.10456949966 8 6 8 C 4.89543050034 8 4 7.10456949966 4 6 C 4 4.89543050034 4.89543050034 4 6 4 Z"/> - <path android:fillColor="@android:color/white" android:pathData="M 12 4 C 13.1045694997 4 14 4.89543050034 14 6 C 14 7.10456949966 13.1045694997 8 12 8 C 10.8954305003 8 10 7.10456949966 10 6 C 10 4.89543050034 10.8954305003 4 12 4 Z"/> - <path android:fillColor="@android:color/white" android:pathData="M 18 4 C 19.1045694997 4 20 4.89543050034 20 6 C 20 7.10456949966 19.1045694997 8 18 8 C 16.8954305003 8 16 7.10456949966 16 6 C 16 4.89543050034 16.8954305003 4 18 4 Z"/> - <path android:fillColor="@android:color/white" android:pathData="M 6 10 C 7.10456949966 10 8 10.8954305003 8 12 C 8 13.1045694997 7.10456949966 14 6 14 C 4.89543050034 14 4 13.1045694997 4 12 C 4 10.8954305003 4.89543050034 10 6 10 Z"/> - <path android:fillColor="@android:color/white" android:pathData="M 12 10 C 13.1045694997 10 14 10.8954305003 14 12 C 14 13.1045694997 13.1045694997 14 12 14 C 10.8954305003 14 10 13.1045694997 10 12 C 10 10.8954305003 10.8954305003 10 12 10 Z"/> - <path android:fillColor="@android:color/white" android:pathData="M 18 10 C 19.1045694997 10 20 10.8954305003 20 12 C 20 13.1045694997 19.1045694997 14 18 14 C 16.8954305003 14 16 13.1045694997 16 12 C 16 10.8954305003 16.8954305003 10 18 10 Z"/> - <path android:fillColor="@android:color/white" android:pathData="M 6 16 C 7.10456949966 16 8 16.8954305003 8 18 C 8 19.1045694997 7.10456949966 20 6 20 C 4.89543050034 20 4 19.1045694997 4 18 C 4 16.8954305003 4.89543050034 16 6 16 Z"/> - <path android:fillColor="@android:color/white" android:pathData="M 12 16 C 13.1045694997 16 14 16.8954305003 14 18 C 14 19.1045694997 13.1045694997 20 12 20 C 10.8954305003 20 10 19.1045694997 10 18 C 10 16.8954305003 10.8954305003 16 12 16 Z"/> - <path android:fillColor="@android:color/white" android:pathData="M 18 16 C 19.1045694997 16 20 16.8954305003 20 18 C 20 19.1045694997 19.1045694997 20 18 20 C 16.8954305003 20 16 19.1045694997 16 18 C 16 16.8954305003 16.8954305003 16 18 16 Z"/> + <path android:fillColor="?android:attr/colorPrimary" android:pathData="M 6 4 C 7.10456949966 4 8 4.89543050034 8 6 C 8 7.10456949966 7.10456949966 8 6 8 C 4.89543050034 8 4 7.10456949966 4 6 C 4 4.89543050034 4.89543050034 4 6 4 Z"/> + <path android:fillColor="?android:attr/colorPrimary" android:pathData="M 12 4 C 13.1045694997 4 14 4.89543050034 14 6 C 14 7.10456949966 13.1045694997 8 12 8 C 10.8954305003 8 10 7.10456949966 10 6 C 10 4.89543050034 10.8954305003 4 12 4 Z"/> + <path android:fillColor="?android:attr/colorPrimary" android:pathData="M 18 4 C 19.1045694997 4 20 4.89543050034 20 6 C 20 7.10456949966 19.1045694997 8 18 8 C 16.8954305003 8 16 7.10456949966 16 6 C 16 4.89543050034 16.8954305003 4 18 4 Z"/> + <path android:fillColor="?android:attr/colorPrimary" android:pathData="M 6 10 C 7.10456949966 10 8 10.8954305003 8 12 C 8 13.1045694997 7.10456949966 14 6 14 C 4.89543050034 14 4 13.1045694997 4 12 C 4 10.8954305003 4.89543050034 10 6 10 Z"/> + <path android:fillColor="?android:attr/colorPrimary" android:pathData="M 12 10 C 13.1045694997 10 14 10.8954305003 14 12 C 14 13.1045694997 13.1045694997 14 12 14 C 10.8954305003 14 10 13.1045694997 10 12 C 10 10.8954305003 10.8954305003 10 12 10 Z"/> + <path android:fillColor="?android:attr/colorPrimary" android:pathData="M 18 10 C 19.1045694997 10 20 10.8954305003 20 12 C 20 13.1045694997 19.1045694997 14 18 14 C 16.8954305003 14 16 13.1045694997 16 12 C 16 10.8954305003 16.8954305003 10 18 10 Z"/> + <path android:fillColor="?android:attr/colorPrimary" android:pathData="M 6 16 C 7.10456949966 16 8 16.8954305003 8 18 C 8 19.1045694997 7.10456949966 20 6 20 C 4.89543050034 20 4 19.1045694997 4 18 C 4 16.8954305003 4.89543050034 16 6 16 Z"/> + <path android:fillColor="?android:attr/colorPrimary" android:pathData="M 12 16 C 13.1045694997 16 14 16.8954305003 14 18 C 14 19.1045694997 13.1045694997 20 12 20 C 10.8954305003 20 10 19.1045694997 10 18 C 10 16.8954305003 10.8954305003 16 12 16 Z"/> + <path android:fillColor="?android:attr/colorPrimary" android:pathData="M 18 16 C 19.1045694997 16 20 16.8954305003 20 18 C 20 19.1045694997 19.1045694997 20 18 20 C 16.8954305003 20 16 19.1045694997 16 18 C 16 16.8954305003 16.8954305003 16 18 16 Z"/> </vector>
\ No newline at end of file diff --git a/packages/overlays/IconPackKaiSettingsOverlay/res/drawable/ic_devices_other.xml b/packages/overlays/IconPackKaiSettingsOverlay/res/drawable/ic_devices_other.xml index fa91107cfe7e..954ff328dbd2 100644 --- a/packages/overlays/IconPackKaiSettingsOverlay/res/drawable/ic_devices_other.xml +++ b/packages/overlays/IconPackKaiSettingsOverlay/res/drawable/ic_devices_other.xml @@ -14,7 +14,7 @@ limitations under the License. --> <vector android:autoMirrored="true" android:height="24dp" android:viewportHeight="24" android:viewportWidth="24" android:width="24dp" xmlns:android="http://schemas.android.com/apk/res/android"> - <path android:fillColor="@android:color/white" android:pathData="M2.51,17.74V6.27c0-0.41,0.34-0.75,0.75-0.75H21v-1.5H3.26c-1.24,0-2.25,1.01-2.25,2.25v11.46c0,1.24,1.01,2.25,2.25,2.25 H7v-1.5H3.26C2.85,18.49,2.51,18.15,2.51,17.74z"/> - <path android:fillColor="@android:color/white" android:pathData="M20.75,8h-3.5C16.01,8,15,9.01,15,10.25v7.5c0,1.24,1.01,2.25,2.25,2.25h3.5c1.24,0,2.25-1.01,2.25-2.25v-7.5 C23,9.01,21.99,8,20.75,8z M21.5,17.75c0,0.41-0.34,0.75-0.75,0.75h-3.5c-0.41,0-0.75-0.34-0.75-0.75v-7.5 c0-0.41,0.34-0.75,0.75-0.75h3.5c0.41,0,0.75,0.34,0.75,0.75V17.75z"/> - <path android:fillColor="@android:color/white" android:pathData="M13,13.84v-1.09c0-0.41-0.34-0.75-0.75-0.75h-2.5C9.34,12,9,12.34,9,12.75v1.09C8.54,14.25,8.18,14.92,8.18,16 c0,1.09,0.36,1.75,0.82,2.16v1.09C9,19.66,9.34,20,9.75,20h2.5c0.41,0,0.75-0.34,0.75-0.75v-1.09c0.46-0.41,0.82-1.08,0.82-2.16 C13.82,14.91,13.46,14.25,13,13.84z M11,17.25C10.03,17.26,9.75,16.9,9.75,16c0-0.9,0.3-1.25,1.25-1.25 c0.95-0.01,1.25,0.33,1.25,1.25C12.25,16.84,11.98,17.25,11,17.25z"/> + <path android:fillColor="?android:attr/colorPrimary" android:pathData="M2.51,17.74V6.27c0-0.41,0.34-0.75,0.75-0.75H21v-1.5H3.26c-1.24,0-2.25,1.01-2.25,2.25v11.46c0,1.24,1.01,2.25,2.25,2.25 H7v-1.5H3.26C2.85,18.49,2.51,18.15,2.51,17.74z"/> + <path android:fillColor="?android:attr/colorPrimary" android:pathData="M20.75,8h-3.5C16.01,8,15,9.01,15,10.25v7.5c0,1.24,1.01,2.25,2.25,2.25h3.5c1.24,0,2.25-1.01,2.25-2.25v-7.5 C23,9.01,21.99,8,20.75,8z M21.5,17.75c0,0.41-0.34,0.75-0.75,0.75h-3.5c-0.41,0-0.75-0.34-0.75-0.75v-7.5 c0-0.41,0.34-0.75,0.75-0.75h3.5c0.41,0,0.75,0.34,0.75,0.75V17.75z"/> + <path android:fillColor="?android:attr/colorPrimary" android:pathData="M13,13.84v-1.09c0-0.41-0.34-0.75-0.75-0.75h-2.5C9.34,12,9,12.34,9,12.75v1.09C8.54,14.25,8.18,14.92,8.18,16 c0,1.09,0.36,1.75,0.82,2.16v1.09C9,19.66,9.34,20,9.75,20h2.5c0.41,0,0.75-0.34,0.75-0.75v-1.09c0.46-0.41,0.82-1.08,0.82-2.16 C13.82,14.91,13.46,14.25,13,13.84z M11,17.25C10.03,17.26,9.75,16.9,9.75,16c0-0.9,0.3-1.25,1.25-1.25 c0.95-0.01,1.25,0.33,1.25,1.25C12.25,16.84,11.98,17.25,11,17.25z"/> </vector>
\ No newline at end of file diff --git a/packages/overlays/IconPackKaiSettingsOverlay/res/drawable/ic_help.xml b/packages/overlays/IconPackKaiSettingsOverlay/res/drawable/ic_help.xml index 8ab01a638545..89b80319a539 100644 --- a/packages/overlays/IconPackKaiSettingsOverlay/res/drawable/ic_help.xml +++ b/packages/overlays/IconPackKaiSettingsOverlay/res/drawable/ic_help.xml @@ -14,7 +14,7 @@ limitations under the License. --> <vector android:height="24dp" android:viewportHeight="24" android:viewportWidth="24" android:width="24dp" xmlns:android="http://schemas.android.com/apk/res/android"> - <path android:fillColor="@android:color/white" android:pathData="M13.73,6.41c-0.51-0.27-1.09-0.4-1.74-0.4c-0.87,0-1.58,0.24-2.13,0.73C9.31,7.22,8.91,7.78,8.67,8.42l1.29,0.54 c0.16-0.46,0.41-0.84,0.73-1.16c0.32-0.31,0.76-0.47,1.3-0.47c0.6,0,1.07,0.16,1.41,0.48c0.34,0.32,0.51,0.75,0.51,1.27 c0,0.39-0.1,0.73-0.29,1.01c-0.19,0.28-0.51,0.61-0.94,1.01c-0.54,0.5-0.91,0.93-1.11,1.29c-0.21,0.36-0.31,0.81-0.31,1.36v0.79 h1.43v-0.69c0-0.39,0.08-0.74,0.25-1.03c0.16-0.29,0.43-0.61,0.79-0.93c0.47-0.43,0.85-0.85,1.15-1.27 c0.3-0.42,0.45-0.93,0.45-1.53c0-0.58-0.14-1.1-0.42-1.57C14.63,7.05,14.24,6.68,13.73,6.41z"/> - <path android:fillColor="@android:color/white" android:pathData="M11.98,15.96c-0.03,0-1-0.06-1,1c0,1.06,0.96,1,1,1c0.03,0,1,0.06,1-1C12.98,15.9,12.02,15.96,11.98,15.96z"/> - <path android:fillColor="@android:color/white" android:pathData="M12,2C4.41,2,2,6.9,2,12c0,5.09,2.35,10,10,10c7.59,0,10-4.9,10-10C22,6.91,19.65,2,12,2z M12,20.5 c-2.64,0.05-8.5-0.59-8.5-8.5c0-7.91,5.88-8.55,8.5-8.5c2.64-0.05,8.5,0.59,8.5,8.5C20.5,19.91,14.62,20.55,12,20.5z"/> + <path android:fillColor="?android:attr/colorPrimary" android:pathData="M13.73,6.41c-0.51-0.27-1.09-0.4-1.74-0.4c-0.87,0-1.58,0.24-2.13,0.73C9.31,7.22,8.91,7.78,8.67,8.42l1.29,0.54 c0.16-0.46,0.41-0.84,0.73-1.16c0.32-0.31,0.76-0.47,1.3-0.47c0.6,0,1.07,0.16,1.41,0.48c0.34,0.32,0.51,0.75,0.51,1.27 c0,0.39-0.1,0.73-0.29,1.01c-0.19,0.28-0.51,0.61-0.94,1.01c-0.54,0.5-0.91,0.93-1.11,1.29c-0.21,0.36-0.31,0.81-0.31,1.36v0.79 h1.43v-0.69c0-0.39,0.08-0.74,0.25-1.03c0.16-0.29,0.43-0.61,0.79-0.93c0.47-0.43,0.85-0.85,1.15-1.27 c0.3-0.42,0.45-0.93,0.45-1.53c0-0.58-0.14-1.1-0.42-1.57C14.63,7.05,14.24,6.68,13.73,6.41z"/> + <path android:fillColor="?android:attr/colorPrimary" android:pathData="M11.98,15.96c-0.03,0-1-0.06-1,1c0,1.06,0.96,1,1,1c0.03,0,1,0.06,1-1C12.98,15.9,12.02,15.96,11.98,15.96z"/> + <path android:fillColor="?android:attr/colorPrimary" android:pathData="M12,2C4.41,2,2,6.9,2,12c0,5.09,2.35,10,10,10c7.59,0,10-4.9,10-10C22,6.91,19.65,2,12,2z M12,20.5 c-2.64,0.05-8.5-0.59-8.5-8.5c0-7.91,5.88-8.55,8.5-8.5c2.64-0.05,8.5,0.59,8.5,8.5C20.5,19.91,14.62,20.55,12,20.5z"/> </vector>
\ No newline at end of file diff --git a/packages/overlays/IconPackKaiSettingsOverlay/res/drawable/ic_phone_info.xml b/packages/overlays/IconPackKaiSettingsOverlay/res/drawable/ic_phone_info.xml index 2edda9c50a2c..e2246ce7fb16 100644 --- a/packages/overlays/IconPackKaiSettingsOverlay/res/drawable/ic_phone_info.xml +++ b/packages/overlays/IconPackKaiSettingsOverlay/res/drawable/ic_phone_info.xml @@ -14,7 +14,7 @@ limitations under the License. --> <vector android:height="24dp" android:viewportHeight="24" android:viewportWidth="24" android:width="24dp" xmlns:android="http://schemas.android.com/apk/res/android"> - <path android:fillColor="@android:color/white" android:pathData="M16.75,1h-9.5C6.01,1,5,2.01,5,3.25v17.5C5,21.99,6.01,23,7.25,23h9.5c1.24,0,2.25-1.01,2.25-2.25V3.25 C19,2.01,17.99,1,16.75,1z M7.25,2.5h9.5c0.41,0,0.75,0.34,0.75,0.75v1h-11v-1C6.5,2.84,6.84,2.5,7.25,2.5z M17.5,5.75v12.5h-11 V5.75H17.5z M16.75,21.5h-9.5c-0.41,0-0.75-0.34-0.75-0.75v-1h11v1C17.5,21.16,17.16,21.5,16.75,21.5z"/> - <path android:fillColor="@android:color/white" android:pathData="M 11.25 11 H 12.75 V 16 H 11.25 V 11 Z"/> - <path android:fillColor="@android:color/white" android:pathData="M 12 8 C 12.4142135624 8 12.75 8.33578643763 12.75 8.75 C 12.75 9.16421356237 12.4142135624 9.5 12 9.5 C 11.5857864376 9.5 11.25 9.16421356237 11.25 8.75 C 11.25 8.33578643763 11.5857864376 8 12 8 Z"/> + <path android:fillColor="?android:attr/colorPrimary" android:pathData="M16.75,1h-9.5C6.01,1,5,2.01,5,3.25v17.5C5,21.99,6.01,23,7.25,23h9.5c1.24,0,2.25-1.01,2.25-2.25V3.25 C19,2.01,17.99,1,16.75,1z M7.25,2.5h9.5c0.41,0,0.75,0.34,0.75,0.75v1h-11v-1C6.5,2.84,6.84,2.5,7.25,2.5z M17.5,5.75v12.5h-11 V5.75H17.5z M16.75,21.5h-9.5c-0.41,0-0.75-0.34-0.75-0.75v-1h11v1C17.5,21.16,17.16,21.5,16.75,21.5z"/> + <path android:fillColor="?android:attr/colorPrimary" android:pathData="M 11.25 11 H 12.75 V 16 H 11.25 V 11 Z"/> + <path android:fillColor="?android:attr/colorPrimary" android:pathData="M 12 8 C 12.4142135624 8 12.75 8.33578643763 12.75 8.75 C 12.75 9.16421356237 12.4142135624 9.5 12 9.5 C 11.5857864376 9.5 11.25 9.16421356237 11.25 8.75 C 11.25 8.33578643763 11.5857864376 8 12 8 Z"/> </vector>
\ No newline at end of file diff --git a/packages/overlays/IconPackKaiSettingsOverlay/res/drawable/ic_settings_accessibility.xml b/packages/overlays/IconPackKaiSettingsOverlay/res/drawable/ic_settings_accessibility.xml index 900a3a6cb082..a92595bea8ac 100644 --- a/packages/overlays/IconPackKaiSettingsOverlay/res/drawable/ic_settings_accessibility.xml +++ b/packages/overlays/IconPackKaiSettingsOverlay/res/drawable/ic_settings_accessibility.xml @@ -14,9 +14,9 @@ limitations under the License. --> <vector android:height="24dp" android:viewportHeight="24" android:viewportWidth="24" android:width="24dp" xmlns:android="http://schemas.android.com/apk/res/android"> - <path android:fillColor="@android:color/white" android:pathData="M 8 22 C 8.55228474983 22 9 22.4477152502 9 23 C 9 23.5522847498 8.55228474983 24 8 24 C 7.44771525017 24 7 23.5522847498 7 23 C 7 22.4477152502 7.44771525017 22 8 22 Z"/> - <path android:fillColor="@android:color/white" android:pathData="M 12 22 C 12.5522847498 22 13 22.4477152502 13 23 C 13 23.5522847498 12.5522847498 24 12 24 C 11.4477152502 24 11 23.5522847498 11 23 C 11 22.4477152502 11.4477152502 22 12 22 Z"/> - <path android:fillColor="@android:color/white" android:pathData="M 16 22 C 16.5522847498 22 17 22.4477152502 17 23 C 17 23.5522847498 16.5522847498 24 16 24 C 15.4477152502 24 15 23.5522847498 15 23 C 15 22.4477152502 15.4477152502 22 16 22 Z"/> - <path android:fillColor="@android:color/white" android:pathData="M 12 0 C 13.1045694997 0 14 0.895430500338 14 2 C 14 3.10456949966 13.1045694997 4 12 4 C 10.8954305003 4 10 3.10456949966 10 2 C 10 0.895430500338 10.8954305003 0 12 0 Z"/> - <path android:fillColor="@android:color/white" android:pathData="M15,20V7c2-0.17,4.14-0.5,6-1l-0.5-2c-2.61,0.7-5.67,1-8.5,1S6.11,4.7,3.5,4L3,6c1.86,0.5,4,0.83,6,1v13h2v-6h2v6H15z"/> + <path android:fillColor="?android:attr/colorPrimary" android:pathData="M 8 22 C 8.55228474983 22 9 22.4477152502 9 23 C 9 23.5522847498 8.55228474983 24 8 24 C 7.44771525017 24 7 23.5522847498 7 23 C 7 22.4477152502 7.44771525017 22 8 22 Z"/> + <path android:fillColor="?android:attr/colorPrimary" android:pathData="M 12 22 C 12.5522847498 22 13 22.4477152502 13 23 C 13 23.5522847498 12.5522847498 24 12 24 C 11.4477152502 24 11 23.5522847498 11 23 C 11 22.4477152502 11.4477152502 22 12 22 Z"/> + <path android:fillColor="?android:attr/colorPrimary" android:pathData="M 16 22 C 16.5522847498 22 17 22.4477152502 17 23 C 17 23.5522847498 16.5522847498 24 16 24 C 15.4477152502 24 15 23.5522847498 15 23 C 15 22.4477152502 15.4477152502 22 16 22 Z"/> + <path android:fillColor="?android:attr/colorPrimary" android:pathData="M 12 0 C 13.1045694997 0 14 0.895430500338 14 2 C 14 3.10456949966 13.1045694997 4 12 4 C 10.8954305003 4 10 3.10456949966 10 2 C 10 0.895430500338 10.8954305003 0 12 0 Z"/> + <path android:fillColor="?android:attr/colorPrimary" android:pathData="M15,20V7c2-0.17,4.14-0.5,6-1l-0.5-2c-2.61,0.7-5.67,1-8.5,1S6.11,4.7,3.5,4L3,6c1.86,0.5,4,0.83,6,1v13h2v-6h2v6H15z"/> </vector>
\ No newline at end of file diff --git a/packages/overlays/IconPackKaiSettingsOverlay/res/drawable/ic_settings_accounts.xml b/packages/overlays/IconPackKaiSettingsOverlay/res/drawable/ic_settings_accounts.xml index 0a7ec3fa3daa..b17efd1fe3ca 100644 --- a/packages/overlays/IconPackKaiSettingsOverlay/res/drawable/ic_settings_accounts.xml +++ b/packages/overlays/IconPackKaiSettingsOverlay/res/drawable/ic_settings_accounts.xml @@ -14,6 +14,6 @@ limitations under the License. --> <vector android:height="24dp" android:viewportHeight="24" android:viewportWidth="24" android:width="24dp" xmlns:android="http://schemas.android.com/apk/res/android"> - <path android:fillColor="@android:color/white" android:pathData="M18.75,3H5.25C4.01,3,3,4.01,3,5.25v13.5C3,19.99,4.01,21,5.25,21h13.5c1.24,0,2.25-1.01,2.25-2.25V5.25 C21,4.01,19.99,3,18.75,3z M18.75,19.5H5.25c-0.35,0-0.64-0.25-0.72-0.58C4.9,18.6,7.23,16.4,12,16.51 c4.77-0.11,7.11,2.1,7.47,2.41C19.39,19.25,19.1,19.5,18.75,19.5z M19.5,17.03c-3.24-2.22-7.05-2.02-7.5-2.02 c-0.46,0-4.27-0.19-7.5,2.02V5.25c0-0.41,0.34-0.75,0.75-0.75h13.5c0.41,0,0.75,0.34,0.75,0.75V17.03z"/> - <path android:fillColor="@android:color/white" android:pathData="M12,6C9.33,6,8.5,7.73,8.5,9.5c0,1.78,0.83,3.5,3.5,3.5c2.67,0,3.5-1.73,3.5-3.5C15.5,7.72,14.67,6,12,6z M12,11.5 c-0.43,0.01-2,0.13-2-2c0-2.14,1.58-2,2-2c0.43-0.01,2-0.13,2,2C14,11.64,12.42,11.5,12,11.5z"/> + <path android:fillColor="?android:attr/colorPrimary" android:pathData="M18.75,3H5.25C4.01,3,3,4.01,3,5.25v13.5C3,19.99,4.01,21,5.25,21h13.5c1.24,0,2.25-1.01,2.25-2.25V5.25 C21,4.01,19.99,3,18.75,3z M18.75,19.5H5.25c-0.35,0-0.64-0.25-0.72-0.58C4.9,18.6,7.23,16.4,12,16.51 c4.77-0.11,7.11,2.1,7.47,2.41C19.39,19.25,19.1,19.5,18.75,19.5z M19.5,17.03c-3.24-2.22-7.05-2.02-7.5-2.02 c-0.46,0-4.27-0.19-7.5,2.02V5.25c0-0.41,0.34-0.75,0.75-0.75h13.5c0.41,0,0.75,0.34,0.75,0.75V17.03z"/> + <path android:fillColor="?android:attr/colorPrimary" android:pathData="M12,6C9.33,6,8.5,7.73,8.5,9.5c0,1.78,0.83,3.5,3.5,3.5c2.67,0,3.5-1.73,3.5-3.5C15.5,7.72,14.67,6,12,6z M12,11.5 c-0.43,0.01-2,0.13-2-2c0-2.14,1.58-2,2-2c0.43-0.01,2-0.13,2,2C14,11.64,12.42,11.5,12,11.5z"/> </vector>
\ No newline at end of file diff --git a/packages/overlays/IconPackKaiSettingsOverlay/res/drawable/ic_settings_battery_white.xml b/packages/overlays/IconPackKaiSettingsOverlay/res/drawable/ic_settings_battery_white.xml index cc80eb7e8b84..4af480634f85 100644 --- a/packages/overlays/IconPackKaiSettingsOverlay/res/drawable/ic_settings_battery_white.xml +++ b/packages/overlays/IconPackKaiSettingsOverlay/res/drawable/ic_settings_battery_white.xml @@ -14,5 +14,5 @@ limitations under the License. --> <vector android:height="24dp" android:viewportHeight="24" android:viewportWidth="24" android:width="24dp" xmlns:android="http://schemas.android.com/apk/res/android"> - <path android:fillColor="@android:color/white" android:pathData="M16,4h-1V2.5C15,2.22,14.78,2,14.5,2h-5C9.22,2,9,2.22,9,2.5V4H8C6.9,4,6,4.9,6,6v12c0,2.21,1.79,4,4,4h4 c2.21,0,4-1.79,4-4V6C18,4.9,17.1,4,16,4z"/> + <path android:fillColor="?android:attr/colorPrimary" android:pathData="M16,4h-1V2.5C15,2.22,14.78,2,14.5,2h-5C9.22,2,9,2.22,9,2.5V4H8C6.9,4,6,4.9,6,6v12c0,2.21,1.79,4,4,4h4 c2.21,0,4-1.79,4-4V6C18,4.9,17.1,4,16,4z"/> </vector>
\ No newline at end of file diff --git a/packages/overlays/IconPackKaiSettingsOverlay/res/drawable/ic_settings_display_white.xml b/packages/overlays/IconPackKaiSettingsOverlay/res/drawable/ic_settings_display_white.xml index d4d4174e0923..9eb8a0b0d2e8 100644 --- a/packages/overlays/IconPackKaiSettingsOverlay/res/drawable/ic_settings_display_white.xml +++ b/packages/overlays/IconPackKaiSettingsOverlay/res/drawable/ic_settings_display_white.xml @@ -14,6 +14,6 @@ limitations under the License. --> <vector android:height="24dp" android:viewportHeight="24" android:viewportWidth="24" android:width="24dp" xmlns:android="http://schemas.android.com/apk/res/android"> - <path android:fillColor="@android:color/white" android:pathData="M12,7V17c0.17,0,5,0.31,5-5C17,6.7,12.22,7,12,7z"/> - <path android:fillColor="@android:color/white" android:pathData="M22.78,11.47L20,8.69V4.75C20,4.34,19.66,4,19.25,4h-3.94l-2.78-2.78c-0.29-0.29-0.77-0.29-1.06,0L8.69,4H4.75 C4.34,4,4,4.34,4,4.75v3.94l-2.78,2.78c-0.29,0.29-0.29,0.77,0,1.06L4,15.31v3.94C4,19.66,4.34,20,4.75,20h3.94l2.78,2.78 C11.62,22.93,11.81,23,12,23s0.38-0.07,0.53-0.22L15.31,20h3.94c0.41,0,0.75-0.34,0.75-0.75v-3.94l2.78-2.78 C23.07,12.24,23.07,11.76,22.78,11.47z M18.72,14.47C18.58,14.61,18.5,14.8,18.5,15v3.5H15c-0.2,0-0.39,0.08-0.53,0.22L12,21.19 l-2.47-2.47C9.39,18.58,9.2,18.5,9,18.5H5.5V15c0-0.2-0.08-0.39-0.22-0.53L2.81,12l2.47-2.47C5.42,9.39,5.5,9.2,5.5,9V5.5H9 c0.2,0,0.39-0.08,0.53-0.22L12,2.81l2.47,2.47C14.61,5.42,14.8,5.5,15,5.5h3.5V9c0,0.2,0.08,0.39,0.22,0.53L21.19,12L18.72,14.47z"/> + <path android:fillColor="?android:attr/colorPrimary" android:pathData="M12,7V17c0.17,0,5,0.31,5-5C17,6.7,12.22,7,12,7z"/> + <path android:fillColor="?android:attr/colorPrimary" android:pathData="M22.78,11.47L20,8.69V4.75C20,4.34,19.66,4,19.25,4h-3.94l-2.78-2.78c-0.29-0.29-0.77-0.29-1.06,0L8.69,4H4.75 C4.34,4,4,4.34,4,4.75v3.94l-2.78,2.78c-0.29,0.29-0.29,0.77,0,1.06L4,15.31v3.94C4,19.66,4.34,20,4.75,20h3.94l2.78,2.78 C11.62,22.93,11.81,23,12,23s0.38-0.07,0.53-0.22L15.31,20h3.94c0.41,0,0.75-0.34,0.75-0.75v-3.94l2.78-2.78 C23.07,12.24,23.07,11.76,22.78,11.47z M18.72,14.47C18.58,14.61,18.5,14.8,18.5,15v3.5H15c-0.2,0-0.39,0.08-0.53,0.22L12,21.19 l-2.47-2.47C9.39,18.58,9.2,18.5,9,18.5H5.5V15c0-0.2-0.08-0.39-0.22-0.53L2.81,12l2.47-2.47C5.42,9.39,5.5,9.2,5.5,9V5.5H9 c0.2,0,0.39-0.08,0.53-0.22L12,2.81l2.47,2.47C14.61,5.42,14.8,5.5,15,5.5h3.5V9c0,0.2,0.08,0.39,0.22,0.53L21.19,12L18.72,14.47z"/> </vector>
\ No newline at end of file diff --git a/packages/overlays/IconPackKaiSettingsOverlay/res/drawable/ic_settings_location.xml b/packages/overlays/IconPackKaiSettingsOverlay/res/drawable/ic_settings_location.xml index 2d1de9490bba..d437035642bc 100644 --- a/packages/overlays/IconPackKaiSettingsOverlay/res/drawable/ic_settings_location.xml +++ b/packages/overlays/IconPackKaiSettingsOverlay/res/drawable/ic_settings_location.xml @@ -14,6 +14,6 @@ limitations under the License. --> <vector android:height="24dp" android:viewportHeight="24" android:viewportWidth="24" android:width="24dp" xmlns:android="http://schemas.android.com/apk/res/android"> - <path android:fillColor="@android:color/white" android:pathData="M12,6c-1.88,0-3,1.04-3,3c0,1.91,1.06,3,3,3c1.89,0,3-1.05,3-3C15,7.08,13.93,6,12,6z M12,10.5 c-1.15,0.01-1.5-0.47-1.5-1.5c0-1.06,0.37-1.5,1.5-1.5c1.15-0.01,1.5,0.47,1.5,1.5C13.5,10.09,13.1,10.5,12,10.5z"/> - <path android:fillColor="@android:color/white" android:pathData="M11.99,2C9.69,1.94,5,2.93,5,9c0,6.88,6.23,12.56,6.5,12.8c0.14,0.13,0.32,0.2,0.5,0.2s0.36-0.06,0.5-0.2 C12.77,21.56,19,15.88,19,9C19,2.87,14.3,1.96,11.99,2z M12,20.2C10.55,18.7,6.5,14.12,6.5,9c0-4.91,3.63-5.55,5.44-5.5 C16.9,3.34,17.5,7.14,17.5,9C17.5,14.13,13.45,18.7,12,20.2z"/> + <path android:fillColor="?android:attr/colorPrimary" android:pathData="M12,6c-1.88,0-3,1.04-3,3c0,1.91,1.06,3,3,3c1.89,0,3-1.05,3-3C15,7.08,13.93,6,12,6z M12,10.5 c-1.15,0.01-1.5-0.47-1.5-1.5c0-1.06,0.37-1.5,1.5-1.5c1.15-0.01,1.5,0.47,1.5,1.5C13.5,10.09,13.1,10.5,12,10.5z"/> + <path android:fillColor="?android:attr/colorPrimary" android:pathData="M11.99,2C9.69,1.94,5,2.93,5,9c0,6.88,6.23,12.56,6.5,12.8c0.14,0.13,0.32,0.2,0.5,0.2s0.36-0.06,0.5-0.2 C12.77,21.56,19,15.88,19,9C19,2.87,14.3,1.96,11.99,2z M12,20.2C10.55,18.7,6.5,14.12,6.5,9c0-4.91,3.63-5.55,5.44-5.5 C16.9,3.34,17.5,7.14,17.5,9C17.5,14.13,13.45,18.7,12,20.2z"/> </vector>
\ No newline at end of file diff --git a/packages/overlays/IconPackKaiSettingsOverlay/res/drawable/ic_settings_privacy.xml b/packages/overlays/IconPackKaiSettingsOverlay/res/drawable/ic_settings_privacy.xml index b8e5a7dd9957..28d111da49a9 100644 --- a/packages/overlays/IconPackKaiSettingsOverlay/res/drawable/ic_settings_privacy.xml +++ b/packages/overlays/IconPackKaiSettingsOverlay/res/drawable/ic_settings_privacy.xml @@ -14,7 +14,7 @@ limitations under the License. --> <vector android:height="24dp" android:viewportHeight="24" android:viewportWidth="24" android:width="24dp" xmlns:android="http://schemas.android.com/apk/res/android"> - <path android:fillColor="@android:color/white" android:pathData="M22.74,11.4C20.87,7.33,16.77,4.5,12,4.5S3.13,7.33,1.26,11.4c-0.17,0.38-0.17,0.83,0,1.21c1.87,4.07,5.97,6.9,10.74,6.9 c0.68,0,1.35-0.06,2-0.18v-1.55C13.35,17.91,12.68,18,12,18c-4.02,0-7.7-2.36-9.38-5.98C4.3,8.36,7.98,6,12,6s7.7,2.36,9.38,5.98 c-0.08,0.17-0.19,0.33-0.28,0.5c0.47,0.22,0.9,0.51,1.27,0.85c0.13-0.24,0.25-0.48,0.37-0.73C22.92,12.22,22.92,11.78,22.74,11.4 z"/> - <path android:fillColor="@android:color/white" android:pathData="M16.5,12c0-2.3-1.06-4.5-4.5-4.5c-3.43,0-4.5,2.22-4.5,4.5c0,2.3,1.06,4.5,4.5,4.5c0.83,0,1.52-0.14,2.09-0.36 c0.26-1.46,1.14-2.69,2.37-3.42C16.48,12.48,16.5,12.24,16.5,12z M12,15c-3.05,0.04-3-2.35-3-3c0-0.63-0.04-3.06,3-3 c3.05-0.04,3,2.35,3,3C15,12.63,15.04,15.06,12,15z"/> - <path android:fillColor="@android:color/white" android:pathData="M21,17v-1c0-1.1-0.9-2-2-2s-2,0.9-2,2v1c-0.55,0-1,0.45-1,1v3c0,0.55,0.45,1,1,1h4c0.55,0,1-0.45,1-1v-3 C22,17.45,21.55,17,21,17z M18.5,16c0-0.28,0.22-0.5,0.5-0.5s0.5,0.22,0.5,0.5v1h-1V16z"/> + <path android:fillColor="?android:attr/colorPrimary" android:pathData="M22.74,11.4C20.87,7.33,16.77,4.5,12,4.5S3.13,7.33,1.26,11.4c-0.17,0.38-0.17,0.83,0,1.21c1.87,4.07,5.97,6.9,10.74,6.9 c0.68,0,1.35-0.06,2-0.18v-1.55C13.35,17.91,12.68,18,12,18c-4.02,0-7.7-2.36-9.38-5.98C4.3,8.36,7.98,6,12,6s7.7,2.36,9.38,5.98 c-0.08,0.17-0.19,0.33-0.28,0.5c0.47,0.22,0.9,0.51,1.27,0.85c0.13-0.24,0.25-0.48,0.37-0.73C22.92,12.22,22.92,11.78,22.74,11.4 z"/> + <path android:fillColor="?android:attr/colorPrimary" android:pathData="M16.5,12c0-2.3-1.06-4.5-4.5-4.5c-3.43,0-4.5,2.22-4.5,4.5c0,2.3,1.06,4.5,4.5,4.5c0.83,0,1.52-0.14,2.09-0.36 c0.26-1.46,1.14-2.69,2.37-3.42C16.48,12.48,16.5,12.24,16.5,12z M12,15c-3.05,0.04-3-2.35-3-3c0-0.63-0.04-3.06,3-3 c3.05-0.04,3,2.35,3,3C15,12.63,15.04,15.06,12,15z"/> + <path android:fillColor="?android:attr/colorPrimary" android:pathData="M21,17v-1c0-1.1-0.9-2-2-2s-2,0.9-2,2v1c-0.55,0-1,0.45-1,1v3c0,0.55,0.45,1,1,1h4c0.55,0,1-0.45,1-1v-3 C22,17.45,21.55,17,21,17z M18.5,16c0-0.28,0.22-0.5,0.5-0.5s0.5,0.22,0.5,0.5v1h-1V16z"/> </vector>
\ No newline at end of file diff --git a/packages/overlays/IconPackKaiSettingsOverlay/res/drawable/ic_settings_security_white.xml b/packages/overlays/IconPackKaiSettingsOverlay/res/drawable/ic_settings_security_white.xml index 0475e33d5350..7f654c120583 100644 --- a/packages/overlays/IconPackKaiSettingsOverlay/res/drawable/ic_settings_security_white.xml +++ b/packages/overlays/IconPackKaiSettingsOverlay/res/drawable/ic_settings_security_white.xml @@ -14,6 +14,6 @@ limitations under the License. --> <vector android:height="24dp" android:viewportHeight="24" android:viewportWidth="24" android:width="24dp" xmlns:android="http://schemas.android.com/apk/res/android"> - <path android:fillColor="@android:color/white" android:pathData="M18.5,1C17.03,0.96,14,1.62,14,5.5v2.49H6.25C5.01,7.99,4,9,4,10.24v9.51C4,20.99,5.01,22,6.25,22h11.5 c1.24,0,2.25-1.01,2.25-2.25v-9.51c0-1.24-1.01-2.25-2.25-2.25H15.5V5.5c0-2.77,2-3.01,3.05-3c1.02-0.02,2.96,0.28,2.96,3V6H23 V5.5C23,1.6,19.97,0.96,18.5,1z M17.75,9.49c0.41,0,0.75,0.34,0.75,0.75v9.51c0,0.41-0.34,0.75-0.75,0.75H6.25 c-0.41,0-0.75-0.34-0.75-0.75v-9.51c0-0.41,0.34-0.75,0.75-0.75H17.75z"/> - <path android:fillColor="@android:color/white" android:pathData="M12,17.74c0.13,0,2.75,0.06,2.75-2.75c0-2.34-1.85-2.77-2.74-2.75c-0.89-0.02-2.76,0.4-2.76,2.75 C9.25,17.41,11.19,17.74,12,17.74z M12.03,13.74c1.32-0.06,1.22,1.22,1.22,1.25c0,0.89-0.42,1.26-1.25,1.25 c-0.81,0.01-1.25-0.34-1.25-1.25C10.75,14.15,11.12,13.73,12.03,13.74z"/> + <path android:fillColor="?android:attr/colorPrimary" android:pathData="M18.5,1C17.03,0.96,14,1.62,14,5.5v2.49H6.25C5.01,7.99,4,9,4,10.24v9.51C4,20.99,5.01,22,6.25,22h11.5 c1.24,0,2.25-1.01,2.25-2.25v-9.51c0-1.24-1.01-2.25-2.25-2.25H15.5V5.5c0-2.77,2-3.01,3.05-3c1.02-0.02,2.96,0.28,2.96,3V6H23 V5.5C23,1.6,19.97,0.96,18.5,1z M17.75,9.49c0.41,0,0.75,0.34,0.75,0.75v9.51c0,0.41-0.34,0.75-0.75,0.75H6.25 c-0.41,0-0.75-0.34-0.75-0.75v-9.51c0-0.41,0.34-0.75,0.75-0.75H17.75z"/> + <path android:fillColor="?android:attr/colorPrimary" android:pathData="M12,17.74c0.13,0,2.75,0.06,2.75-2.75c0-2.34-1.85-2.77-2.74-2.75c-0.89-0.02-2.76,0.4-2.76,2.75 C9.25,17.41,11.19,17.74,12,17.74z M12.03,13.74c1.32-0.06,1.22,1.22,1.22,1.25c0,0.89-0.42,1.26-1.25,1.25 c-0.81,0.01-1.25-0.34-1.25-1.25C10.75,14.15,11.12,13.73,12.03,13.74z"/> </vector>
\ No newline at end of file diff --git a/packages/overlays/IconPackKaiSettingsOverlay/res/drawable/ic_settings_system_dashboard_white.xml b/packages/overlays/IconPackKaiSettingsOverlay/res/drawable/ic_settings_system_dashboard_white.xml index 0c0a6827f617..70d36280c8b1 100644 --- a/packages/overlays/IconPackKaiSettingsOverlay/res/drawable/ic_settings_system_dashboard_white.xml +++ b/packages/overlays/IconPackKaiSettingsOverlay/res/drawable/ic_settings_system_dashboard_white.xml @@ -14,7 +14,7 @@ limitations under the License. --> <vector android:height="24dp" android:viewportHeight="24" android:viewportWidth="24" android:width="24dp" xmlns:android="http://schemas.android.com/apk/res/android"> - <path android:fillColor="@android:color/white" android:pathData="M 11.25 10 H 12.75 V 17 H 11.25 V 10 Z"/> - <path android:fillColor="@android:color/white" android:pathData="M 12 7 C 12.4142135624 7 12.75 7.33578643763 12.75 7.75 C 12.75 8.16421356237 12.4142135624 8.5 12 8.5 C 11.5857864376 8.5 11.25 8.16421356237 11.25 7.75 C 11.25 7.33578643763 11.5857864376 7 12 7 Z"/> - <path android:fillColor="@android:color/white" android:pathData="M12,2C4.41,2,2,6.9,2,12c0,5.09,2.35,10,10,10c7.59,0,10-4.9,10-10C22,6.91,19.65,2,12,2z M12,20.5 c-2.64,0.05-8.5-0.59-8.5-8.5c0-7.91,5.88-8.55,8.5-8.5c2.64-0.05,8.5,0.59,8.5,8.5C20.5,19.91,14.62,20.55,12,20.5z"/> + <path android:fillColor="?android:attr/colorPrimary" android:pathData="M 11.25 10 H 12.75 V 17 H 11.25 V 10 Z"/> + <path android:fillColor="?android:attr/colorPrimary" android:pathData="M 12 7 C 12.4142135624 7 12.75 7.33578643763 12.75 7.75 C 12.75 8.16421356237 12.4142135624 8.5 12 8.5 C 11.5857864376 8.5 11.25 8.16421356237 11.25 7.75 C 11.25 7.33578643763 11.5857864376 7 12 7 Z"/> + <path android:fillColor="?android:attr/colorPrimary" android:pathData="M12,2C4.41,2,2,6.9,2,12c0,5.09,2.35,10,10,10c7.59,0,10-4.9,10-10C22,6.91,19.65,2,12,2z M12,20.5 c-2.64,0.05-8.5-0.59-8.5-8.5c0-7.91,5.88-8.55,8.5-8.5c2.64-0.05,8.5,0.59,8.5,8.5C20.5,19.91,14.62,20.55,12,20.5z"/> </vector>
\ No newline at end of file diff --git a/packages/overlays/IconPackKaiSettingsOverlay/res/drawable/ic_settings_wireless.xml b/packages/overlays/IconPackKaiSettingsOverlay/res/drawable/ic_settings_wireless.xml index 2899c7f1a580..63346a485f0d 100644 --- a/packages/overlays/IconPackKaiSettingsOverlay/res/drawable/ic_settings_wireless.xml +++ b/packages/overlays/IconPackKaiSettingsOverlay/res/drawable/ic_settings_wireless.xml @@ -14,7 +14,7 @@ limitations under the License. --> <vector android:height="24dp" android:tint="?android:attr/colorControlNormal" android:viewportHeight="24" android:viewportWidth="24" android:width="24dp" xmlns:android="http://schemas.android.com/apk/res/android"> - <path android:fillColor="@android:color/white" android:pathData="M12.14,6c4.29,0.06,7.79,2.34,9.81,4.05l1.07-1.07c-2.26-1.94-6.06-4.41-10.86-4.48C8.32,4.46,4.57,5.96,1,9l1.07,1.07 C5.33,7.32,8.72,5.95,12.14,6z"/> - <path android:fillColor="@android:color/white" android:pathData="M11.97,11.5c0.04,0,0.08,0,0.12,0c2.54,0.04,4.67,1.25,6.07,2.34l1.08-1.08c-1.6-1.28-4.07-2.71-7.13-2.76 c-2.54-0.03-4.99,0.91-7.33,2.78l1.07,1.07C7.84,12.3,9.89,11.5,11.97,11.5z"/> - <path android:fillColor="@android:color/white" android:pathData="M11.98,17.5c0.02,0,0.04,0,0.05,0c0.73,0.01,1.38,0.24,1.93,0.53l1.1-1.1c-0.79-0.49-1.81-0.92-3.01-0.93 c-1.07-0.01-2.12,0.31-3.12,0.94l1.1,1.1C10.68,17.69,11.33,17.5,11.98,17.5z"/> + <path android:fillColor="?android:attr/colorPrimary" android:pathData="M12.14,6c4.29,0.06,7.79,2.34,9.81,4.05l1.07-1.07c-2.26-1.94-6.06-4.41-10.86-4.48C8.32,4.46,4.57,5.96,1,9l1.07,1.07 C5.33,7.32,8.72,5.95,12.14,6z"/> + <path android:fillColor="?android:attr/colorPrimary" android:pathData="M11.97,11.5c0.04,0,0.08,0,0.12,0c2.54,0.04,4.67,1.25,6.07,2.34l1.08-1.08c-1.6-1.28-4.07-2.71-7.13-2.76 c-2.54-0.03-4.99,0.91-7.33,2.78l1.07,1.07C7.84,12.3,9.89,11.5,11.97,11.5z"/> + <path android:fillColor="?android:attr/colorPrimary" android:pathData="M11.98,17.5c0.02,0,0.04,0,0.05,0c0.73,0.01,1.38,0.24,1.93,0.53l1.1-1.1c-0.79-0.49-1.81-0.92-3.01-0.93 c-1.07-0.01-2.12,0.31-3.12,0.94l1.1,1.1C10.68,17.69,11.33,17.5,11.98,17.5z"/> </vector>
\ No newline at end of file diff --git a/packages/overlays/IconPackKaiSettingsOverlay/res/drawable/ic_storage_white.xml b/packages/overlays/IconPackKaiSettingsOverlay/res/drawable/ic_storage_white.xml index 7b5d94696a52..0ef8a7cc9c6b 100644 --- a/packages/overlays/IconPackKaiSettingsOverlay/res/drawable/ic_storage_white.xml +++ b/packages/overlays/IconPackKaiSettingsOverlay/res/drawable/ic_storage_white.xml @@ -14,7 +14,7 @@ limitations under the License. --> <vector android:height="24dp" android:viewportHeight="24" android:viewportWidth="24" android:width="24dp" xmlns:android="http://schemas.android.com/apk/res/android"> - <path android:fillColor="@android:color/white" android:pathData="M20,4H4C3.45,4,3,4.45,3,5v2c0,0.55,0.45,1,1,1h16c0.55,0,1-0.45,1-1V5C21,4.45,20.55,4,20,4z M6,7C5.96,7,5,7.06,5,6 c0-1.06,0.97-1,1-1c0.04,0,1-0.06,1,1C7,7.06,6.03,7,6,7z"/> - <path android:fillColor="@android:color/white" android:pathData="M20,10H4c-0.55,0-1,0.45-1,1v2c0,0.55,0.45,1,1,1h16c0.55,0,1-0.45,1-1v-2C21,10.45,20.55,10,20,10z M6,13 c-0.04,0-1,0.06-1-1c0-1.06,0.97-1,1-1c0.04,0,1-0.06,1,1C7,13.06,6.03,13,6,13z"/> - <path android:fillColor="@android:color/white" android:pathData="M20,16H4c-0.55,0-1,0.45-1,1v2c0,0.55,0.45,1,1,1h16c0.55,0,1-0.45,1-1v-2C21,16.45,20.55,16,20,16z M6,19 c-0.04,0-1,0.06-1-1c0-1.06,0.97-1,1-1c0.04,0,1-0.06,1,1C7,19.06,6.03,19,6,19z"/> + <path android:fillColor="?android:attr/colorPrimary" android:pathData="M20,4H4C3.45,4,3,4.45,3,5v2c0,0.55,0.45,1,1,1h16c0.55,0,1-0.45,1-1V5C21,4.45,20.55,4,20,4z M6,7C5.96,7,5,7.06,5,6 c0-1.06,0.97-1,1-1c0.04,0,1-0.06,1,1C7,7.06,6.03,7,6,7z"/> + <path android:fillColor="?android:attr/colorPrimary" android:pathData="M20,10H4c-0.55,0-1,0.45-1,1v2c0,0.55,0.45,1,1,1h16c0.55,0,1-0.45,1-1v-2C21,10.45,20.55,10,20,10z M6,13 c-0.04,0-1,0.06-1-1c0-1.06,0.97-1,1-1c0.04,0,1-0.06,1,1C7,13.06,6.03,13,6,13z"/> + <path android:fillColor="?android:attr/colorPrimary" android:pathData="M20,16H4c-0.55,0-1,0.45-1,1v2c0,0.55,0.45,1,1,1h16c0.55,0,1-0.45,1-1v-2C21,16.45,20.55,16,20,16z M6,19 c-0.04,0-1,0.06-1-1c0-1.06,0.97-1,1-1c0.04,0,1-0.06,1,1C7,19.06,6.03,19,6,19z"/> </vector>
\ No newline at end of file diff --git a/packages/overlays/IconPackKaiSettingsOverlay/res/drawable/ic_volume_up_24dp.xml b/packages/overlays/IconPackKaiSettingsOverlay/res/drawable/ic_volume_up_24dp.xml index 019fed9f6470..b9753f5d8e9d 100644 --- a/packages/overlays/IconPackKaiSettingsOverlay/res/drawable/ic_volume_up_24dp.xml +++ b/packages/overlays/IconPackKaiSettingsOverlay/res/drawable/ic_volume_up_24dp.xml @@ -14,7 +14,7 @@ limitations under the License. --> <vector android:height="24dp" android:viewportHeight="24" android:viewportWidth="24" android:width="24dp" xmlns:android="http://schemas.android.com/apk/res/android"> - <path android:fillColor="@android:color/white" android:pathData="M14,3.3v1.58c2.35,0.89,5.55,3.95,5.5,7.18c-0.06,3.65-3.79,6.41-5.5,7.05v1.58c1.12-0.33,6.92-3.18,7-8.61 C21.07,7.39,16.32,3.99,14,3.3z"/> - <path android:fillColor="@android:color/white" android:pathData="M16.5,12.05c0.01-0.53-0.02-2.55-2.5-4.54v9C15.72,15.12,16.48,13.52,16.5,12.05z"/> - <path android:fillColor="@android:color/white" android:pathData="M9.86,6.08L6.95,9H6c-2.56,0-3.02,2.02-3,2.99C2.97,12.96,3.44,15,6,15h0.95l2.91,2.92C10.69,18.75,12,18.1,12,17.04V6.96 C12,5.85,10.65,5.29,9.86,6.08z M10.5,16.43l-2.7-2.71c-0.14-0.14-0.33-0.22-0.53-0.22H6c-1.42,0-1.51-0.99-1.5-1.54 C4.47,10.73,5.29,10.5,6,10.5h1.26c0.2,0,0.39-0.08,0.53-0.22l2.7-2.71V16.43z"/> + <path android:fillColor="?android:attr/colorPrimary" android:pathData="M14,3.3v1.58c2.35,0.89,5.55,3.95,5.5,7.18c-0.06,3.65-3.79,6.41-5.5,7.05v1.58c1.12-0.33,6.92-3.18,7-8.61 C21.07,7.39,16.32,3.99,14,3.3z"/> + <path android:fillColor="?android:attr/colorPrimary" android:pathData="M16.5,12.05c0.01-0.53-0.02-2.55-2.5-4.54v9C15.72,15.12,16.48,13.52,16.5,12.05z"/> + <path android:fillColor="?android:attr/colorPrimary" android:pathData="M9.86,6.08L6.95,9H6c-2.56,0-3.02,2.02-3,2.99C2.97,12.96,3.44,15,6,15h0.95l2.91,2.92C10.69,18.75,12,18.1,12,17.04V6.96 C12,5.85,10.65,5.29,9.86,6.08z M10.5,16.43l-2.7-2.71c-0.14-0.14-0.33-0.22-0.53-0.22H6c-1.42,0-1.51-0.99-1.5-1.54 C4.47,10.73,5.29,10.5,6,10.5h1.26c0.2,0,0.39-0.08,0.53-0.22l2.7-2.71V16.43z"/> </vector>
\ No newline at end of file diff --git a/packages/overlays/IconPackRoundedSettingsOverlay/res/drawable/ic_apps.xml b/packages/overlays/IconPackRoundedSettingsOverlay/res/drawable/ic_apps.xml index fd71322eb8b0..8db613dfa5d2 100644 --- a/packages/overlays/IconPackRoundedSettingsOverlay/res/drawable/ic_apps.xml +++ b/packages/overlays/IconPackRoundedSettingsOverlay/res/drawable/ic_apps.xml @@ -20,30 +20,30 @@ android:viewportWidth="24" android:width="24dp" > <path - android:fillColor="@android:color/white" + android:fillColor="?android:attr/colorPrimary" android:pathData="M6.5,4h-1C4.67,4,4,4.67,4,5.5v1C4,7.33,4.67,8,5.5,8h1C7.33,8,8,7.33,8,6.5v-1C8,4.67,7.33,4,6.5,4z" /> <path - android:fillColor="@android:color/white" + android:fillColor="?android:attr/colorPrimary" android:pathData="M12.5,4h-1C10.67,4,10,4.67,10,5.5v1C10,7.33,10.67,8,11.5,8h1C13.33,8,14,7.33,14,6.5v-1C14,4.67,13.33,4,12.5,4z" /> <path - android:fillColor="@android:color/white" + android:fillColor="?android:attr/colorPrimary" android:pathData="M18.5,4h-1C16.67,4,16,4.67,16,5.5v1C16,7.33,16.67,8,17.5,8h1C19.33,8,20,7.33,20,6.5v-1C20,4.67,19.33,4,18.5,4z" /> <path - android:fillColor="@android:color/white" + android:fillColor="?android:attr/colorPrimary" android:pathData="M6.5,10h-1C4.67,10,4,10.67,4,11.5v1C4,13.33,4.67,14,5.5,14h1C7.33,14,8,13.33,8,12.5v-1C8,10.67,7.33,10,6.5,10z" /> <path - android:fillColor="@android:color/white" + android:fillColor="?android:attr/colorPrimary" android:pathData="M12.5,10h-1c-0.83,0-1.5,0.67-1.5,1.5v1c0,0.83,0.67,1.5,1.5,1.5h1c0.83,0,1.5-0.67,1.5-1.5v-1C14,10.67,13.33,10,12.5,10 z" /> <path - android:fillColor="@android:color/white" + android:fillColor="?android:attr/colorPrimary" android:pathData="M18.5,10h-1c-0.83,0-1.5,0.67-1.5,1.5v1c0,0.83,0.67,1.5,1.5,1.5h1c0.83,0,1.5-0.67,1.5-1.5v-1C20,10.67,19.33,10,18.5,10 z" /> <path - android:fillColor="@android:color/white" + android:fillColor="?android:attr/colorPrimary" android:pathData="M6.5,16h-1C4.67,16,4,16.67,4,17.5v1C4,19.33,4.67,20,5.5,20h1C7.33,20,8,19.33,8,18.5v-1C8,16.67,7.33,16,6.5,16z" /> <path - android:fillColor="@android:color/white" + android:fillColor="?android:attr/colorPrimary" android:pathData="M12.5,16h-1c-0.83,0-1.5,0.67-1.5,1.5v1c0,0.83,0.67,1.5,1.5,1.5h1c0.83,0,1.5-0.67,1.5-1.5v-1C14,16.67,13.33,16,12.5,16 z" /> <path - android:fillColor="@android:color/white" + android:fillColor="?android:attr/colorPrimary" android:pathData="M18.5,16h-1c-0.83,0-1.5,0.67-1.5,1.5v1c0,0.83,0.67,1.5,1.5,1.5h1c0.83,0,1.5-0.67,1.5-1.5v-1C20,16.67,19.33,16,18.5,16 z" /> </vector>
\ No newline at end of file diff --git a/packages/overlays/IconPackRoundedSettingsOverlay/res/drawable/ic_devices_other.xml b/packages/overlays/IconPackRoundedSettingsOverlay/res/drawable/ic_devices_other.xml index 463525d4bf26..1b4ec92e3734 100644 --- a/packages/overlays/IconPackRoundedSettingsOverlay/res/drawable/ic_devices_other.xml +++ b/packages/overlays/IconPackRoundedSettingsOverlay/res/drawable/ic_devices_other.xml @@ -21,12 +21,12 @@ android:viewportWidth="24" android:width="24dp" > <path - android:fillColor="@android:color/white" + android:fillColor="?android:attr/colorPrimary" android:pathData="M5.25,18H3.5V5.5h17.75C21.66,5.5,22,5.16,22,4.75S21.66,4,21.25,4H3.5C2.67,4,2,4.67,2,5.5V18c0,0.83,0.67,1.5,1.5,1.5 h1.75C5.66,19.5,6,19.16,6,18.75S5.66,18,5.25,18z" /> <path - android:fillColor="@android:color/white" + android:fillColor="?android:attr/colorPrimary" android:pathData="M10.5,14.5C9.12,14.5,8,15.62,8,17s1.12,2.5,2.5,2.5S13,18.38,13,17S11.88,14.5,10.5,14.5z M10.5,18c-0.55,0-1-0.45-1-1 s0.45-1,1-1s1,0.45,1,1S11.05,18,10.5,18z" /> <path - android:fillColor="@android:color/white" + android:fillColor="?android:attr/colorPrimary" android:pathData="M20.5,8.5h-4C15.67,8.5,15,9.17,15,10v8c0,0.83,0.67,1.5,1.5,1.5h4c0.83,0,1.5-0.67,1.5-1.5v-8 C22,9.17,21.33,8.5,20.5,8.5z M20.5,18h-4v-8h4V18z" /> </vector>
\ No newline at end of file diff --git a/packages/overlays/IconPackRoundedSettingsOverlay/res/drawable/ic_help.xml b/packages/overlays/IconPackRoundedSettingsOverlay/res/drawable/ic_help.xml index fce8140fa3b0..d062e65dedd5 100644 --- a/packages/overlays/IconPackRoundedSettingsOverlay/res/drawable/ic_help.xml +++ b/packages/overlays/IconPackRoundedSettingsOverlay/res/drawable/ic_help.xml @@ -20,12 +20,12 @@ android:viewportWidth="24" android:width="24dp" > <path - android:fillColor="@android:color/white" + android:fillColor="?android:attr/colorPrimary" android:pathData="M12,22c0,0,0.01,0,0.01,0c5.5,0,9.98-4.47,9.99-9.98V12c0-5.51-4.49-10-10-10S2,6.49,2,12S6.49,22,12,22z M12,3.5 c4.69,0,8.5,3.81,8.5,8.5v0.02c0,4.68-3.81,8.48-8.49,8.48c0,0-0.01,0-0.01,0c-4.69,0-8.5-3.81-8.5-8.5S7.31,3.5,12,3.5z" /> <path - android:fillColor="@android:color/white" + android:fillColor="?android:attr/colorPrimary" android:pathData="M8.67,9.98c0.4,0.1,0.81-0.15,0.9-0.56c0.11-0.47,0.33-0.86,0.65-1.19c0.94-0.94,2.59-0.94,3.54,0 c0.49,0.49,0.73,1.13,0.67,1.76c-0.06,0.57-0.36,1.06-0.84,1.38c-0.13,0.08-0.26,0.16-0.4,0.24c-0.7,0.4-1.67,0.94-1.93,2.51 c-0.07,0.41,0.21,0.8,0.61,0.86C11.92,15,11.96,15,12,15c0.36,0,0.68-0.26,0.74-0.62c0.15-0.87,0.58-1.12,1.19-1.46 c0.17-0.09,0.33-0.19,0.49-0.29c0.87-0.58,1.41-1.46,1.51-2.48c0.11-1.08-0.29-2.17-1.1-2.97c-1.51-1.51-4.15-1.51-5.66,0 c-0.52,0.51-0.88,1.17-1.05,1.9C8.02,9.48,8.27,9.88,8.67,9.98z" /> <path - android:fillColor="@android:color/white" + android:fillColor="?android:attr/colorPrimary" android:pathData="M 12 16 C 12.5522847498 16 13 16.4477152502 13 17 C 13 17.5522847498 12.5522847498 18 12 18 C 11.4477152502 18 11 17.5522847498 11 17 C 11 16.4477152502 11.4477152502 16 12 16 Z" /> </vector>
\ No newline at end of file diff --git a/packages/overlays/IconPackRoundedSettingsOverlay/res/drawable/ic_phone_info.xml b/packages/overlays/IconPackRoundedSettingsOverlay/res/drawable/ic_phone_info.xml index 0983f9f4a8aa..f41f7a05e68f 100644 --- a/packages/overlays/IconPackRoundedSettingsOverlay/res/drawable/ic_phone_info.xml +++ b/packages/overlays/IconPackRoundedSettingsOverlay/res/drawable/ic_phone_info.xml @@ -20,12 +20,12 @@ android:viewportWidth="24" android:width="24dp" > <path - android:fillColor="@android:color/white" + android:fillColor="?android:attr/colorPrimary" android:pathData="M8,1C6.34,1,5,2.34,5,4v16c0,1.66,1.34,3,3,3h8c1.66,0,3-1.34,3-3V4c0-1.66-1.34-3-3-3H8z M16,21.5H8 c-0.83,0-1.5-0.67-1.5-1.5h11C17.5,20.83,16.83,21.5,16,21.5z M17.5,18.5h-11v-13h11V18.5z M17.5,4h-11c0-0.83,0.67-1.5,1.5-1.5h8 C16.83,2.5,17.5,3.17,17.5,4z" /> <path - android:fillColor="@android:color/white" + android:fillColor="?android:attr/colorPrimary" android:pathData="M12,10.5c-0.41,0-0.75,0.34-0.75,0.75v5c0,0.41,0.34,0.75,0.75,0.75s0.75-0.34,0.75-0.75v-5 C12.75,10.84,12.41,10.5,12,10.5z" /> <path - android:fillColor="@android:color/white" + android:fillColor="?android:attr/colorPrimary" android:pathData="M 12 7 C 12.5522847498 7 13 7.44771525017 13 8 C 13 8.55228474983 12.5522847498 9 12 9 C 11.4477152502 9 11 8.55228474983 11 8 C 11 7.44771525017 11.4477152502 7 12 7 Z" /> </vector>
\ No newline at end of file diff --git a/packages/overlays/IconPackRoundedSettingsOverlay/res/drawable/ic_settings_accessibility.xml b/packages/overlays/IconPackRoundedSettingsOverlay/res/drawable/ic_settings_accessibility.xml index bfffc307c9b6..f17b5b93d84f 100644 --- a/packages/overlays/IconPackRoundedSettingsOverlay/res/drawable/ic_settings_accessibility.xml +++ b/packages/overlays/IconPackRoundedSettingsOverlay/res/drawable/ic_settings_accessibility.xml @@ -20,18 +20,18 @@ android:viewportWidth="24" android:width="24dp" > <path - android:fillColor="@android:color/white" + android:fillColor="?android:attr/colorPrimary" android:pathData="M 12 0.5 C 13.1045694997 0.5 14 1.39543050034 14 2.5 C 14 3.60456949966 13.1045694997 4.5 12 4.5 C 10.8954305003 4.5 10 3.60456949966 10 2.5 C 10 1.39543050034 10.8954305003 0.5 12 0.5 Z" /> <path - android:fillColor="@android:color/white" + android:fillColor="?android:attr/colorPrimary" android:pathData="M20.72,5.28c-0.12-0.4-0.54-0.62-0.94-0.5C19.75,4.79,16.55,5.75,12,5.75c-4.53,0-7.75-0.96-7.78-0.97 c-0.39-0.12-0.81,0.1-0.94,0.5c-0.12,0.4,0.1,0.81,0.5,0.94C3.89,6.25,5.89,6.85,9,7.12v12.13C9,19.66,9.34,20,9.75,20 s0.75-0.34,0.75-0.75V14h3v5.25c0,0.41,0.34,0.75,0.75,0.75S15,19.66,15,19.25V7.12c3.11-0.27,5.11-0.87,5.22-0.9 C20.61,6.1,20.84,5.68,20.72,5.28z" /> <path - android:fillColor="@android:color/white" + android:fillColor="?android:attr/colorPrimary" android:pathData="M 8 22 C 8.55228474983 22 9 22.4477152502 9 23 C 9 23.5522847498 8.55228474983 24 8 24 C 7.44771525017 24 7 23.5522847498 7 23 C 7 22.4477152502 7.44771525017 22 8 22 Z" /> <path - android:fillColor="@android:color/white" + android:fillColor="?android:attr/colorPrimary" android:pathData="M 12 22 C 12.5522847498 22 13 22.4477152502 13 23 C 13 23.5522847498 12.5522847498 24 12 24 C 11.4477152502 24 11 23.5522847498 11 23 C 11 22.4477152502 11.4477152502 22 12 22 Z" /> <path - android:fillColor="@android:color/white" + android:fillColor="?android:attr/colorPrimary" android:pathData="M 16 22 C 16.5522847498 22 17 22.4477152502 17 23 C 17 23.5522847498 16.5522847498 24 16 24 C 15.4477152502 24 15 23.5522847498 15 23 C 15 22.4477152502 15.4477152502 22 16 22 Z" /> </vector>
\ No newline at end of file diff --git a/packages/overlays/IconPackRoundedSettingsOverlay/res/drawable/ic_settings_accounts.xml b/packages/overlays/IconPackRoundedSettingsOverlay/res/drawable/ic_settings_accounts.xml index f213bc4ddc91..f02da58550ba 100644 --- a/packages/overlays/IconPackRoundedSettingsOverlay/res/drawable/ic_settings_accounts.xml +++ b/packages/overlays/IconPackRoundedSettingsOverlay/res/drawable/ic_settings_accounts.xml @@ -20,9 +20,9 @@ android:viewportWidth="24" android:width="24dp" > <path - android:fillColor="@android:color/white" + android:fillColor="?android:attr/colorPrimary" android:pathData="M12,6c-1.65,0-3,1.35-3,3v0.01C9,10.66,10.35,12,11.99,12c0,0,0,0,0.01,0c1.65,0,3-1.35,3-3S13.65,6,12,6z M12,10.5 C12,10.5,12,10.5,12,10.5c-0.83,0-1.5-0.67-1.5-1.49V9c0-0.83,0.67-1.5,1.5-1.5s1.5,0.67,1.5,1.5S12.83,10.5,12,10.5z" /> <path - android:fillColor="@android:color/white" + android:fillColor="?android:attr/colorPrimary" android:pathData="M19.25,3.25H4.75c-0.83,0-1.5,0.67-1.5,1.5v14.5c0,0.83,0.67,1.5,1.5,1.5h14.5c0.83,0,1.5-0.67,1.5-1.5V4.75 C20.75,3.92,20.08,3.25,19.25,3.25z M16.5,19.25h-9v-3.5C7.5,15.34,7.84,15,8.25,15h7.5c0.41,0,0.75,0.34,0.75,0.75V19.25z M19.25,19.25H18v-3.5c0-1.24-1.01-2.25-2.25-2.25h-7.5C7.01,13.5,6,14.51,6,15.75v3.5H4.75V4.75h14.5L19.25,19.25z" /> </vector>
\ No newline at end of file diff --git a/packages/overlays/IconPackRoundedSettingsOverlay/res/drawable/ic_settings_battery_white.xml b/packages/overlays/IconPackRoundedSettingsOverlay/res/drawable/ic_settings_battery_white.xml index 4ed698cfb164..e27cb8d54838 100644 --- a/packages/overlays/IconPackRoundedSettingsOverlay/res/drawable/ic_settings_battery_white.xml +++ b/packages/overlays/IconPackRoundedSettingsOverlay/res/drawable/ic_settings_battery_white.xml @@ -20,6 +20,6 @@ android:viewportWidth="24" android:width="24dp" > <path - android:fillColor="@android:color/white" + android:fillColor="?android:attr/colorPrimary" android:pathData="M13,2.49h-2c-0.55,0-1,0.45-1,1V4H7C6.45,4,6,4.45,6,5v16c0,0.55,0.45,1,1,1h10c0.55,0,1-0.45,1-1V5c0-0.55-0.45-1-1-1h-3 V3.49C14,2.94,13.55,2.49,13,2.49z" /> </vector>
\ No newline at end of file diff --git a/packages/overlays/IconPackRoundedSettingsOverlay/res/drawable/ic_settings_display_white.xml b/packages/overlays/IconPackRoundedSettingsOverlay/res/drawable/ic_settings_display_white.xml index 2e662684438f..19acd6ae8210 100644 --- a/packages/overlays/IconPackRoundedSettingsOverlay/res/drawable/ic_settings_display_white.xml +++ b/packages/overlays/IconPackRoundedSettingsOverlay/res/drawable/ic_settings_display_white.xml @@ -20,9 +20,9 @@ android:viewportWidth="24" android:width="24dp" > <path - android:fillColor="@android:color/white" + android:fillColor="?android:attr/colorPrimary" android:pathData="M21.25,11.25h-2.8A6.46,6.46,0,0,0,17.09,8l2-2A0.75 0.75 ,0,0,0,18,4.93l-2,2a6.46,6.46,0,0,0-3.28-1.36V2.75a0.75 0.75 ,0,0,0-1.5,0v2.8A6.46,6.46,0,0,0,8,6.91l-2-2A0.75 0.75 ,0,0,0,4.93,6l2,2a6.46,6.46,0,0,0-1.36,3.28H2.75a0.75 0.75 ,0,0,0,0,1.5h2.8A6.46,6.46,0,0,0,6.91,16l-2,2A0.75 0.75 ,0,0,0,6,19.07l2-2a6.46,6.46,0,0,0,3.28,1.36v2.8a0.75 0.75 ,0,0,0,1.5,0v-2.8A6.46,6.46,0,0,0,16,17.09l2,2A0.75 0.75 ,0,0,0,19.07,18l-2-2a6.46,6.46,0,0,0,1.36-3.28h2.8a0.75 0.75 ,0,0,0,0-1.5ZM12,17a5,5,0,1,1,5-5A5,5,0,0,1,12,17Z" /> <path - android:fillColor="@android:color/white" + android:fillColor="?android:attr/colorPrimary" android:pathData="M12,15.5a3.5,3.5,0,0,0,0-7Z" /> </vector>
\ No newline at end of file diff --git a/packages/overlays/IconPackRoundedSettingsOverlay/res/drawable/ic_settings_location.xml b/packages/overlays/IconPackRoundedSettingsOverlay/res/drawable/ic_settings_location.xml index a00c85f3d1f1..762d67d7fa87 100644 --- a/packages/overlays/IconPackRoundedSettingsOverlay/res/drawable/ic_settings_location.xml +++ b/packages/overlays/IconPackRoundedSettingsOverlay/res/drawable/ic_settings_location.xml @@ -20,9 +20,9 @@ android:viewportWidth="24" android:width="24dp" > <path - android:fillColor="@android:color/white" + android:fillColor="?android:attr/colorPrimary" android:pathData="M12,7c-1.66,0-3,1.34-3,3s1.34,3,3,3s3-1.34,3-3S13.66,7,12,7z M12,11.5c-0.83,0-1.5-0.67-1.5-1.5s0.67-1.5,1.5-1.5 s1.5,0.67,1.5,1.5S12.83,11.5,12,11.5z" /> <path - android:fillColor="@android:color/white" + android:fillColor="?android:attr/colorPrimary" android:pathData="M12,2.01c-4.5,0-8,3.49-8,8c0,5.49,5.48,10.24,7.37,11.76c0.19,0.15,0.41,0.22,0.63,0.22c0.22,0,0.44-0.07,0.62-0.22 C14.5,20.26,20,15.5,20,10C20,5.72,16.5,2.01,12,2.01z M12,20.34c-2.18-1.8-6.5-5.94-6.5-10.34c0-3.64,2.86-6.5,6.5-6.5 c3.58,0,6.5,2.91,6.5,6.49C18.5,14.4,14.19,18.53,12,20.34z" /> </vector>
\ No newline at end of file diff --git a/packages/overlays/IconPackRoundedSettingsOverlay/res/drawable/ic_settings_privacy.xml b/packages/overlays/IconPackRoundedSettingsOverlay/res/drawable/ic_settings_privacy.xml index 69cd1a4aa018..12a82f2c1f0b 100644 --- a/packages/overlays/IconPackRoundedSettingsOverlay/res/drawable/ic_settings_privacy.xml +++ b/packages/overlays/IconPackRoundedSettingsOverlay/res/drawable/ic_settings_privacy.xml @@ -20,12 +20,12 @@ android:viewportWidth="24" android:width="24dp" > <path - android:fillColor="@android:color/white" + android:fillColor="?android:attr/colorPrimary" android:pathData="M12,8c-2.21,0-4,1.79-4,4s1.79,4,4,4c0.76,0,1.46-0.22,2.06-0.59c0.16-1.38,0.88-2.59,1.94-3.39c0-0.01,0-0.02,0-0.02 C16,9.79,14.21,8,12,8z M12,14.5c-1.38,0-2.5-1.12-2.5-2.5s1.12-2.5,2.5-2.5c1.38,0,2.5,1.12,2.5,2.5S13.38,14.5,12,14.5z" /> <path - android:fillColor="@android:color/white" + android:fillColor="?android:attr/colorPrimary" android:pathData="M3.09,12C3.73,10.81,6.43,6.5,12,6.5c4.53,0,7.14,2.79,8.31,4.5h1.77C21.1,9.28,18.05,5,12,5c-7.4,0-10.32,6.42-10.44,6.7 c-0.09,0.19-0.09,0.41,0,0.61C1.68,12.58,4.61,19,12,19c0.71,0,1.37-0.07,2-0.18V17.3c-0.62,0.13-1.28,0.2-2,0.2 C6.39,17.5,3.73,13.21,3.09,12z" /> <path - android:fillColor="@android:color/white" + android:fillColor="?android:attr/colorPrimary" android:pathData="M21,16c0-0.35,0-0.72,0-1c0-1.1-0.9-2-2-2c-1.1,0-2,0.9-2,2c0,0.37,0,0.7,0,1c-0.55,0-1,0.45-1,1v3c0,0.55,0.45,1,1,1h4 c0.55,0,1-0.45,1-1v-3C22,16.45,21.55,16,21,16z M20,16h-2v-1c0-0.55,0.45-1,1-1s1,0.45,1,1V16z" /> </vector>
\ No newline at end of file diff --git a/packages/overlays/IconPackRoundedSettingsOverlay/res/drawable/ic_settings_security_white.xml b/packages/overlays/IconPackRoundedSettingsOverlay/res/drawable/ic_settings_security_white.xml index c49959628470..e93e63f5f603 100644 --- a/packages/overlays/IconPackRoundedSettingsOverlay/res/drawable/ic_settings_security_white.xml +++ b/packages/overlays/IconPackRoundedSettingsOverlay/res/drawable/ic_settings_security_white.xml @@ -20,9 +20,9 @@ android:viewportWidth="24" android:width="24dp" > <path - android:fillColor="@android:color/white" + android:fillColor="?android:attr/colorPrimary" android:pathData="M 12 13.5 C 12.8284271247 13.5 13.5 14.1715728753 13.5 15 C 13.5 15.8284271247 12.8284271247 16.5 12 16.5 C 11.1715728753 16.5 10.5 15.8284271247 10.5 15 C 10.5 14.1715728753 11.1715728753 13.5 12 13.5 Z" /> <path - android:fillColor="@android:color/white" + android:fillColor="?android:attr/colorPrimary" android:pathData="M18.51,1.46c-2.19-0.06-3.98,1.61-4.01,3.68V8.5h-9C4.67,8.5,4,9.17,4,10v10c0,0.83,0.67,1.5,1.5,1.5h13 c0.83,0,1.5-0.67,1.5-1.5V10c0-0.83-0.67-1.5-1.5-1.5H16V5.15c0.02-1.23,1.14-2.23,2.51-2.19C19.9,2.93,20.98,3.92,21,5.15 c0.01,0.41,0.36,0.71,0.76,0.74c0.41-0.01,0.74-0.35,0.74-0.76C22.46,3.07,20.7,1.39,18.51,1.46z M18.5,10v10h-13V10H18.5z" /> </vector>
\ No newline at end of file diff --git a/packages/overlays/IconPackRoundedSettingsOverlay/res/drawable/ic_settings_system_dashboard_white.xml b/packages/overlays/IconPackRoundedSettingsOverlay/res/drawable/ic_settings_system_dashboard_white.xml index fe9c57870035..9ec3ffcc47fa 100644 --- a/packages/overlays/IconPackRoundedSettingsOverlay/res/drawable/ic_settings_system_dashboard_white.xml +++ b/packages/overlays/IconPackRoundedSettingsOverlay/res/drawable/ic_settings_system_dashboard_white.xml @@ -20,12 +20,12 @@ android:viewportWidth="24" android:width="24dp" > <path - android:fillColor="@android:color/white" + android:fillColor="?android:attr/colorPrimary" android:pathData="M4.92,4.94c-3.9,3.91-3.9,10.24,0.01,14.14s10.24,3.9,14.14-0.01C20.95,17.2,22,14.65,22,12c0-2.65-1.06-5.19-2.93-7.07 C15.16,1.03,8.83,1.03,4.92,4.94z M18,18c-1.6,1.59-3.76,2.48-6.02,2.48c-4.69-0.01-8.49-3.83-8.48-8.52 c0.01-4.69,3.83-8.49,8.52-8.48c4.69,0.01,8.49,3.83,8.48,8.52C20.49,14.25,19.6,16.41,18,18z" /> <path - android:fillColor="@android:color/white" + android:fillColor="?android:attr/colorPrimary" android:pathData="M 12 7 C 12.5522847498 7 13 7.44771525017 13 8 C 13 8.55228474983 12.5522847498 9 12 9 C 11.4477152502 9 11 8.55228474983 11 8 C 11 7.44771525017 11.4477152502 7 12 7 Z" /> <path - android:fillColor="@android:color/white" + android:fillColor="?android:attr/colorPrimary" android:pathData="M12,10.5c-0.41,0-0.75,0.34-0.75,0.75v5c0,0.41,0.34,0.75,0.75,0.75s0.75-0.34,0.75-0.75v-5 C12.75,10.84,12.41,10.5,12,10.5z" /> </vector>
\ No newline at end of file diff --git a/packages/overlays/IconPackRoundedSettingsOverlay/res/drawable/ic_settings_wireless.xml b/packages/overlays/IconPackRoundedSettingsOverlay/res/drawable/ic_settings_wireless.xml index be66878909a2..c8c8abc0b6ac 100644 --- a/packages/overlays/IconPackRoundedSettingsOverlay/res/drawable/ic_settings_wireless.xml +++ b/packages/overlays/IconPackRoundedSettingsOverlay/res/drawable/ic_settings_wireless.xml @@ -21,15 +21,15 @@ android:viewportWidth="24" android:width="24dp" > <path - android:fillColor="@android:color/white" + android:fillColor="?android:attr/colorPrimary" android:pathData="M12,2.75C7.95,2.69,4.05,4.3,1.22,7.2C0.96,7.5,0.97,7.95,1.24,8.23C1.53,8.53,2,8.54,2.3,8.25c2.55-2.61,6.05-4.06,9.7-4 c3.65-0.06,7.17,1.4,9.72,4.02c0.28,0.27,0.73,0.28,1.03,0.01c0.31-0.28,0.33-0.75,0.05-1.06C19.96,4.32,16.06,2.69,12,2.75z" /> <path - android:fillColor="@android:color/white" + android:fillColor="?android:attr/colorPrimary" android:pathData="M15.78,14.82c0.05,0.06,0.1,0.11,0.17,0.15c0.34,0.23,0.81,0.14,1.04-0.21s0.14-0.81-0.21-1.04 c-2.64-2.64-6.91-2.64-9.55,0c-0.27,0.29-0.27,0.73,0,1.02c0.28,0.3,0.76,0.32,1.06,0.04h0.03c0,0,0,0,0.01-0.01 c2.05-2.05,5.37-2.04,7.42,0.01C15.75,14.8,15.76,14.81,15.78,14.82z" /> <path - android:fillColor="@android:color/white" + android:fillColor="?android:attr/colorPrimary" android:pathData="M 12 17 C 12.8284271247 17 13.5 17.6715728753 13.5 18.5 C 13.5 19.3284271247 12.8284271247 20 12 20 C 11.1715728753 20 10.5 19.3284271247 10.5 18.5 C 10.5 17.6715728753 11.1715728753 17 12 17 Z" /> <path - android:fillColor="@android:color/white" + android:fillColor="?android:attr/colorPrimary" android:pathData="M20.03,11.79c0.3-0.29,0.3-0.77,0.01-1.06h-0.01c-2.12-2.18-5.01-3.44-8.04-3.5c-3.04,0.06-5.93,1.32-8.05,3.5 c-0.29,0.3-0.28,0.77,0.01,1.06c0.3,0.29,0.77,0.28,1.06-0.01c1.85-1.88,4.36-2.96,7-3c2.62,0.05,5.11,1.13,6.95,3 C19.25,12.07,19.73,12.08,20.03,11.79z" /> </vector>
\ No newline at end of file diff --git a/packages/overlays/IconPackRoundedSettingsOverlay/res/drawable/ic_storage_white.xml b/packages/overlays/IconPackRoundedSettingsOverlay/res/drawable/ic_storage_white.xml index e80df4f20c82..355ee3b920ee 100644 --- a/packages/overlays/IconPackRoundedSettingsOverlay/res/drawable/ic_storage_white.xml +++ b/packages/overlays/IconPackRoundedSettingsOverlay/res/drawable/ic_storage_white.xml @@ -20,21 +20,21 @@ android:viewportWidth="24" android:width="24dp" > <path - android:fillColor="@android:color/white" + android:fillColor="?android:attr/colorPrimary" android:pathData="M20.25,2H3.75C3.34,2,3,2.34,3,2.75v4.5C3,7.66,3.34,8,3.75,8h16.5C20.66,8,21,7.66,21,7.25v-4.5C21,2.34,20.66,2,20.25,2 z M19.5,6.5h-15v-3h15V6.5z" /> <path - android:fillColor="@android:color/white" + android:fillColor="?android:attr/colorPrimary" android:pathData="M 5.25 4.25 H 6.75 V 5.75 H 5.25 V 4.25 Z" /> <path - android:fillColor="@android:color/white" + android:fillColor="?android:attr/colorPrimary" android:pathData="M20.25,9H3.75C3.34,9,3,9.34,3,9.75v4.5C3,14.66,3.34,15,3.75,15h16.5c0.41,0,0.75-0.34,0.75-0.75v-4.5 C21,9.34,20.66,9,20.25,9z M19.5,13.5h-15v-3h15V13.5z" /> <path - android:fillColor="@android:color/white" + android:fillColor="?android:attr/colorPrimary" android:pathData="M 5.25 11.25 H 6.75 V 12.75 H 5.25 V 11.25 Z" /> <path - android:fillColor="@android:color/white" + android:fillColor="?android:attr/colorPrimary" android:pathData="M20.25,16H3.75C3.34,16,3,16.34,3,16.75v4.5C3,21.66,3.34,22,3.75,22h16.5c0.41,0,0.75-0.34,0.75-0.75v-4.5 C21,16.34,20.66,16,20.25,16z M19.5,20.5h-15v-3h15V20.5z" /> <path - android:fillColor="@android:color/white" + android:fillColor="?android:attr/colorPrimary" android:pathData="M 5.25 18.25 H 6.75 V 19.75 H 5.25 V 18.25 Z" /> </vector>
\ No newline at end of file diff --git a/packages/overlays/IconPackRoundedSettingsOverlay/res/drawable/ic_volume_up_24dp.xml b/packages/overlays/IconPackRoundedSettingsOverlay/res/drawable/ic_volume_up_24dp.xml index 9370151c4255..25f81b71256a 100644 --- a/packages/overlays/IconPackRoundedSettingsOverlay/res/drawable/ic_volume_up_24dp.xml +++ b/packages/overlays/IconPackRoundedSettingsOverlay/res/drawable/ic_volume_up_24dp.xml @@ -20,12 +20,12 @@ android:viewportWidth="24" android:width="24dp" > <path - android:fillColor="@android:color/white" + android:fillColor="?android:attr/colorPrimary" android:pathData="M5.69,16l5.03,5.03c0.14,0.14,0.34,0.22,0.53,0.22c0.1,0,0.19-0.02,0.29-0.06C11.82,21.08,12,20.8,12,20.5v-17 c0-0.3-0.18-0.58-0.46-0.69c-0.28-0.11-0.6-0.05-0.82,0.16L5.69,8H3.49C2.68,8.01,2.01,8.68,2,9.5v5C2,15.33,2.67,16,3.5,16H5.69z M3.5,9.5H6c0.2,0,0.39-0.08,0.53-0.22l3.97-3.97v13.38l-3.97-3.97C6.39,14.58,6.2,14.5,6,14.5H3.5V9.5z" /> <path - android:fillColor="@android:color/white" + android:fillColor="?android:attr/colorPrimary" android:pathData="M13.52,20.64c0.09,0.34,0.39,0.56,0.72,0.56c0.06,0,0.13-0.01,0.19-0.02c3.29-0.87,5.88-3.46,6.75-6.75 c1.34-5.06-1.69-10.26-6.75-11.59c-0.4-0.11-0.81,0.13-0.92,0.53c-0.11,0.4,0.13,0.81,0.53,0.92c4.26,1.13,6.8,5.5,5.68,9.76 c-0.73,2.77-2.91,4.95-5.68,5.68C13.66,19.83,13.42,20.24,13.52,20.64z" /> <path - android:fillColor="@android:color/white" + android:fillColor="?android:attr/colorPrimary" android:pathData="M13.85,14.96c-0.35,0.22-0.46,0.68-0.24,1.03c0.14,0.23,0.39,0.35,0.64,0.35c0.14,0,0.27-0.04,0.4-0.11 c1.13-0.7,1.92-1.81,2.22-3.1c0.3-1.3,0.08-2.64-0.62-3.77c-0.4-0.65-0.96-1.2-1.6-1.6C14.29,7.54,13.83,7.65,13.61,8 c-0.22,0.35-0.11,0.81,0.24,1.03c0.45,0.28,0.84,0.67,1.12,1.12c0.49,0.79,0.65,1.73,0.44,2.64C15.2,13.7,14.65,14.47,13.85,14.96z" /> </vector>
\ No newline at end of file diff --git a/packages/overlays/IconPackSamSettingsOverlay/res/drawable/ic_apps.xml b/packages/overlays/IconPackSamSettingsOverlay/res/drawable/ic_apps.xml index ff34995e9e8b..69835c0b867a 100644 --- a/packages/overlays/IconPackSamSettingsOverlay/res/drawable/ic_apps.xml +++ b/packages/overlays/IconPackSamSettingsOverlay/res/drawable/ic_apps.xml @@ -14,13 +14,13 @@ limitations under the License. --> <vector android:height="24dp" android:viewportHeight="24" android:viewportWidth="24" android:width="24dp" xmlns:android="http://schemas.android.com/apk/res/android"> - <path android:fillColor="@android:color/white" android:pathData="M 6 10 C 7.10456949966 10 8 10.8954305003 8 12 C 8 13.1045694997 7.10456949966 14 6 14 C 4.89543050034 14 4 13.1045694997 4 12 C 4 10.8954305003 4.89543050034 10 6 10 Z"/> - <path android:fillColor="@android:color/white" android:pathData="M 6 4 C 7.10456949966 4 8 4.89543050034 8 6 C 8 7.10456949966 7.10456949966 8 6 8 C 4.89543050034 8 4 7.10456949966 4 6 C 4 4.89543050034 4.89543050034 4 6 4 Z"/> - <path android:fillColor="@android:color/white" android:pathData="M 18 4 C 19.1045694997 4 20 4.89543050034 20 6 C 20 7.10456949966 19.1045694997 8 18 8 C 16.8954305003 8 16 7.10456949966 16 6 C 16 4.89543050034 16.8954305003 4 18 4 Z"/> - <path android:fillColor="@android:color/white" android:pathData="M 6 16 C 7.10456949966 16 8 16.8954305003 8 18 C 8 19.1045694997 7.10456949966 20 6 20 C 4.89543050034 20 4 19.1045694997 4 18 C 4 16.8954305003 4.89543050034 16 6 16 Z"/> - <path android:fillColor="@android:color/white" android:pathData="M 12 16 C 13.1045694997 16 14 16.8954305003 14 18 C 14 19.1045694997 13.1045694997 20 12 20 C 10.8954305003 20 10 19.1045694997 10 18 C 10 16.8954305003 10.8954305003 16 12 16 Z"/> - <path android:fillColor="@android:color/white" android:pathData="M 18 10 C 19.1045694997 10 20 10.8954305003 20 12 C 20 13.1045694997 19.1045694997 14 18 14 C 16.8954305003 14 16 13.1045694997 16 12 C 16 10.8954305003 16.8954305003 10 18 10 Z"/> - <path android:fillColor="@android:color/white" android:pathData="M 18 16 C 19.1045694997 16 20 16.8954305003 20 18 C 20 19.1045694997 19.1045694997 20 18 20 C 16.8954305003 20 16 19.1045694997 16 18 C 16 16.8954305003 16.8954305003 16 18 16 Z"/> - <path android:fillColor="@android:color/white" android:pathData="M 12 10 C 13.1045694997 10 14 10.8954305003 14 12 C 14 13.1045694997 13.1045694997 14 12 14 C 10.8954305003 14 10 13.1045694997 10 12 C 10 10.8954305003 10.8954305003 10 12 10 Z"/> - <path android:fillColor="@android:color/white" android:pathData="M 12 4 C 13.1045694997 4 14 4.89543050034 14 6 C 14 7.10456949966 13.1045694997 8 12 8 C 10.8954305003 8 10 7.10456949966 10 6 C 10 4.89543050034 10.8954305003 4 12 4 Z"/> + <path android:fillColor="?android:attr/colorPrimary" android:pathData="M 6 10 C 7.10456949966 10 8 10.8954305003 8 12 C 8 13.1045694997 7.10456949966 14 6 14 C 4.89543050034 14 4 13.1045694997 4 12 C 4 10.8954305003 4.89543050034 10 6 10 Z"/> + <path android:fillColor="?android:attr/colorPrimary" android:pathData="M 6 4 C 7.10456949966 4 8 4.89543050034 8 6 C 8 7.10456949966 7.10456949966 8 6 8 C 4.89543050034 8 4 7.10456949966 4 6 C 4 4.89543050034 4.89543050034 4 6 4 Z"/> + <path android:fillColor="?android:attr/colorPrimary" android:pathData="M 18 4 C 19.1045694997 4 20 4.89543050034 20 6 C 20 7.10456949966 19.1045694997 8 18 8 C 16.8954305003 8 16 7.10456949966 16 6 C 16 4.89543050034 16.8954305003 4 18 4 Z"/> + <path android:fillColor="?android:attr/colorPrimary" android:pathData="M 6 16 C 7.10456949966 16 8 16.8954305003 8 18 C 8 19.1045694997 7.10456949966 20 6 20 C 4.89543050034 20 4 19.1045694997 4 18 C 4 16.8954305003 4.89543050034 16 6 16 Z"/> + <path android:fillColor="?android:attr/colorPrimary" android:pathData="M 12 16 C 13.1045694997 16 14 16.8954305003 14 18 C 14 19.1045694997 13.1045694997 20 12 20 C 10.8954305003 20 10 19.1045694997 10 18 C 10 16.8954305003 10.8954305003 16 12 16 Z"/> + <path android:fillColor="?android:attr/colorPrimary" android:pathData="M 18 10 C 19.1045694997 10 20 10.8954305003 20 12 C 20 13.1045694997 19.1045694997 14 18 14 C 16.8954305003 14 16 13.1045694997 16 12 C 16 10.8954305003 16.8954305003 10 18 10 Z"/> + <path android:fillColor="?android:attr/colorPrimary" android:pathData="M 18 16 C 19.1045694997 16 20 16.8954305003 20 18 C 20 19.1045694997 19.1045694997 20 18 20 C 16.8954305003 20 16 19.1045694997 16 18 C 16 16.8954305003 16.8954305003 16 18 16 Z"/> + <path android:fillColor="?android:attr/colorPrimary" android:pathData="M 12 10 C 13.1045694997 10 14 10.8954305003 14 12 C 14 13.1045694997 13.1045694997 14 12 14 C 10.8954305003 14 10 13.1045694997 10 12 C 10 10.8954305003 10.8954305003 10 12 10 Z"/> + <path android:fillColor="?android:attr/colorPrimary" android:pathData="M 12 4 C 13.1045694997 4 14 4.89543050034 14 6 C 14 7.10456949966 13.1045694997 8 12 8 C 10.8954305003 8 10 7.10456949966 10 6 C 10 4.89543050034 10.8954305003 4 12 4 Z"/> </vector>
\ No newline at end of file diff --git a/packages/overlays/IconPackSamSettingsOverlay/res/drawable/ic_devices_other.xml b/packages/overlays/IconPackSamSettingsOverlay/res/drawable/ic_devices_other.xml index 733fe7ffaa98..c692eebb1adf 100644 --- a/packages/overlays/IconPackSamSettingsOverlay/res/drawable/ic_devices_other.xml +++ b/packages/overlays/IconPackSamSettingsOverlay/res/drawable/ic_devices_other.xml @@ -14,5 +14,5 @@ limitations under the License. --> <vector android:autoMirrored="true" android:height="24dp" android:viewportHeight="24" android:viewportWidth="24" android:width="24dp" xmlns:android="http://schemas.android.com/apk/res/android"> - <path android:fillColor="@android:color/white" android:pathData="M3,7c0-0.55,0.45-1,1-1h16c0.55,0,1-0.45,1-1v0c0-0.55-0.45-1-1-1H4C2.35,4,1,5.35,1,7v10c0,1.65,1.35,3,3,3h2 c0.55,0,1-0.45,1-1v0c0-0.55-0.45-1-1-1H4c-0.55,0-1-0.45-1-1V7z M12,12h-2c-0.55,0-1,0.45-1,1v0.78C8.39,14.33,8,15.11,8,16 c0,0.89,0.39,1.67,1,2.22V19c0,0.55,0.45,1,1,1h2c0.55,0,1-0.45,1-1v-0.78c0.61-0.55,1-1.34,1-2.22c0-0.88-0.39-1.67-1-2.22V13 C13,12.45,12.55,12,12,12z M11,17.5c-0.83,0-1.5-0.67-1.5-1.5s0.67-1.5,1.5-1.5s1.5,0.67,1.5,1.5S11.83,17.5,11,17.5 M17,8 c-1.1,0-2,0.9-2,2v8c0,1.1,0.9,2,2,2h4c1.1,0,2-0.9,2-2v-8c0-1.1-0.9-2-2-2H17z M21,18h-4v-8h4V18z"/> + <path android:fillColor="?android:attr/colorPrimary" android:pathData="M3,7c0-0.55,0.45-1,1-1h16c0.55,0,1-0.45,1-1v0c0-0.55-0.45-1-1-1H4C2.35,4,1,5.35,1,7v10c0,1.65,1.35,3,3,3h2 c0.55,0,1-0.45,1-1v0c0-0.55-0.45-1-1-1H4c-0.55,0-1-0.45-1-1V7z M12,12h-2c-0.55,0-1,0.45-1,1v0.78C8.39,14.33,8,15.11,8,16 c0,0.89,0.39,1.67,1,2.22V19c0,0.55,0.45,1,1,1h2c0.55,0,1-0.45,1-1v-0.78c0.61-0.55,1-1.34,1-2.22c0-0.88-0.39-1.67-1-2.22V13 C13,12.45,12.55,12,12,12z M11,17.5c-0.83,0-1.5-0.67-1.5-1.5s0.67-1.5,1.5-1.5s1.5,0.67,1.5,1.5S11.83,17.5,11,17.5 M17,8 c-1.1,0-2,0.9-2,2v8c0,1.1,0.9,2,2,2h4c1.1,0,2-0.9,2-2v-8c0-1.1-0.9-2-2-2H17z M21,18h-4v-8h4V18z"/> </vector>
\ No newline at end of file diff --git a/packages/overlays/IconPackSamSettingsOverlay/res/drawable/ic_help.xml b/packages/overlays/IconPackSamSettingsOverlay/res/drawable/ic_help.xml index 4650a72605ea..5a8185a3b173 100644 --- a/packages/overlays/IconPackSamSettingsOverlay/res/drawable/ic_help.xml +++ b/packages/overlays/IconPackSamSettingsOverlay/res/drawable/ic_help.xml @@ -14,5 +14,5 @@ limitations under the License. --> <vector android:height="24dp" android:viewportHeight="24" android:viewportWidth="24" android:width="24dp" xmlns:android="http://schemas.android.com/apk/res/android"> - <path android:fillColor="@android:color/white" android:pathData="M12,2C6.48,2,2,6.48,2,12c0,5.52,4.48,10,10,10s10-4.48,10-10C22,6.48,17.52,2,12,2z M12,18c-0.55,0-1-0.45-1-1 c0-0.55,0.45-1,1-1s1,0.45,1,1C13,17.55,12.55,18,12,18z M13.1,14.32C12.98,14.71,12.65,15,12.24,15h-0.15 c-0.62,0-1.11-0.6-0.93-1.2c0.61-1.97,2.71-2.08,2.83-3.66c0.07-0.97-0.62-1.9-1.57-2.1c-1.04-0.22-1.98,0.39-2.3,1.28 C9.98,9.71,9.65,10,9.24,10H9.07C8.45,10,8,9.4,8.18,8.81C8.68,7.18,10.2,6,12,6c2.21,0,4,1.79,4,4 C16,12.22,13.63,12.67,13.1,14.32z"/> + <path android:fillColor="?android:attr/colorPrimary" android:pathData="M12,2C6.48,2,2,6.48,2,12c0,5.52,4.48,10,10,10s10-4.48,10-10C22,6.48,17.52,2,12,2z M12,18c-0.55,0-1-0.45-1-1 c0-0.55,0.45-1,1-1s1,0.45,1,1C13,17.55,12.55,18,12,18z M13.1,14.32C12.98,14.71,12.65,15,12.24,15h-0.15 c-0.62,0-1.11-0.6-0.93-1.2c0.61-1.97,2.71-2.08,2.83-3.66c0.07-0.97-0.62-1.9-1.57-2.1c-1.04-0.22-1.98,0.39-2.3,1.28 C9.98,9.71,9.65,10,9.24,10H9.07C8.45,10,8,9.4,8.18,8.81C8.68,7.18,10.2,6,12,6c2.21,0,4,1.79,4,4 C16,12.22,13.63,12.67,13.1,14.32z"/> </vector>
\ No newline at end of file diff --git a/packages/overlays/IconPackSamSettingsOverlay/res/drawable/ic_phone_info.xml b/packages/overlays/IconPackSamSettingsOverlay/res/drawable/ic_phone_info.xml index dac1deef3e20..54336d0ca1ff 100644 --- a/packages/overlays/IconPackSamSettingsOverlay/res/drawable/ic_phone_info.xml +++ b/packages/overlays/IconPackSamSettingsOverlay/res/drawable/ic_phone_info.xml @@ -14,7 +14,7 @@ limitations under the License. --> <vector android:height="24dp" android:viewportHeight="24" android:viewportWidth="24" android:width="24dp" xmlns:android="http://schemas.android.com/apk/res/android"> - <path android:fillColor="@android:color/white" android:pathData="M12,11L12,11c0.55,0,1,0.45,1,1v4c0,0.55-0.45,1-1,1c-0.55,0-1-0.45-1-1v-4C11,11.45,11.45,11,12,11"/> - <path android:fillColor="@android:color/white" android:pathData="M16,1H8C6.34,1,5,2.34,5,4v16c0,1.65,1.35,3,3,3h8c1.65,0,3-1.35,3-3V4C19,2.34,17.66,1,16,1 M17,18H7V6h10V18z"/> - <path android:fillColor="@android:color/white" android:pathData="M13,8c0,0.55-0.45,1-1,1c-0.55,0-1-0.45-1-1c0-0.55,0.45-1,1-1C12.55,7,13,7.45,13,8"/> + <path android:fillColor="?android:attr/colorPrimary" android:pathData="M12,11L12,11c0.55,0,1,0.45,1,1v4c0,0.55-0.45,1-1,1c-0.55,0-1-0.45-1-1v-4C11,11.45,11.45,11,12,11"/> + <path android:fillColor="?android:attr/colorPrimary" android:pathData="M16,1H8C6.34,1,5,2.34,5,4v16c0,1.65,1.35,3,3,3h8c1.65,0,3-1.35,3-3V4C19,2.34,17.66,1,16,1 M17,18H7V6h10V18z"/> + <path android:fillColor="?android:attr/colorPrimary" android:pathData="M13,8c0,0.55-0.45,1-1,1c-0.55,0-1-0.45-1-1c0-0.55,0.45-1,1-1C12.55,7,13,7.45,13,8"/> </vector>
\ No newline at end of file diff --git a/packages/overlays/IconPackSamSettingsOverlay/res/drawable/ic_settings_accessibility.xml b/packages/overlays/IconPackSamSettingsOverlay/res/drawable/ic_settings_accessibility.xml index 3be42b3c88ab..4a4baf9121bb 100644 --- a/packages/overlays/IconPackSamSettingsOverlay/res/drawable/ic_settings_accessibility.xml +++ b/packages/overlays/IconPackSamSettingsOverlay/res/drawable/ic_settings_accessibility.xml @@ -14,9 +14,9 @@ limitations under the License. --> <vector android:height="24dp" android:viewportHeight="24" android:viewportWidth="24" android:width="24dp" xmlns:android="http://schemas.android.com/apk/res/android"> - <path android:fillColor="@android:color/white" android:pathData="M20.75,4.99c-0.14-0.55-0.69-0.87-1.24-0.75C17.13,4.77,14.48,5,12,5S6.87,4.77,4.49,4.24c-0.55-0.12-1.1,0.2-1.24,0.75 c-0.14,0.56,0.2,1.13,0.75,1.26C5.61,6.61,7.35,6.86,9,7v12c0,0.55,0.45,1,1,1s1-0.45,1-1v-4c0-0.55,0.45-1,1-1s1,0.45,1,1v4 c0,0.55,0.45,1,1,1s1-0.45,1-1V7c1.65-0.14,3.39-0.39,4.99-0.75C20.55,6.12,20.89,5.55,20.75,4.99z"/> - <path android:fillColor="@android:color/white" android:pathData="M 12 0 C 13.1045694997 0 14 0.895430500338 14 2 C 14 3.10456949966 13.1045694997 4 12 4 C 10.8954305003 4 10 3.10456949966 10 2 C 10 0.895430500338 10.8954305003 0 12 0 Z"/> - <path android:fillColor="@android:color/white" android:pathData="M 8 22 C 8.55228474983 22 9 22.4477152502 9 23 C 9 23.5522847498 8.55228474983 24 8 24 C 7.44771525017 24 7 23.5522847498 7 23 C 7 22.4477152502 7.44771525017 22 8 22 Z"/> - <path android:fillColor="@android:color/white" android:pathData="M 12 22 C 12.5522847498 22 13 22.4477152502 13 23 C 13 23.5522847498 12.5522847498 24 12 24 C 11.4477152502 24 11 23.5522847498 11 23 C 11 22.4477152502 11.4477152502 22 12 22 Z"/> - <path android:fillColor="@android:color/white" android:pathData="M 16 22 C 16.5522847498 22 17 22.4477152502 17 23 C 17 23.5522847498 16.5522847498 24 16 24 C 15.4477152502 24 15 23.5522847498 15 23 C 15 22.4477152502 15.4477152502 22 16 22 Z"/> + <path android:fillColor="?android:attr/colorPrimary" android:pathData="M20.75,4.99c-0.14-0.55-0.69-0.87-1.24-0.75C17.13,4.77,14.48,5,12,5S6.87,4.77,4.49,4.24c-0.55-0.12-1.1,0.2-1.24,0.75 c-0.14,0.56,0.2,1.13,0.75,1.26C5.61,6.61,7.35,6.86,9,7v12c0,0.55,0.45,1,1,1s1-0.45,1-1v-4c0-0.55,0.45-1,1-1s1,0.45,1,1v4 c0,0.55,0.45,1,1,1s1-0.45,1-1V7c1.65-0.14,3.39-0.39,4.99-0.75C20.55,6.12,20.89,5.55,20.75,4.99z"/> + <path android:fillColor="?android:attr/colorPrimary" android:pathData="M 12 0 C 13.1045694997 0 14 0.895430500338 14 2 C 14 3.10456949966 13.1045694997 4 12 4 C 10.8954305003 4 10 3.10456949966 10 2 C 10 0.895430500338 10.8954305003 0 12 0 Z"/> + <path android:fillColor="?android:attr/colorPrimary" android:pathData="M 8 22 C 8.55228474983 22 9 22.4477152502 9 23 C 9 23.5522847498 8.55228474983 24 8 24 C 7.44771525017 24 7 23.5522847498 7 23 C 7 22.4477152502 7.44771525017 22 8 22 Z"/> + <path android:fillColor="?android:attr/colorPrimary" android:pathData="M 12 22 C 12.5522847498 22 13 22.4477152502 13 23 C 13 23.5522847498 12.5522847498 24 12 24 C 11.4477152502 24 11 23.5522847498 11 23 C 11 22.4477152502 11.4477152502 22 12 22 Z"/> + <path android:fillColor="?android:attr/colorPrimary" android:pathData="M 16 22 C 16.5522847498 22 17 22.4477152502 17 23 C 17 23.5522847498 16.5522847498 24 16 24 C 15.4477152502 24 15 23.5522847498 15 23 C 15 22.4477152502 15.4477152502 22 16 22 Z"/> </vector>
\ No newline at end of file diff --git a/packages/overlays/IconPackSamSettingsOverlay/res/drawable/ic_settings_accounts.xml b/packages/overlays/IconPackSamSettingsOverlay/res/drawable/ic_settings_accounts.xml index a9bf036b284f..334b2b7bde48 100644 --- a/packages/overlays/IconPackSamSettingsOverlay/res/drawable/ic_settings_accounts.xml +++ b/packages/overlays/IconPackSamSettingsOverlay/res/drawable/ic_settings_accounts.xml @@ -14,6 +14,6 @@ limitations under the License. --> <vector android:height="24dp" android:viewportHeight="24" android:viewportWidth="24" android:width="24dp" xmlns:android="http://schemas.android.com/apk/res/android"> - <path android:fillColor="@android:color/white" android:pathData="M6,3C4.34,3,3,4.34,3,6v12c0,1.66,1.34,3,3,3h12c1.65,0,3-1.35,3-3V6c0-1.66-1.34-3-3-3H6z M19,6v9.79 C16.52,14.37,13.23,14,12,14s-4.52,0.37-7,1.79V6c0-0.55,0.45-1,1-1h12C18.55,5,19,5.45,19,6z"/> - <path android:fillColor="@android:color/white" android:pathData="M12,13c1.94,0,3.5-1.56,3.5-3.5S13.94,6,12,6S8.5,7.56,8.5,9.5S10.06,13,12,13"/> + <path android:fillColor="?android:attr/colorPrimary" android:pathData="M6,3C4.34,3,3,4.34,3,6v12c0,1.66,1.34,3,3,3h12c1.65,0,3-1.35,3-3V6c0-1.66-1.34-3-3-3H6z M19,6v9.79 C16.52,14.37,13.23,14,12,14s-4.52,0.37-7,1.79V6c0-0.55,0.45-1,1-1h12C18.55,5,19,5.45,19,6z"/> + <path android:fillColor="?android:attr/colorPrimary" android:pathData="M12,13c1.94,0,3.5-1.56,3.5-3.5S13.94,6,12,6S8.5,7.56,8.5,9.5S10.06,13,12,13"/> </vector>
\ No newline at end of file diff --git a/packages/overlays/IconPackSamSettingsOverlay/res/drawable/ic_settings_battery_white.xml b/packages/overlays/IconPackSamSettingsOverlay/res/drawable/ic_settings_battery_white.xml index d616ad6d0886..fff56cede23f 100644 --- a/packages/overlays/IconPackSamSettingsOverlay/res/drawable/ic_settings_battery_white.xml +++ b/packages/overlays/IconPackSamSettingsOverlay/res/drawable/ic_settings_battery_white.xml @@ -14,5 +14,5 @@ limitations under the License. --> <vector android:height="24dp" android:viewportHeight="24" android:viewportWidth="24" android:width="24dp" xmlns:android="http://schemas.android.com/apk/res/android"> - <path android:fillColor="@android:color/white" android:pathData="M15,4h-1c0-1.1-0.9-2-2-2s-2,0.9-2,2H9C7.35,4,6,5.35,6,7v12c0,1.65,1.35,3,3,3h6c1.65,0,3-1.35,3-3V7 C18,5.35,16.65,4,15,4z"/> + <path android:fillColor="?android:attr/colorPrimary" android:pathData="M15,4h-1c0-1.1-0.9-2-2-2s-2,0.9-2,2H9C7.35,4,6,5.35,6,7v12c0,1.65,1.35,3,3,3h6c1.65,0,3-1.35,3-3V7 C18,5.35,16.65,4,15,4z"/> </vector>
\ No newline at end of file diff --git a/packages/overlays/IconPackSamSettingsOverlay/res/drawable/ic_settings_display_white.xml b/packages/overlays/IconPackSamSettingsOverlay/res/drawable/ic_settings_display_white.xml index cf4af6f1e1fd..723c36c669ea 100644 --- a/packages/overlays/IconPackSamSettingsOverlay/res/drawable/ic_settings_display_white.xml +++ b/packages/overlays/IconPackSamSettingsOverlay/res/drawable/ic_settings_display_white.xml @@ -14,6 +14,6 @@ limitations under the License. --> <vector android:height="24dp" android:viewportHeight="24" android:viewportWidth="24" android:width="24dp" xmlns:android="http://schemas.android.com/apk/res/android"> - <path android:fillColor="@android:color/white" android:pathData="M13.25,7.16C12.62,6.99,12,7.48,12,8.13v7.74c0,0.65,0.62,1.14,1.25,0.97C15.41,16.29,17,14.33,17,12 S15.41,7.71,13.25,7.16z"/> - <path android:fillColor="@android:color/white" android:pathData="M21.19,9.88l-0.9-0.9C20.1,8.8,20,8.54,20,8.28V7c0-1.66-1.34-3-3-3h-1.28c-0.27,0-0.52-0.11-0.71-0.29l-0.9-0.9 c-1.17-1.17-3.07-1.17-4.24,0l-0.9,0.9C8.79,3.89,8.54,4,8.28,4H7C5.34,4,4,5.34,4,7v1.28C4,8.54,3.89,8.8,3.71,8.98l-0.9,0.9 c-1.17,1.17-1.17,3.07,0,4.24l0.9,0.9C3.89,15.2,4,15.46,4,15.72V17c0,1.66,1.34,3,3,3h1.28c0.27,0,0.52,0.11,0.71,0.29l0.9,0.9 c1.17,1.17,3.07,1.17,4.24,0l0.9-0.9C15.2,20.11,15.46,20,15.72,20H17c1.66,0,3-1.34,3-3v-1.28c0-0.27,0.11-0.52,0.29-0.71 l0.9-0.9C22.36,12.95,22.36,11.05,21.19,9.88z M19.77,12.71l-1.48,1.48C18.11,14.37,18,14.63,18,14.89V17c0,0.55-0.45,1-1,1h-2.11 c-0.27,0-0.52,0.11-0.71,0.29l-1.48,1.48c-0.39,0.39-1.02,0.39-1.41,0l-1.48-1.48C9.63,18.1,9.37,18,9.11,18H7c-0.55,0-1-0.45-1-1 v-2.1c0-0.27-0.11-0.52-0.29-0.71l-1.48-1.48c-0.39-0.39-0.39-1.02,0-1.41l1.48-1.48C5.9,9.63,6,9.37,6,9.11V7c0-0.55,0.45-1,1-1 h2.11c0.27,0,0.52-0.11,0.71-0.29l1.48-1.48c0.39-0.39,1.02-0.39,1.41,0l1.48,1.48C14.38,5.89,14.63,6,14.89,6H17 c0.55,0,1,0.45,1,1v2.11c0,0.27,0.11,0.52,0.29,0.71l1.48,1.48C20.16,11.68,20.16,12.32,19.77,12.71z"/> + <path android:fillColor="?android:attr/colorPrimary" android:pathData="M13.25,7.16C12.62,6.99,12,7.48,12,8.13v7.74c0,0.65,0.62,1.14,1.25,0.97C15.41,16.29,17,14.33,17,12 S15.41,7.71,13.25,7.16z"/> + <path android:fillColor="?android:attr/colorPrimary" android:pathData="M21.19,9.88l-0.9-0.9C20.1,8.8,20,8.54,20,8.28V7c0-1.66-1.34-3-3-3h-1.28c-0.27,0-0.52-0.11-0.71-0.29l-0.9-0.9 c-1.17-1.17-3.07-1.17-4.24,0l-0.9,0.9C8.79,3.89,8.54,4,8.28,4H7C5.34,4,4,5.34,4,7v1.28C4,8.54,3.89,8.8,3.71,8.98l-0.9,0.9 c-1.17,1.17-1.17,3.07,0,4.24l0.9,0.9C3.89,15.2,4,15.46,4,15.72V17c0,1.66,1.34,3,3,3h1.28c0.27,0,0.52,0.11,0.71,0.29l0.9,0.9 c1.17,1.17,3.07,1.17,4.24,0l0.9-0.9C15.2,20.11,15.46,20,15.72,20H17c1.66,0,3-1.34,3-3v-1.28c0-0.27,0.11-0.52,0.29-0.71 l0.9-0.9C22.36,12.95,22.36,11.05,21.19,9.88z M19.77,12.71l-1.48,1.48C18.11,14.37,18,14.63,18,14.89V17c0,0.55-0.45,1-1,1h-2.11 c-0.27,0-0.52,0.11-0.71,0.29l-1.48,1.48c-0.39,0.39-1.02,0.39-1.41,0l-1.48-1.48C9.63,18.1,9.37,18,9.11,18H7c-0.55,0-1-0.45-1-1 v-2.1c0-0.27-0.11-0.52-0.29-0.71l-1.48-1.48c-0.39-0.39-0.39-1.02,0-1.41l1.48-1.48C5.9,9.63,6,9.37,6,9.11V7c0-0.55,0.45-1,1-1 h2.11c0.27,0,0.52-0.11,0.71-0.29l1.48-1.48c0.39-0.39,1.02-0.39,1.41,0l1.48,1.48C14.38,5.89,14.63,6,14.89,6H17 c0.55,0,1,0.45,1,1v2.11c0,0.27,0.11,0.52,0.29,0.71l1.48,1.48C20.16,11.68,20.16,12.32,19.77,12.71z"/> </vector>
\ No newline at end of file diff --git a/packages/overlays/IconPackSamSettingsOverlay/res/drawable/ic_settings_location.xml b/packages/overlays/IconPackSamSettingsOverlay/res/drawable/ic_settings_location.xml index a802bee9af5f..9230e3eb8a08 100644 --- a/packages/overlays/IconPackSamSettingsOverlay/res/drawable/ic_settings_location.xml +++ b/packages/overlays/IconPackSamSettingsOverlay/res/drawable/ic_settings_location.xml @@ -14,5 +14,5 @@ limitations under the License. --> <vector android:height="24dp" android:viewportHeight="24" android:viewportWidth="24" android:width="24dp" xmlns:android="http://schemas.android.com/apk/res/android"> - <path android:fillColor="@android:color/white" android:pathData="M12,3c-3.87,0-7,3.13-7,7c0,3.18,2.56,7.05,4.59,9.78c1.2,1.62,3.62,1.62,4.82,0C16.44,17.05,19,13.18,19,10 C19,6.13,15.87,3,12,3 M12,12.5c-1.38,0-2.5-1.12-2.5-2.5s1.12-2.5,2.5-2.5s2.5,1.12,2.5,2.5S13.38,12.5,12,12.5"/> + <path android:fillColor="?android:attr/colorPrimary" android:pathData="M12,3c-3.87,0-7,3.13-7,7c0,3.18,2.56,7.05,4.59,9.78c1.2,1.62,3.62,1.62,4.82,0C16.44,17.05,19,13.18,19,10 C19,6.13,15.87,3,12,3 M12,12.5c-1.38,0-2.5-1.12-2.5-2.5s1.12-2.5,2.5-2.5s2.5,1.12,2.5,2.5S13.38,12.5,12,12.5"/> </vector>
\ No newline at end of file diff --git a/packages/overlays/IconPackSamSettingsOverlay/res/drawable/ic_settings_privacy.xml b/packages/overlays/IconPackSamSettingsOverlay/res/drawable/ic_settings_privacy.xml index 10c059f9f592..4392c121c4ff 100644 --- a/packages/overlays/IconPackSamSettingsOverlay/res/drawable/ic_settings_privacy.xml +++ b/packages/overlays/IconPackSamSettingsOverlay/res/drawable/ic_settings_privacy.xml @@ -14,7 +14,7 @@ limitations under the License. --> <vector android:height="24dp" android:viewportHeight="24" android:viewportWidth="24" android:width="24dp" xmlns:android="http://schemas.android.com/apk/res/android"> - <path android:fillColor="@android:color/white" android:pathData="M14.14,15.43C13.5,15.78,12.78,16,12,16c-2.48,0-4.5-2.02-4.5-4.5C7.5,9.02,9.52,7,12,7c2.48,0,4.5,2.02,4.5,4.5 c0,0.37-0.06,0.72-0.14,1.07C17,12.22,17.72,12,18.5,12c1.38,0,2.59,0.63,3.42,1.6c0.15-0.23,0.29-0.46,0.42-0.69 c0.48-0.87,0.48-1.95,0-2.81C20.32,6.46,16.45,4,12,4C7.55,4,3.68,6.46,1.66,10.09c-0.48,0.87-0.48,1.95,0,2.81 C3.68,16.54,7.55,19,12,19c0.68,0,1.35-0.06,2-0.18V16.5C14,16.13,14.06,15.78,14.14,15.43z"/> - <path android:fillColor="@android:color/white" android:pathData="M 12 8.8 C 13.4911688245 8.8 14.7 10.0088311755 14.7 11.5 C 14.7 12.9911688245 13.4911688245 14.2 12 14.2 C 10.5088311755 14.2 9.3 12.9911688245 9.3 11.5 C 9.3 10.0088311755 10.5088311755 8.8 12 8.8 Z"/> - <path android:fillColor="@android:color/white" android:pathData="M21,17v-1c0-1.1-0.9-2-2-2s-2,0.9-2,2v1c-0.55,0-1,0.45-1,1v3c0,0.55,0.45,1,1,1h4c0.55,0,1-0.45,1-1v-3 C22,17.45,21.55,17,21,17z M18.5,16c0-0.28,0.22-0.5,0.5-0.5s0.5,0.22,0.5,0.5v1h-1V16z"/> + <path android:fillColor="?android:attr/colorPrimary" android:pathData="M14.14,15.43C13.5,15.78,12.78,16,12,16c-2.48,0-4.5-2.02-4.5-4.5C7.5,9.02,9.52,7,12,7c2.48,0,4.5,2.02,4.5,4.5 c0,0.37-0.06,0.72-0.14,1.07C17,12.22,17.72,12,18.5,12c1.38,0,2.59,0.63,3.42,1.6c0.15-0.23,0.29-0.46,0.42-0.69 c0.48-0.87,0.48-1.95,0-2.81C20.32,6.46,16.45,4,12,4C7.55,4,3.68,6.46,1.66,10.09c-0.48,0.87-0.48,1.95,0,2.81 C3.68,16.54,7.55,19,12,19c0.68,0,1.35-0.06,2-0.18V16.5C14,16.13,14.06,15.78,14.14,15.43z"/> + <path android:fillColor="?android:attr/colorPrimary" android:pathData="M 12 8.8 C 13.4911688245 8.8 14.7 10.0088311755 14.7 11.5 C 14.7 12.9911688245 13.4911688245 14.2 12 14.2 C 10.5088311755 14.2 9.3 12.9911688245 9.3 11.5 C 9.3 10.0088311755 10.5088311755 8.8 12 8.8 Z"/> + <path android:fillColor="?android:attr/colorPrimary" android:pathData="M21,17v-1c0-1.1-0.9-2-2-2s-2,0.9-2,2v1c-0.55,0-1,0.45-1,1v3c0,0.55,0.45,1,1,1h4c0.55,0,1-0.45,1-1v-3 C22,17.45,21.55,17,21,17z M18.5,16c0-0.28,0.22-0.5,0.5-0.5s0.5,0.22,0.5,0.5v1h-1V16z"/> </vector>
\ No newline at end of file diff --git a/packages/overlays/IconPackSamSettingsOverlay/res/drawable/ic_settings_security_white.xml b/packages/overlays/IconPackSamSettingsOverlay/res/drawable/ic_settings_security_white.xml index 8ca2dd6d2589..baa6a0a11700 100644 --- a/packages/overlays/IconPackSamSettingsOverlay/res/drawable/ic_settings_security_white.xml +++ b/packages/overlays/IconPackSamSettingsOverlay/res/drawable/ic_settings_security_white.xml @@ -14,5 +14,5 @@ limitations under the License. --> <vector android:height="24dp" android:viewportHeight="24" android:viewportWidth="24" android:width="24dp" xmlns:android="http://schemas.android.com/apk/res/android"> - <path android:fillColor="@android:color/white" android:pathData="M18.5,1C16.01,1,14,3.01,14,5.5V8H7c-1.65,0-3,1.35-3,3v8c0,1.65,1.35,3,3,3h10c1.65,0,3-1.35,3-3v-8c0-1.65-1.35-3-3-3h-1 V5.64c0-1.31,0.94-2.5,2.24-2.63c0.52-0.05,2.3,0.02,2.69,2.12C21.02,5.63,21.42,6,21.92,6c0.6,0,1.09-0.53,0.99-1.13 C22.47,2.3,20.55,1,18.5,1z M12,17c-1.1,0-2-0.9-2-2s0.9-2,2-2s2,0.9,2,2S13.1,17,12,17z"/> + <path android:fillColor="?android:attr/colorPrimary" android:pathData="M18.5,1C16.01,1,14,3.01,14,5.5V8H7c-1.65,0-3,1.35-3,3v8c0,1.65,1.35,3,3,3h10c1.65,0,3-1.35,3-3v-8c0-1.65-1.35-3-3-3h-1 V5.64c0-1.31,0.94-2.5,2.24-2.63c0.52-0.05,2.3,0.02,2.69,2.12C21.02,5.63,21.42,6,21.92,6c0.6,0,1.09-0.53,0.99-1.13 C22.47,2.3,20.55,1,18.5,1z M12,17c-1.1,0-2-0.9-2-2s0.9-2,2-2s2,0.9,2,2S13.1,17,12,17z"/> </vector>
\ No newline at end of file diff --git a/packages/overlays/IconPackSamSettingsOverlay/res/drawable/ic_settings_system_dashboard_white.xml b/packages/overlays/IconPackSamSettingsOverlay/res/drawable/ic_settings_system_dashboard_white.xml index 437afcce6add..54aa6ce5c9ca 100644 --- a/packages/overlays/IconPackSamSettingsOverlay/res/drawable/ic_settings_system_dashboard_white.xml +++ b/packages/overlays/IconPackSamSettingsOverlay/res/drawable/ic_settings_system_dashboard_white.xml @@ -14,5 +14,5 @@ limitations under the License. --> <vector android:height="24dp" android:viewportHeight="24" android:viewportWidth="24" android:width="24dp" xmlns:android="http://schemas.android.com/apk/res/android"> - <path android:fillColor="@android:color/white" android:pathData="M12,2C6.48,2,2,6.48,2,12c0,5.52,4.48,10,10,10s10-4.48,10-10C22,6.48,17.52,2,12,2z M13,16c0,0.55-0.45,1-1,1s-1-0.45-1-1 v-4c0-0.55,0.45-1,1-1s1,0.45,1,1V16z M12,9c-0.55,0-1-0.45-1-1c0-0.55,0.45-1,1-1s1,0.45,1,1C13,8.55,12.55,9,12,9z"/> + <path android:fillColor="?android:attr/colorPrimary" android:pathData="M12,2C6.48,2,2,6.48,2,12c0,5.52,4.48,10,10,10s10-4.48,10-10C22,6.48,17.52,2,12,2z M13,16c0,0.55-0.45,1-1,1s-1-0.45-1-1 v-4c0-0.55,0.45-1,1-1s1,0.45,1,1V16z M12,9c-0.55,0-1-0.45-1-1c0-0.55,0.45-1,1-1s1,0.45,1,1C13,8.55,12.55,9,12,9z"/> </vector>
\ No newline at end of file diff --git a/packages/overlays/IconPackSamSettingsOverlay/res/drawable/ic_settings_wireless.xml b/packages/overlays/IconPackSamSettingsOverlay/res/drawable/ic_settings_wireless.xml index 145886a49ce2..c40f314e8c78 100644 --- a/packages/overlays/IconPackSamSettingsOverlay/res/drawable/ic_settings_wireless.xml +++ b/packages/overlays/IconPackSamSettingsOverlay/res/drawable/ic_settings_wireless.xml @@ -14,7 +14,7 @@ limitations under the License. --> <vector android:height="24dp" android:tint="?android:attr/colorControlNormal" android:viewportHeight="24" android:viewportWidth="24" android:width="24dp" xmlns:android="http://schemas.android.com/apk/res/android"> - <path android:fillColor="@android:color/white" android:pathData="M17.79,11.97c-3.7-2.67-8.4-2.29-11.58,0c-0.69,0.5-0.73,1.51-0.13,2.11l0.01,0.01c0.49,0.49,1.26,0.54,1.83,0.13 c2.6-1.84,5.88-1.61,8.16,0c0.57,0.4,1.34,0.36,1.83-0.13l0.01-0.01C18.52,13.48,18.48,12.47,17.79,11.97z"/> - <path android:fillColor="@android:color/white" android:pathData="M21.84,7.95c-5.71-4.68-13.97-4.67-19.69,0c-0.65,0.53-0.69,1.51-0.1,2.1c0.51,0.51,1.33,0.55,1.89,0.09 c3.45-2.83,10.36-4.72,16.11,0c0.56,0.46,1.38,0.42,1.89-0.09C22.54,9.46,22.49,8.48,21.84,7.95z"/> - <path android:fillColor="@android:color/white" android:pathData="M 12 15 C 13.1045694997 15 14 15.8954305003 14 17 C 14 18.1045694997 13.1045694997 19 12 19 C 10.8954305003 19 10 18.1045694997 10 17 C 10 15.8954305003 10.8954305003 15 12 15 Z"/> + <path android:fillColor="?android:attr/colorPrimary" android:pathData="M17.79,11.97c-3.7-2.67-8.4-2.29-11.58,0c-0.69,0.5-0.73,1.51-0.13,2.11l0.01,0.01c0.49,0.49,1.26,0.54,1.83,0.13 c2.6-1.84,5.88-1.61,8.16,0c0.57,0.4,1.34,0.36,1.83-0.13l0.01-0.01C18.52,13.48,18.48,12.47,17.79,11.97z"/> + <path android:fillColor="?android:attr/colorPrimary" android:pathData="M21.84,7.95c-5.71-4.68-13.97-4.67-19.69,0c-0.65,0.53-0.69,1.51-0.1,2.1c0.51,0.51,1.33,0.55,1.89,0.09 c3.45-2.83,10.36-4.72,16.11,0c0.56,0.46,1.38,0.42,1.89-0.09C22.54,9.46,22.49,8.48,21.84,7.95z"/> + <path android:fillColor="?android:attr/colorPrimary" android:pathData="M 12 15 C 13.1045694997 15 14 15.8954305003 14 17 C 14 18.1045694997 13.1045694997 19 12 19 C 10.8954305003 19 10 18.1045694997 10 17 C 10 15.8954305003 10.8954305003 15 12 15 Z"/> </vector>
\ No newline at end of file diff --git a/packages/overlays/IconPackSamSettingsOverlay/res/drawable/ic_storage_white.xml b/packages/overlays/IconPackSamSettingsOverlay/res/drawable/ic_storage_white.xml index 3ed51506752d..2ae828d8be55 100644 --- a/packages/overlays/IconPackSamSettingsOverlay/res/drawable/ic_storage_white.xml +++ b/packages/overlays/IconPackSamSettingsOverlay/res/drawable/ic_storage_white.xml @@ -14,7 +14,7 @@ limitations under the License. --> <vector android:height="24dp" android:viewportHeight="24" android:viewportWidth="24" android:width="24dp" xmlns:android="http://schemas.android.com/apk/res/android"> - <path android:fillColor="@android:color/white" android:pathData="M19,16H5c-1.1,0-2,0.9-2,2c0,1.1,0.9,2,2,2h14c1.1,0,2-0.9,2-2C21,16.9,20.1,16,19,16z M6,19c-0.55,0-1-0.45-1-1 c0-0.55,0.45-1,1-1s1,0.45,1,1C7,18.55,6.55,19,6,19z"/> - <path android:fillColor="@android:color/white" android:pathData="M5,8h14c1.1,0,2-0.9,2-2c0-1.1-0.9-2-2-2H5C3.9,4,3,4.9,3,6C3,7.1,3.9,8,5,8z M6,5c0.55,0,1,0.45,1,1c0,0.55-0.45,1-1,1 S5,6.55,5,6C5,5.45,5.45,5,6,5z"/> - <path android:fillColor="@android:color/white" android:pathData="M19,10H5c-1.1,0-2,0.9-2,2c0,1.1,0.9,2,2,2h14c1.1,0,2-0.9,2-2C21,10.9,20.1,10,19,10z M6,13c-0.55,0-1-0.45-1-1 c0-0.55,0.45-1,1-1s1,0.45,1,1C7,12.55,6.55,13,6,13z"/> + <path android:fillColor="?android:attr/colorPrimary" android:pathData="M19,16H5c-1.1,0-2,0.9-2,2c0,1.1,0.9,2,2,2h14c1.1,0,2-0.9,2-2C21,16.9,20.1,16,19,16z M6,19c-0.55,0-1-0.45-1-1 c0-0.55,0.45-1,1-1s1,0.45,1,1C7,18.55,6.55,19,6,19z"/> + <path android:fillColor="?android:attr/colorPrimary" android:pathData="M5,8h14c1.1,0,2-0.9,2-2c0-1.1-0.9-2-2-2H5C3.9,4,3,4.9,3,6C3,7.1,3.9,8,5,8z M6,5c0.55,0,1,0.45,1,1c0,0.55-0.45,1-1,1 S5,6.55,5,6C5,5.45,5.45,5,6,5z"/> + <path android:fillColor="?android:attr/colorPrimary" android:pathData="M19,10H5c-1.1,0-2,0.9-2,2c0,1.1,0.9,2,2,2h14c1.1,0,2-0.9,2-2C21,10.9,20.1,10,19,10z M6,13c-0.55,0-1-0.45-1-1 c0-0.55,0.45-1,1-1s1,0.45,1,1C7,12.55,6.55,13,6,13z"/> </vector>
\ No newline at end of file diff --git a/packages/overlays/IconPackSamSettingsOverlay/res/drawable/ic_volume_up_24dp.xml b/packages/overlays/IconPackSamSettingsOverlay/res/drawable/ic_volume_up_24dp.xml index 0a9a4c79bb72..4fc2489d6541 100644 --- a/packages/overlays/IconPackSamSettingsOverlay/res/drawable/ic_volume_up_24dp.xml +++ b/packages/overlays/IconPackSamSettingsOverlay/res/drawable/ic_volume_up_24dp.xml @@ -14,7 +14,7 @@ limitations under the License. --> <vector android:height="24dp" android:viewportHeight="24" android:viewportWidth="24" android:width="24dp" xmlns:android="http://schemas.android.com/apk/res/android"> - <path android:fillColor="@android:color/white" android:pathData="M10.29,5.71L7,9H6c-1.66,0-3,1.34-3,3s1.34,3,3,3h1l3.29,3.29c0.63,0.63,1.71,0.18,1.71-0.71V6.41 C12,5.52,10.92,5.08,10.29,5.71z"/> - <path android:fillColor="@android:color/white" android:pathData="M14.79,15.52c1.04-0.82,1.71-2.09,1.71-3.52c0-1.43-0.67-2.7-1.71-3.52C14.47,8.22,14,8.47,14,8.88v6.23 C14,15.52,14.47,15.77,14.79,15.52z"/> - <path android:fillColor="@android:color/white" android:pathData="M15.36,3.65C14.71,3.39,14,3.89,14,4.59v0c0,0.41,0.25,0.77,0.62,0.92C17.19,6.55,19,9.06,19,12 c0,2.94-1.81,5.45-4.38,6.49C14.25,18.64,14,19.01,14,19.41v0c0,0.7,0.71,1.19,1.36,0.93C18.67,19.02,21,15.78,21,12 C21,8.22,18.67,4.98,15.36,3.65z"/> + <path android:fillColor="?android:attr/colorPrimary" android:pathData="M10.29,5.71L7,9H6c-1.66,0-3,1.34-3,3s1.34,3,3,3h1l3.29,3.29c0.63,0.63,1.71,0.18,1.71-0.71V6.41 C12,5.52,10.92,5.08,10.29,5.71z"/> + <path android:fillColor="?android:attr/colorPrimary" android:pathData="M14.79,15.52c1.04-0.82,1.71-2.09,1.71-3.52c0-1.43-0.67-2.7-1.71-3.52C14.47,8.22,14,8.47,14,8.88v6.23 C14,15.52,14.47,15.77,14.79,15.52z"/> + <path android:fillColor="?android:attr/colorPrimary" android:pathData="M15.36,3.65C14.71,3.39,14,3.89,14,4.59v0c0,0.41,0.25,0.77,0.62,0.92C17.19,6.55,19,9.06,19,12 c0,2.94-1.81,5.45-4.38,6.49C14.25,18.64,14,19.01,14,19.41v0c0,0.7,0.71,1.19,1.36,0.93C18.67,19.02,21,15.78,21,12 C21,8.22,18.67,4.98,15.36,3.65z"/> </vector>
\ No newline at end of file diff --git a/packages/overlays/IconPackVictorSettingsOverlay/res/drawable/ic_apps.xml b/packages/overlays/IconPackVictorSettingsOverlay/res/drawable/ic_apps.xml index ab58f2045f1a..d62b777592b7 100644 --- a/packages/overlays/IconPackVictorSettingsOverlay/res/drawable/ic_apps.xml +++ b/packages/overlays/IconPackVictorSettingsOverlay/res/drawable/ic_apps.xml @@ -14,13 +14,13 @@ limitations under the License. --> <vector android:height="24dp" android:viewportHeight="24" android:viewportWidth="24" android:width="24dp" xmlns:android="http://schemas.android.com/apk/res/android"> - <path android:fillColor="@android:color/white" android:pathData="M 4 4 H 8 V 8 H 4 V 4 Z"/> - <path android:fillColor="@android:color/white" android:pathData="M 4 10 H 8 V 14 H 4 V 10 Z"/> - <path android:fillColor="@android:color/white" android:pathData="M 4 16 H 8 V 20 H 4 V 16 Z"/> - <path android:fillColor="@android:color/white" android:pathData="M 10 4 H 14 V 8 H 10 V 4 Z"/> - <path android:fillColor="@android:color/white" android:pathData="M 10 10 H 14 V 14 H 10 V 10 Z"/> - <path android:fillColor="@android:color/white" android:pathData="M 10 16 H 14 V 20 H 10 V 16 Z"/> - <path android:fillColor="@android:color/white" android:pathData="M 16 4 H 20 V 8 H 16 V 4 Z"/> - <path android:fillColor="@android:color/white" android:pathData="M 16 10 H 20 V 14 H 16 V 10 Z"/> - <path android:fillColor="@android:color/white" android:pathData="M 16 16 H 20 V 20 H 16 V 16 Z"/> + <path android:fillColor="?android:attr/colorPrimary" android:pathData="M 4 4 H 8 V 8 H 4 V 4 Z"/> + <path android:fillColor="?android:attr/colorPrimary" android:pathData="M 4 10 H 8 V 14 H 4 V 10 Z"/> + <path android:fillColor="?android:attr/colorPrimary" android:pathData="M 4 16 H 8 V 20 H 4 V 16 Z"/> + <path android:fillColor="?android:attr/colorPrimary" android:pathData="M 10 4 H 14 V 8 H 10 V 4 Z"/> + <path android:fillColor="?android:attr/colorPrimary" android:pathData="M 10 10 H 14 V 14 H 10 V 10 Z"/> + <path android:fillColor="?android:attr/colorPrimary" android:pathData="M 10 16 H 14 V 20 H 10 V 16 Z"/> + <path android:fillColor="?android:attr/colorPrimary" android:pathData="M 16 4 H 20 V 8 H 16 V 4 Z"/> + <path android:fillColor="?android:attr/colorPrimary" android:pathData="M 16 10 H 20 V 14 H 16 V 10 Z"/> + <path android:fillColor="?android:attr/colorPrimary" android:pathData="M 16 16 H 20 V 20 H 16 V 16 Z"/> </vector>
\ No newline at end of file diff --git a/packages/overlays/IconPackVictorSettingsOverlay/res/drawable/ic_devices_other.xml b/packages/overlays/IconPackVictorSettingsOverlay/res/drawable/ic_devices_other.xml index 7548dfac18e0..71fb7a3d5096 100644 --- a/packages/overlays/IconPackVictorSettingsOverlay/res/drawable/ic_devices_other.xml +++ b/packages/overlays/IconPackVictorSettingsOverlay/res/drawable/ic_devices_other.xml @@ -14,7 +14,7 @@ limitations under the License. --> <vector android:autoMirrored="true" android:height="24dp" android:viewportHeight="24" android:viewportWidth="24" android:width="24dp" xmlns:android="http://schemas.android.com/apk/res/android"> - <path android:fillColor="@android:color/white" android:pathData="M21.5,8h-5L15,9.5v9l1.5,1.5h5l1.5-1.5v-9L21.5,8z M21.5,18.5h-5v-9h5V18.5z"/> - <path android:fillColor="@android:color/white" android:pathData="M 2.5 5.5 L 21 5.5 L 21 4 L 2.5 4 L 1 5.5 L 1 18.5 L 2.5 20 L 7 20 L 7 18.5 L 2.5 18.5 Z"/> - <path android:fillColor="@android:color/white" android:pathData="M13,12H9v1.78C8.39,14.33,8,15.11,8,16s0.39,1.67,1,2.22V20h4v-1.78c0.61-0.55,1-1.34,1-2.22s-0.39-1.67-1-2.22V12z M11,14.5c0.83,0,1.5,0.67,1.5,1.5s-0.67,1.5-1.5,1.5S9.5,16.83,9.5,16S10.17,14.5,11,14.5z"/> + <path android:fillColor="?android:attr/colorPrimary" android:pathData="M21.5,8h-5L15,9.5v9l1.5,1.5h5l1.5-1.5v-9L21.5,8z M21.5,18.5h-5v-9h5V18.5z"/> + <path android:fillColor="?android:attr/colorPrimary" android:pathData="M 2.5 5.5 L 21 5.5 L 21 4 L 2.5 4 L 1 5.5 L 1 18.5 L 2.5 20 L 7 20 L 7 18.5 L 2.5 18.5 Z"/> + <path android:fillColor="?android:attr/colorPrimary" android:pathData="M13,12H9v1.78C8.39,14.33,8,15.11,8,16s0.39,1.67,1,2.22V20h4v-1.78c0.61-0.55,1-1.34,1-2.22s-0.39-1.67-1-2.22V12z M11,14.5c0.83,0,1.5,0.67,1.5,1.5s-0.67,1.5-1.5,1.5S9.5,16.83,9.5,16S10.17,14.5,11,14.5z"/> </vector>
\ No newline at end of file diff --git a/packages/overlays/IconPackVictorSettingsOverlay/res/drawable/ic_help.xml b/packages/overlays/IconPackVictorSettingsOverlay/res/drawable/ic_help.xml index eb2e966d2bcb..32c603dc7a26 100644 --- a/packages/overlays/IconPackVictorSettingsOverlay/res/drawable/ic_help.xml +++ b/packages/overlays/IconPackVictorSettingsOverlay/res/drawable/ic_help.xml @@ -14,7 +14,7 @@ limitations under the License. --> <vector android:height="24dp" android:viewportHeight="24" android:viewportWidth="24" android:width="24dp" xmlns:android="http://schemas.android.com/apk/res/android"> - <path android:fillColor="@android:color/white" android:pathData="M12,2C6.48,2,2,6.48,2,12s4.48,10,10,10s10-4.48,10-10S17.52,2,12,2z M12,20.5c-4.69,0-8.5-3.81-8.5-8.5S7.31,3.5,12,3.5 s8.5,3.81,8.5,8.5S16.69,20.5,12,20.5z"/> - <path android:fillColor="@android:color/white" android:pathData="M12.48,6c-1.71,0-3.53,0.96-3.53,3.17v0.37c0,0.11,0.06,0.17,0.17,0.17l1.29,0.07c0.11,0,0.17-0.06,0.17-0.17V9.17 c0-1.29,1.07-1.7,1.85-1.7c0.74,0,1.81,0.37,1.81,1.66c0,1.48-1.57,1.85-2.54,3.09c-0.48,0.6-0.39,1.28-0.39,2.1 c0,0.09,0.08,0.17,0.17,0.17l1.3,0c0.11,0,0.17-0.06,0.17-0.17c0-0.72-0.07-1.13,0.28-1.54c0.91-1.05,2.65-1.46,2.65-3.7 C15.89,6.99,14.27,6,12.48,6z"/> - <path android:fillColor="@android:color/white" android:pathData="M12.83,16h-1.66C11.08,16,11,16.08,11,16.17v1.66c0,0.09,0.08,0.17,0.17,0.17h1.66c0.09,0,0.17-0.08,0.17-0.17v-1.66 C13,16.08,12.92,16,12.83,16z"/> + <path android:fillColor="?android:attr/colorPrimary" android:pathData="M12,2C6.48,2,2,6.48,2,12s4.48,10,10,10s10-4.48,10-10S17.52,2,12,2z M12,20.5c-4.69,0-8.5-3.81-8.5-8.5S7.31,3.5,12,3.5 s8.5,3.81,8.5,8.5S16.69,20.5,12,20.5z"/> + <path android:fillColor="?android:attr/colorPrimary" android:pathData="M12.48,6c-1.71,0-3.53,0.96-3.53,3.17v0.37c0,0.11,0.06,0.17,0.17,0.17l1.29,0.07c0.11,0,0.17-0.06,0.17-0.17V9.17 c0-1.29,1.07-1.7,1.85-1.7c0.74,0,1.81,0.37,1.81,1.66c0,1.48-1.57,1.85-2.54,3.09c-0.48,0.6-0.39,1.28-0.39,2.1 c0,0.09,0.08,0.17,0.17,0.17l1.3,0c0.11,0,0.17-0.06,0.17-0.17c0-0.72-0.07-1.13,0.28-1.54c0.91-1.05,2.65-1.46,2.65-3.7 C15.89,6.99,14.27,6,12.48,6z"/> + <path android:fillColor="?android:attr/colorPrimary" android:pathData="M12.83,16h-1.66C11.08,16,11,16.08,11,16.17v1.66c0,0.09,0.08,0.17,0.17,0.17h1.66c0.09,0,0.17-0.08,0.17-0.17v-1.66 C13,16.08,12.92,16,12.83,16z"/> </vector>
\ No newline at end of file diff --git a/packages/overlays/IconPackVictorSettingsOverlay/res/drawable/ic_phone_info.xml b/packages/overlays/IconPackVictorSettingsOverlay/res/drawable/ic_phone_info.xml index 0484309adedb..1307f38494f3 100644 --- a/packages/overlays/IconPackVictorSettingsOverlay/res/drawable/ic_phone_info.xml +++ b/packages/overlays/IconPackVictorSettingsOverlay/res/drawable/ic_phone_info.xml @@ -14,7 +14,7 @@ limitations under the License. --> <vector android:height="24dp" android:viewportHeight="24" android:viewportWidth="24" android:width="24dp" xmlns:android="http://schemas.android.com/apk/res/android"> - <path android:fillColor="@android:color/white" android:pathData="M 11.25 11 H 12.75 V 16 H 11.25 V 11 Z"/> - <path android:fillColor="@android:color/white" android:pathData="M 11.25 8 H 12.75 V 9.5 H 11.25 V 8 Z"/> - <path android:fillColor="@android:color/white" android:pathData="M17.59,1H6.41L5,2.41v19.17L6.41,23h11.17L19,21.59V2.41L17.59,1z M17.5,2.5v1.75h-11V2.5H17.5z M17.5,5.75v12.5h-11V5.75 H17.5z M6.5,21.5v-1.75h11v1.75H6.5z"/> + <path android:fillColor="?android:attr/colorPrimary" android:pathData="M 11.25 11 H 12.75 V 16 H 11.25 V 11 Z"/> + <path android:fillColor="?android:attr/colorPrimary" android:pathData="M 11.25 8 H 12.75 V 9.5 H 11.25 V 8 Z"/> + <path android:fillColor="?android:attr/colorPrimary" android:pathData="M17.59,1H6.41L5,2.41v19.17L6.41,23h11.17L19,21.59V2.41L17.59,1z M17.5,2.5v1.75h-11V2.5H17.5z M17.5,5.75v12.5h-11V5.75 H17.5z M6.5,21.5v-1.75h11v1.75H6.5z"/> </vector>
\ No newline at end of file diff --git a/packages/overlays/IconPackVictorSettingsOverlay/res/drawable/ic_settings_accessibility.xml b/packages/overlays/IconPackVictorSettingsOverlay/res/drawable/ic_settings_accessibility.xml index 7f80c7d18df8..5983b89e4dc8 100644 --- a/packages/overlays/IconPackVictorSettingsOverlay/res/drawable/ic_settings_accessibility.xml +++ b/packages/overlays/IconPackVictorSettingsOverlay/res/drawable/ic_settings_accessibility.xml @@ -14,6 +14,6 @@ limitations under the License. --> <vector android:height="24dp" android:viewportHeight="24" android:viewportWidth="24" android:width="24dp" xmlns:android="http://schemas.android.com/apk/res/android"> - <path android:fillColor="@android:color/white" android:pathData="M20.5,4c-2.61,0.7-5.67,1-8.5,1S6.11,4.7,3.5,4L3,6c1.86,0.5,4,0.83,6,1v13h2v-6h2v6h2V7c2-0.17,4.14-0.5,6-1L20.5,4z M12,4c1.1,0,2-0.9,2-2c0-1.1-0.9-2-2-2s-2,0.9-2,2C10,3.1,10.9,4,12,4"/> - <path android:fillColor="@android:color/white" android:pathData="M7,24h2v-2H7V24z M11,24h2v-2h-2V24z M15,24h2v-2h-2V24z"/> + <path android:fillColor="?android:attr/colorPrimary" android:pathData="M20.5,4c-2.61,0.7-5.67,1-8.5,1S6.11,4.7,3.5,4L3,6c1.86,0.5,4,0.83,6,1v13h2v-6h2v6h2V7c2-0.17,4.14-0.5,6-1L20.5,4z M12,4c1.1,0,2-0.9,2-2c0-1.1-0.9-2-2-2s-2,0.9-2,2C10,3.1,10.9,4,12,4"/> + <path android:fillColor="?android:attr/colorPrimary" android:pathData="M7,24h2v-2H7V24z M11,24h2v-2h-2V24z M15,24h2v-2h-2V24z"/> </vector>
\ No newline at end of file diff --git a/packages/overlays/IconPackVictorSettingsOverlay/res/drawable/ic_settings_accounts.xml b/packages/overlays/IconPackVictorSettingsOverlay/res/drawable/ic_settings_accounts.xml index 758e63f7087a..3a997b085d40 100644 --- a/packages/overlays/IconPackVictorSettingsOverlay/res/drawable/ic_settings_accounts.xml +++ b/packages/overlays/IconPackVictorSettingsOverlay/res/drawable/ic_settings_accounts.xml @@ -14,6 +14,6 @@ limitations under the License. --> <vector android:height="24dp" android:viewportHeight="24" android:viewportWidth="24" android:width="24dp" xmlns:android="http://schemas.android.com/apk/res/android"> - <path android:fillColor="@android:color/white" android:pathData="M12,13c1.93,0,3.5-1.57,3.5-3.5S13.93,6,12,6S8.5,7.57,8.5,9.5S10.07,13,12,13z M12,7.5c1.1,0,2,0.9,2,2s-0.9,2-2,2 s-2-0.9-2-2S10.9,7.5,12,7.5z"/> - <path android:fillColor="@android:color/white" android:pathData="M19.5,3h-15L3,4.5v15L4.5,21h15l1.5-1.5v-15L19.5,3z M19.5,19.5h-15v-1.65c1.76-1.53,5.37-2.35,7.5-2.35 c1.45,0,3.76,0.38,5.67,1.24c0.62,0.28,1.29,0.65,1.83,1.12V19.5z M19.5,16.02C17.26,14.64,14.04,14,12,14s-5.26,0.64-7.5,2.02 V4.5h15V16.02z"/> + <path android:fillColor="?android:attr/colorPrimary" android:pathData="M12,13c1.93,0,3.5-1.57,3.5-3.5S13.93,6,12,6S8.5,7.57,8.5,9.5S10.07,13,12,13z M12,7.5c1.1,0,2,0.9,2,2s-0.9,2-2,2 s-2-0.9-2-2S10.9,7.5,12,7.5z"/> + <path android:fillColor="?android:attr/colorPrimary" android:pathData="M19.5,3h-15L3,4.5v15L4.5,21h15l1.5-1.5v-15L19.5,3z M19.5,19.5h-15v-1.65c1.76-1.53,5.37-2.35,7.5-2.35 c1.45,0,3.76,0.38,5.67,1.24c0.62,0.28,1.29,0.65,1.83,1.12V19.5z M19.5,16.02C17.26,14.64,14.04,14,12,14s-5.26,0.64-7.5,2.02 V4.5h15V16.02z"/> </vector>
\ No newline at end of file diff --git a/packages/overlays/IconPackVictorSettingsOverlay/res/drawable/ic_settings_battery_white.xml b/packages/overlays/IconPackVictorSettingsOverlay/res/drawable/ic_settings_battery_white.xml index e1b7945aec2f..ca9bfc9ad8da 100644 --- a/packages/overlays/IconPackVictorSettingsOverlay/res/drawable/ic_settings_battery_white.xml +++ b/packages/overlays/IconPackVictorSettingsOverlay/res/drawable/ic_settings_battery_white.xml @@ -14,5 +14,5 @@ limitations under the License. --> <vector android:height="24dp" android:viewportHeight="24" android:viewportWidth="24" android:width="24dp" xmlns:android="http://schemas.android.com/apk/res/android"> - <path android:fillColor="@android:color/white" android:pathData="M 16.59 4 L 15 4 L 14 2 L 10 2 L 9 4 L 7.41 4 L 6 5.41 L 6 20.59 L 7.41 22 L 16.59 22 L 18 20.59 L 18 5.41 Z"/> + <path android:fillColor="?android:attr/colorPrimary" android:pathData="M 16.59 4 L 15 4 L 14 2 L 10 2 L 9 4 L 7.41 4 L 6 5.41 L 6 20.59 L 7.41 22 L 16.59 22 L 18 20.59 L 18 5.41 Z"/> </vector>
\ No newline at end of file diff --git a/packages/overlays/IconPackVictorSettingsOverlay/res/drawable/ic_settings_display_white.xml b/packages/overlays/IconPackVictorSettingsOverlay/res/drawable/ic_settings_display_white.xml index c7d8a617bb70..adf1c82b729d 100644 --- a/packages/overlays/IconPackVictorSettingsOverlay/res/drawable/ic_settings_display_white.xml +++ b/packages/overlays/IconPackVictorSettingsOverlay/res/drawable/ic_settings_display_white.xml @@ -14,6 +14,6 @@ limitations under the License. --> <vector android:height="24dp" android:viewportHeight="24" android:viewportWidth="24" android:width="24dp" xmlns:android="http://schemas.android.com/apk/res/android"> - <path android:fillColor="@android:color/white" android:pathData="M12,7v10c2.76,0,5-2.24,5-5S14.76,7,12,7z"/> - <path android:fillColor="@android:color/white" android:pathData="M22.81,12.5v-1L20,8.69V4.71l0,0L19.29,4l0,0h-3.98L12.5,1.19h-1v0L8.69,4H4.71l0,0L4,4.71l0,0v3.98L1.19,11.5v1h0 L4,15.31v3.98l0,0L4.71,20l0,0h3.98l2.81,2.81v0h1L15.31,20h3.98l0,0L20,19.29l0,0v-3.98L22.81,12.5L22.81,12.5z M18.5,14.69v3.81 h-3.81L12,21.19L9.31,18.5H5.5v-3.81L2.81,12L5.5,9.31V5.5h3.81L12,2.81l2.69,2.69h3.81v3.81L21.19,12L18.5,14.69z"/> + <path android:fillColor="?android:attr/colorPrimary" android:pathData="M12,7v10c2.76,0,5-2.24,5-5S14.76,7,12,7z"/> + <path android:fillColor="?android:attr/colorPrimary" android:pathData="M22.81,12.5v-1L20,8.69V4.71l0,0L19.29,4l0,0h-3.98L12.5,1.19h-1v0L8.69,4H4.71l0,0L4,4.71l0,0v3.98L1.19,11.5v1h0 L4,15.31v3.98l0,0L4.71,20l0,0h3.98l2.81,2.81v0h1L15.31,20h3.98l0,0L20,19.29l0,0v-3.98L22.81,12.5L22.81,12.5z M18.5,14.69v3.81 h-3.81L12,21.19L9.31,18.5H5.5v-3.81L2.81,12L5.5,9.31V5.5h3.81L12,2.81l2.69,2.69h3.81v3.81L21.19,12L18.5,14.69z"/> </vector>
\ No newline at end of file diff --git a/packages/overlays/IconPackVictorSettingsOverlay/res/drawable/ic_settings_location.xml b/packages/overlays/IconPackVictorSettingsOverlay/res/drawable/ic_settings_location.xml index 7975db696d2d..90ad165e8fda 100644 --- a/packages/overlays/IconPackVictorSettingsOverlay/res/drawable/ic_settings_location.xml +++ b/packages/overlays/IconPackVictorSettingsOverlay/res/drawable/ic_settings_location.xml @@ -14,6 +14,6 @@ limitations under the License. --> <vector android:height="24dp" android:viewportHeight="24" android:viewportWidth="24" android:width="24dp" xmlns:android="http://schemas.android.com/apk/res/android"> - <path android:fillColor="@android:color/white" android:pathData="M12,2c-4.2,0-8,3.22-8,8.2c0,3.11,2.33,6.62,7,10.8h2c4.67-4.18,7-7.7,7-10.8C20,5.22,16.2,2,12,2z M12.42,19.5h-0.84 c-4.04-3.7-6.08-6.74-6.08-9.3c0-4.35,3.35-6.7,6.5-6.7s6.5,2.35,6.5,6.7C18.5,12.76,16.46,15.8,12.42,19.5z"/> - <path android:fillColor="@android:color/white" android:pathData="M12,7c-1.66,0-3,1.34-3,3c0,1.66,1.34,3,3,3s3-1.34,3-3C15,8.34,13.66,7,12,7z M12,11.5c-0.83,0-1.5-0.67-1.5-1.5 c0-0.83,0.67-1.5,1.5-1.5s1.5,0.67,1.5,1.5C13.5,10.83,12.83,11.5,12,11.5z"/> + <path android:fillColor="?android:attr/colorPrimary" android:pathData="M12,2c-4.2,0-8,3.22-8,8.2c0,3.11,2.33,6.62,7,10.8h2c4.67-4.18,7-7.7,7-10.8C20,5.22,16.2,2,12,2z M12.42,19.5h-0.84 c-4.04-3.7-6.08-6.74-6.08-9.3c0-4.35,3.35-6.7,6.5-6.7s6.5,2.35,6.5,6.7C18.5,12.76,16.46,15.8,12.42,19.5z"/> + <path android:fillColor="?android:attr/colorPrimary" android:pathData="M12,7c-1.66,0-3,1.34-3,3c0,1.66,1.34,3,3,3s3-1.34,3-3C15,8.34,13.66,7,12,7z M12,11.5c-0.83,0-1.5-0.67-1.5-1.5 c0-0.83,0.67-1.5,1.5-1.5s1.5,0.67,1.5,1.5C13.5,10.83,12.83,11.5,12,11.5z"/> </vector>
\ No newline at end of file diff --git a/packages/overlays/IconPackVictorSettingsOverlay/res/drawable/ic_settings_privacy.xml b/packages/overlays/IconPackVictorSettingsOverlay/res/drawable/ic_settings_privacy.xml index 7da20c14749c..f499227f9d80 100644 --- a/packages/overlays/IconPackVictorSettingsOverlay/res/drawable/ic_settings_privacy.xml +++ b/packages/overlays/IconPackVictorSettingsOverlay/res/drawable/ic_settings_privacy.xml @@ -14,7 +14,7 @@ limitations under the License. --> <vector android:height="24dp" android:viewportHeight="24" android:viewportWidth="24" android:width="24dp" xmlns:android="http://schemas.android.com/apk/res/android"> - <path android:fillColor="@android:color/white" android:pathData="M12,8.5c1.48,0,2.71,1.08,2.95,2.5h1.5C16.2,8.76,14.31,7,12,7c-2.48,0-4.5,2.02-4.5,4.5c0,2.48,2.02,4.5,4.5,4.5 c0.72,0,1.39-0.19,2-0.49v-1.79c-0.53,0.48-1.23,0.78-2,0.78c-1.65,0-3-1.35-3-3S10.35,8.5,12,8.5z"/> - <path android:fillColor="@android:color/white" android:pathData="M12,4C7.38,4,3.4,6.65,1.45,10.51v1.97C3.4,16.35,7.38,19,12,19c0.68,0,1.35-0.06,2-0.18v-1.54 c-0.65,0.13-1.32,0.22-2,0.22c-4.07,0-7.68-2.34-9.37-6c1.69-3.66,5.3-6,9.37-6c3.87,0,7.32,2.13,9.09,5.5h1.46v-0.49 C20.6,6.65,16.62,4,12,4z"/> - <path android:fillColor="@android:color/white" android:pathData="M21,14l-1-1h-2l-1,1v2h-1v5h6v-5h-1V14z M19.5,16h-1v-1.5h1V16z"/> + <path android:fillColor="?android:attr/colorPrimary" android:pathData="M12,8.5c1.48,0,2.71,1.08,2.95,2.5h1.5C16.2,8.76,14.31,7,12,7c-2.48,0-4.5,2.02-4.5,4.5c0,2.48,2.02,4.5,4.5,4.5 c0.72,0,1.39-0.19,2-0.49v-1.79c-0.53,0.48-1.23,0.78-2,0.78c-1.65,0-3-1.35-3-3S10.35,8.5,12,8.5z"/> + <path android:fillColor="?android:attr/colorPrimary" android:pathData="M12,4C7.38,4,3.4,6.65,1.45,10.51v1.97C3.4,16.35,7.38,19,12,19c0.68,0,1.35-0.06,2-0.18v-1.54 c-0.65,0.13-1.32,0.22-2,0.22c-4.07,0-7.68-2.34-9.37-6c1.69-3.66,5.3-6,9.37-6c3.87,0,7.32,2.13,9.09,5.5h1.46v-0.49 C20.6,6.65,16.62,4,12,4z"/> + <path android:fillColor="?android:attr/colorPrimary" android:pathData="M21,14l-1-1h-2l-1,1v2h-1v5h6v-5h-1V14z M19.5,16h-1v-1.5h1V16z"/> </vector>
\ No newline at end of file diff --git a/packages/overlays/IconPackVictorSettingsOverlay/res/drawable/ic_settings_security_white.xml b/packages/overlays/IconPackVictorSettingsOverlay/res/drawable/ic_settings_security_white.xml index fbf6ae13903b..49d31f73317b 100644 --- a/packages/overlays/IconPackVictorSettingsOverlay/res/drawable/ic_settings_security_white.xml +++ b/packages/overlays/IconPackVictorSettingsOverlay/res/drawable/ic_settings_security_white.xml @@ -14,6 +14,6 @@ limitations under the License. --> <vector android:height="24dp" android:viewportHeight="24" android:viewportWidth="24" android:width="24dp" xmlns:android="http://schemas.android.com/apk/res/android"> - <path android:fillColor="@android:color/white" android:pathData="M21,2h-5l-1.5,1.5l0,4.5h-9L4,9.5v11L5.5,22h13l1.5-1.5v-11L18.5,8H16V3.5h5v2.62h1.5V3.5L21,2z M18.5,20.5h-13v-11h13 V20.5z"/> - <path android:fillColor="@android:color/white" android:pathData="M 12 13 C 13.1045694997 13 14 13.8954305003 14 15 C 14 16.1045694997 13.1045694997 17 12 17 C 10.8954305003 17 10 16.1045694997 10 15 C 10 13.8954305003 10.8954305003 13 12 13 Z"/> + <path android:fillColor="?android:attr/colorPrimary" android:pathData="M21,2h-5l-1.5,1.5l0,4.5h-9L4,9.5v11L5.5,22h13l1.5-1.5v-11L18.5,8H16V3.5h5v2.62h1.5V3.5L21,2z M18.5,20.5h-13v-11h13 V20.5z"/> + <path android:fillColor="?android:attr/colorPrimary" android:pathData="M 12 13 C 13.1045694997 13 14 13.8954305003 14 15 C 14 16.1045694997 13.1045694997 17 12 17 C 10.8954305003 17 10 16.1045694997 10 15 C 10 13.8954305003 10.8954305003 13 12 13 Z"/> </vector>
\ No newline at end of file diff --git a/packages/overlays/IconPackVictorSettingsOverlay/res/drawable/ic_settings_system_dashboard_white.xml b/packages/overlays/IconPackVictorSettingsOverlay/res/drawable/ic_settings_system_dashboard_white.xml index 0594b9abd1bf..b668eb20c28a 100644 --- a/packages/overlays/IconPackVictorSettingsOverlay/res/drawable/ic_settings_system_dashboard_white.xml +++ b/packages/overlays/IconPackVictorSettingsOverlay/res/drawable/ic_settings_system_dashboard_white.xml @@ -14,7 +14,7 @@ limitations under the License. --> <vector android:height="24dp" android:viewportHeight="24" android:viewportWidth="24" android:width="24dp" xmlns:android="http://schemas.android.com/apk/res/android"> - <path android:fillColor="@android:color/white" android:pathData="M12,2C6.48,2,2,6.48,2,12s4.48,10,10,10s10-4.48,10-10S17.52,2,12,2z M12,20.5c-4.69,0-8.5-3.81-8.5-8.5S7.31,3.5,12,3.5 s8.5,3.81,8.5,8.5S16.69,20.5,12,20.5z"/> - <path android:fillColor="@android:color/white" android:pathData="M 11.25 10 H 12.75 V 17 H 11.25 V 10 Z"/> - <path android:fillColor="@android:color/white" android:pathData="M 11.25 7 H 12.75 V 8.5 H 11.25 V 7 Z"/> + <path android:fillColor="?android:attr/colorPrimary" android:pathData="M12,2C6.48,2,2,6.48,2,12s4.48,10,10,10s10-4.48,10-10S17.52,2,12,2z M12,20.5c-4.69,0-8.5-3.81-8.5-8.5S7.31,3.5,12,3.5 s8.5,3.81,8.5,8.5S16.69,20.5,12,20.5z"/> + <path android:fillColor="?android:attr/colorPrimary" android:pathData="M 11.25 10 H 12.75 V 17 H 11.25 V 10 Z"/> + <path android:fillColor="?android:attr/colorPrimary" android:pathData="M 11.25 7 H 12.75 V 8.5 H 11.25 V 7 Z"/> </vector>
\ No newline at end of file diff --git a/packages/overlays/IconPackVictorSettingsOverlay/res/drawable/ic_settings_wireless.xml b/packages/overlays/IconPackVictorSettingsOverlay/res/drawable/ic_settings_wireless.xml index 802a041edfb3..9223902d5a23 100644 --- a/packages/overlays/IconPackVictorSettingsOverlay/res/drawable/ic_settings_wireless.xml +++ b/packages/overlays/IconPackVictorSettingsOverlay/res/drawable/ic_settings_wireless.xml @@ -14,7 +14,7 @@ limitations under the License. --> <vector android:height="24dp" android:tint="?android:attr/colorControlNormal" android:viewportHeight="24" android:viewportWidth="24" android:width="24dp" xmlns:android="http://schemas.android.com/apk/res/android"> - <path android:fillColor="@android:color/white" android:pathData="M12,4.5c-4.29,0-8.17,1.72-11.01,4.49l1.42,1.42C4.89,8,8.27,6.5,12,6.5c3.73,0,7.11,1.5,9.59,3.91l1.42-1.42 C20.17,6.22,16.29,4.5,12,4.5z"/> - <path android:fillColor="@android:color/white" android:pathData="M4.93,12.93l1.42,1.42C7.79,12.9,9.79,12,12,12s4.21,0.9,5.65,2.35l1.42-1.42C17.26,11.12,14.76,10,12,10 S6.74,11.12,4.93,12.93z"/> - <path android:fillColor="@android:color/white" android:pathData="M9.06,17.06L12,20l2.94-2.94c-0.73-0.8-1.77-1.31-2.94-1.31S9.79,16.26,9.06,17.06z"/> + <path android:fillColor="?android:attr/colorPrimary" android:pathData="M12,4.5c-4.29,0-8.17,1.72-11.01,4.49l1.42,1.42C4.89,8,8.27,6.5,12,6.5c3.73,0,7.11,1.5,9.59,3.91l1.42-1.42 C20.17,6.22,16.29,4.5,12,4.5z"/> + <path android:fillColor="?android:attr/colorPrimary" android:pathData="M4.93,12.93l1.42,1.42C7.79,12.9,9.79,12,12,12s4.21,0.9,5.65,2.35l1.42-1.42C17.26,11.12,14.76,10,12,10 S6.74,11.12,4.93,12.93z"/> + <path android:fillColor="?android:attr/colorPrimary" android:pathData="M9.06,17.06L12,20l2.94-2.94c-0.73-0.8-1.77-1.31-2.94-1.31S9.79,16.26,9.06,17.06z"/> </vector>
\ No newline at end of file diff --git a/packages/overlays/IconPackVictorSettingsOverlay/res/drawable/ic_storage_white.xml b/packages/overlays/IconPackVictorSettingsOverlay/res/drawable/ic_storage_white.xml index 45288f9df04f..22f6092a35ed 100644 --- a/packages/overlays/IconPackVictorSettingsOverlay/res/drawable/ic_storage_white.xml +++ b/packages/overlays/IconPackVictorSettingsOverlay/res/drawable/ic_storage_white.xml @@ -14,7 +14,7 @@ limitations under the License. --> <vector android:height="24dp" android:viewportHeight="24" android:viewportWidth="24" android:width="24dp" xmlns:android="http://schemas.android.com/apk/res/android"> - <path android:fillColor="@android:color/white" android:pathData="M19.5,4h-15L3,5.5v1L4.5,8h15L21,6.5v-1L19.5,4z M6.5,6.75H5v-1.5h1.5V6.75z"/> - <path android:fillColor="@android:color/white" android:pathData="M4.5,10L3,11.5v1L4.5,14h15l1.5-1.5v-1L19.5,10H4.5z M6.5,12.75H5v-1.5h1.5V12.75z"/> - <path android:fillColor="@android:color/white" android:pathData="M4.5,16L3,17.5v1L4.5,20h15l1.5-1.5v-1L19.5,16H4.5z M6.5,18.75H5v-1.5h1.5V18.75z"/> + <path android:fillColor="?android:attr/colorPrimary" android:pathData="M19.5,4h-15L3,5.5v1L4.5,8h15L21,6.5v-1L19.5,4z M6.5,6.75H5v-1.5h1.5V6.75z"/> + <path android:fillColor="?android:attr/colorPrimary" android:pathData="M4.5,10L3,11.5v1L4.5,14h15l1.5-1.5v-1L19.5,10H4.5z M6.5,12.75H5v-1.5h1.5V12.75z"/> + <path android:fillColor="?android:attr/colorPrimary" android:pathData="M4.5,16L3,17.5v1L4.5,20h15l1.5-1.5v-1L19.5,16H4.5z M6.5,18.75H5v-1.5h1.5V18.75z"/> </vector>
\ No newline at end of file diff --git a/packages/overlays/IconPackVictorSettingsOverlay/res/drawable/ic_volume_up_24dp.xml b/packages/overlays/IconPackVictorSettingsOverlay/res/drawable/ic_volume_up_24dp.xml index 781ed94cfb58..964f66897822 100644 --- a/packages/overlays/IconPackVictorSettingsOverlay/res/drawable/ic_volume_up_24dp.xml +++ b/packages/overlays/IconPackVictorSettingsOverlay/res/drawable/ic_volume_up_24dp.xml @@ -14,7 +14,7 @@ limitations under the License. --> <vector android:height="24dp" android:viewportHeight="24" android:viewportWidth="24" android:width="24dp" xmlns:android="http://schemas.android.com/apk/res/android"> - <path android:fillColor="@android:color/white" android:pathData="M7,9H4.5L3,10.5v3L4.5,15H7l4,4h1V5h-1L7,9z M10.5,16.38L7.62,13.5H4.5v-3h3.12l2.88-2.88V16.38z"/> - <path android:fillColor="@android:color/white" android:pathData="M14,3.23v1.55c3.17,0.88,5.5,3.78,5.5,7.22s-2.33,6.34-5.5,7.22v1.55c4.01-0.91,7-4.49,7-8.77S18.01,4.14,14,3.23z"/> - <path android:fillColor="@android:color/white" android:pathData="M16.5,12c0-1.76-1.02-3.27-2.5-4.01v8.02C15.48,15.27,16.5,13.76,16.5,12z"/> + <path android:fillColor="?android:attr/colorPrimary" android:pathData="M7,9H4.5L3,10.5v3L4.5,15H7l4,4h1V5h-1L7,9z M10.5,16.38L7.62,13.5H4.5v-3h3.12l2.88-2.88V16.38z"/> + <path android:fillColor="?android:attr/colorPrimary" android:pathData="M14,3.23v1.55c3.17,0.88,5.5,3.78,5.5,7.22s-2.33,6.34-5.5,7.22v1.55c4.01-0.91,7-4.49,7-8.77S18.01,4.14,14,3.23z"/> + <path android:fillColor="?android:attr/colorPrimary" android:pathData="M16.5,12c0-1.76-1.02-3.27-2.5-4.01v8.02C15.48,15.27,16.5,13.76,16.5,12z"/> </vector>
\ No newline at end of file diff --git a/services/Android.bp b/services/Android.bp index b51e4b014b27..da24719c0f68 100644 --- a/services/Android.bp +++ b/services/Android.bp @@ -30,6 +30,7 @@ filegroup { ":services.searchui-sources", ":services.startop.iorap-sources", ":services.systemcaptions-sources", + ":services.translation-sources", ":services.usage-sources", ":services.usb-sources", ":services.voiceinteraction-sources", @@ -76,6 +77,7 @@ java_library { "services.searchui", "services.startop", "services.systemcaptions", + "services.translation", "services.usage", "services.usb", "services.voiceinteraction", diff --git a/services/companion/java/com/android/server/companion/CompanionDeviceManagerService.java b/services/companion/java/com/android/server/companion/CompanionDeviceManagerService.java index 9d028358561f..04e08aeaf78e 100644 --- a/services/companion/java/com/android/server/companion/CompanionDeviceManagerService.java +++ b/services/companion/java/com/android/server/companion/CompanionDeviceManagerService.java @@ -27,12 +27,14 @@ 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 java.util.Objects.requireNonNull; import static java.util.concurrent.TimeUnit.MINUTES; import android.annotation.CheckResult; import android.annotation.NonNull; import android.annotation.Nullable; import android.annotation.SuppressLint; +import android.app.ActivityManagerInternal; import android.app.AppOpsManager; import android.app.PendingIntent; import android.app.role.RoleManager; @@ -53,6 +55,7 @@ import android.content.pm.FeatureInfo; import android.content.pm.PackageInfo; import android.content.pm.PackageItemInfo; import android.content.pm.PackageManager; +import android.content.pm.PackageManagerInternal; import android.content.pm.UserInfo; import android.net.NetworkPolicyManager; import android.os.Binder; @@ -70,6 +73,7 @@ import android.os.ShellCallback; import android.os.ShellCommand; import android.os.UserHandle; import android.os.UserManager; +import android.permission.PermissionControllerManager; import android.provider.Settings; import android.provider.SettingsStringUtil.ComponentNameSet; import android.text.BidiFormatter; @@ -111,7 +115,6 @@ import java.io.PrintWriter; import java.nio.charset.StandardCharsets; import java.util.ArrayList; import java.util.Collections; -import java.util.HashSet; import java.util.List; import java.util.Objects; import java.util.Set; @@ -158,6 +161,7 @@ public class CompanionDeviceManagerService extends SystemService implements Bind private AssociationRequest mRequest; private String mCallingPackage; private AndroidFuture<Association> mOngoingDeviceDiscovery; + private PermissionControllerManager mPermissionControllerManager; private BluetoothDeviceConnectedListener mBluetoothDeviceConnectedListener = new BluetoothDeviceConnectedListener(); @@ -169,6 +173,10 @@ public class CompanionDeviceManagerService extends SystemService implements Bind @GuardedBy("mLock") private @Nullable SparseArray<Set<Association>> mCachedAssociations = new SparseArray<>(); + ActivityTaskManagerInternal mAtmInternal; + ActivityManagerInternal mAmInternal; + PackageManagerInternal mPackageManagerInternal; + public CompanionDeviceManagerService(Context context) { super(context); mImpl = new CompanionDeviceManagerImpl(); @@ -176,6 +184,11 @@ public class CompanionDeviceManagerService extends SystemService implements Bind mRoleManager = context.getSystemService(RoleManager.class); mAppOpsManager = IAppOpsService.Stub.asInterface( ServiceManager.getService(Context.APP_OPS_SERVICE)); + mAtmInternal = LocalServices.getService(ActivityTaskManagerInternal.class); + mAmInternal = LocalServices.getService(ActivityManagerInternal.class); + mPackageManagerInternal = LocalServices.getService(PackageManagerInternal.class); + mPermissionControllerManager = requireNonNull( + context.getSystemService(PermissionControllerManager.class)); Intent serviceIntent = new Intent().setComponent(SERVICE_TO_BIND_TO); mServiceConnectors = new PerUser<ServiceConnector<ICompanionDeviceDiscoveryService>>() { @@ -236,15 +249,7 @@ public class CompanionDeviceManagerService extends SystemService implements Bind if (associations == null || associations.isEmpty()) { return; } - Set<String> companionAppPackages = new HashSet<>(); - for (Association association : associations) { - companionAppPackages.add(association.getPackageName()); - } - ActivityTaskManagerInternal atmInternal = LocalServices.getService( - ActivityTaskManagerInternal.class); - if (atmInternal != null) { - atmInternal.setCompanionAppPackages(userHandle, companionAppPackages); - } + updateAtm(userHandle, associations); BackgroundThread.getHandler().sendMessageDelayed( obtainMessage(CompanionDeviceManagerService::maybeGrantAutoRevokeExemptions, this), @@ -344,13 +349,25 @@ public class CompanionDeviceManagerService extends SystemService implements Bind request.setCallingPackage(callingPackage); callback.asBinder().linkToDeath(CompanionDeviceManagerService.this /* recipient */, 0); - final long callingIdentity = Binder.clearCallingIdentity(); - try { - mOngoingDeviceDiscovery = mServiceConnectors.forUser(userId).postAsync(service -> { + AndroidFuture<String> fetchProfileDescription = + request.getDeviceProfile() == null + ? AndroidFuture.completedFuture(null) + : getDeviceProfilePermissionDescription( + request.getDeviceProfile()); + + mOngoingDeviceDiscovery = fetchProfileDescription.thenComposeAsync(description -> { + request.setDeviceProfilePrivilegesDescription(description); + + return mServiceConnectors.forUser(userId).postAsync(service -> { AndroidFuture<Association> future = new AndroidFuture<>(); service.startDiscovery(request, callingPackage, callback, future); return future; - }).cancelTimeout().whenComplete(uncheckExceptions((association, err) -> { + }).cancelTimeout(); + + }, FgThread.getExecutor()).whenComplete(uncheckExceptions((association, err) -> { + + final long callingIdentity = Binder.clearCallingIdentity(); + try { if (err == null) { addAssociation(association); } else { @@ -358,10 +375,10 @@ public class CompanionDeviceManagerService extends SystemService implements Bind callback.onFailure("No devices found: " + err.getMessage()); } cleanup(); - })); - } finally { - Binder.restoreCallingIdentity(callingIdentity); - } + } finally { + Binder.restoreCallingIdentity(callingIdentity); + } + })); } @Override @@ -727,12 +744,6 @@ public class CompanionDeviceManagerService extends SystemService implements Bind final Set<Association> old = getAllAssociations(userId); Set<Association> associations = new ArraySet<>(old); associations = update.apply(associations); - - Set<String> companionAppPackages = new HashSet<>(); - for (Association association : associations) { - companionAppPackages.add(association.getPackageName()); - } - if (DEBUG) { Slog.i(LOG_TAG, "Updating associations: " + old + " --> " + associations); } @@ -741,9 +752,25 @@ public class CompanionDeviceManagerService extends SystemService implements Bind CompanionDeviceManagerService::persistAssociations, this, associations, userId)); - ActivityTaskManagerInternal atmInternal = LocalServices.getService( - ActivityTaskManagerInternal.class); - atmInternal.setCompanionAppPackages(userId, companionAppPackages); + updateAtm(userId, associations); + } + } + + private void updateAtm(int userId, Set<Association> associations) { + final Set<Integer> companionAppUids = new ArraySet<>(); + for (Association association : associations) { + final int uid = mPackageManagerInternal.getPackageUid(association.getPackageName(), + 0, userId); + if (uid >= 0) { + companionAppUids.add(uid); + } + } + if (mAtmInternal != null) { + mAtmInternal.setCompanionAppUids(userId, companionAppUids); + } + if (mAmInternal != null) { + // Make a copy of companionAppUids and send it to ActivityManager. + mAmInternal.setCompanionAppUids(userId, new ArraySet<>(companionAppUids)); } } @@ -894,6 +921,19 @@ public class CompanionDeviceManagerService extends SystemService implements Bind mCurrentlyConnectedDevices.remove(address); } + private AndroidFuture<String> getDeviceProfilePermissionDescription(String deviceProfile) { + AndroidFuture<String> result = new AndroidFuture<>(); + mPermissionControllerManager.getPrivilegesDescriptionStringForProfile( + deviceProfile, FgThread.getExecutor(), desc -> { + try { + result.complete(requireNonNull(desc)); + } catch (Exception e) { + result.completeExceptionally(e); + } + }); + return result; + } + private class ShellCmd extends ShellCommand { public static final String USAGE = "help\n" + "list USER_ID\n" diff --git a/services/core/Android.bp b/services/core/Android.bp index 019d8c558aef..3750f148f45c 100644 --- a/services/core/Android.bp +++ b/services/core/Android.bp @@ -212,15 +212,11 @@ filegroup { "java/com/android/server/connectivity/AutodestructReference.java", "java/com/android/server/connectivity/ConnectivityConstants.java", "java/com/android/server/connectivity/DataConnectionStats.java", - "java/com/android/server/connectivity/DefaultNetworkMetrics.java", "java/com/android/server/connectivity/DnsManager.java", - "java/com/android/server/connectivity/IpConnectivityEventBuilder.java", - "java/com/android/server/connectivity/IpConnectivityMetrics.java", "java/com/android/server/connectivity/KeepaliveTracker.java", "java/com/android/server/connectivity/LingerMonitor.java", "java/com/android/server/connectivity/MockableSystemProperties.java", "java/com/android/server/connectivity/Nat464Xlat.java", - "java/com/android/server/connectivity/NetdEventListenerService.java", "java/com/android/server/connectivity/NetworkAgentInfo.java", "java/com/android/server/connectivity/NetworkDiagnostics.java", "java/com/android/server/connectivity/NetworkNotificationManager.java", diff --git a/services/core/java/com/android/server/ConnectivityService.java b/services/core/java/com/android/server/ConnectivityService.java index 020c17a77084..71821b33f47d 100644 --- a/services/core/java/com/android/server/ConnectivityService.java +++ b/services/core/java/com/android/server/ConnectivityService.java @@ -56,12 +56,14 @@ import static android.net.NetworkPolicyManager.RULE_NONE; import static android.net.NetworkPolicyManager.uidRulesToString; import static android.net.shared.NetworkMonitorUtils.isPrivateDnsValidationRequired; import static android.os.Process.INVALID_UID; +import static android.os.Process.VPN_UID; import static android.system.OsConstants.IPPROTO_TCP; import static android.system.OsConstants.IPPROTO_UDP; import static java.util.Map.Entry; import android.Manifest; +import android.annotation.BoolRes; import android.annotation.NonNull; import android.annotation.Nullable; import android.app.AppOpsManager; @@ -87,7 +89,6 @@ import android.net.ICaptivePortal; import android.net.IConnectivityDiagnosticsCallback; import android.net.IConnectivityManager; import android.net.IDnsResolver; -import android.net.IIpConnectivityMetrics; import android.net.INetd; import android.net.INetworkManagementEventObserver; import android.net.INetworkMonitor; @@ -154,7 +155,6 @@ import android.os.PersistableBundle; import android.os.PowerManager; import android.os.Process; import android.os.RemoteException; -import android.os.ServiceManager; import android.os.ServiceSpecificException; import android.os.SystemClock; import android.os.SystemProperties; @@ -927,14 +927,6 @@ public class ConnectivityService extends IConnectivityManager.Stub "no IpConnectivityMetrics service"); } - /** - * @see IpConnectivityMetrics - */ - public IIpConnectivityMetrics getIpConnectivityMetrics() { - return IIpConnectivityMetrics.Stub.asInterface( - ServiceManager.getService(IpConnectivityLog.SERVICE_NAME)); - } - public IBatteryStats getBatteryStatsService() { return BatteryStatsService.getService(); } @@ -973,6 +965,10 @@ public class ConnectivityService extends IConnectivityManager.Stub mDefaultWifiRequest = createDefaultInternetRequestForTransport( NetworkCapabilities.TRANSPORT_WIFI, NetworkRequest.Type.BACKGROUND_REQUEST); + mDefaultVehicleRequest = createAlwaysOnRequestForCapability( + NetworkCapabilities.NET_CAPABILITY_VEHICLE_INTERNAL, + NetworkRequest.Type.BACKGROUND_REQUEST); + mHandlerThread = mDeps.makeHandlerThread(); mHandlerThread.start(); mHandler = new InternalHandler(mHandlerThread.getLooper()); @@ -1172,6 +1168,15 @@ public class ConnectivityService extends IConnectivityManager.Stub return new NetworkRequest(netCap, TYPE_NONE, nextNetworkRequestId(), type); } + private NetworkRequest createAlwaysOnRequestForCapability(int capability, + NetworkRequest.Type type) { + final NetworkCapabilities netCap = new NetworkCapabilities(); + netCap.clearAll(); + netCap.addCapability(capability); + netCap.setRequestorUidAndPackageName(Process.myUid(), mContext.getPackageName()); + return new NetworkRequest(netCap, TYPE_NONE, nextNetworkRequestId(), type); + } + // Used only for testing. // TODO: Delete this and either: // 1. Give FakeSettingsProvider the ability to send settings change notifications (requires @@ -1189,10 +1194,19 @@ public class ConnectivityService extends IConnectivityManager.Stub mHandler.sendEmptyMessage(EVENT_PRIVATE_DNS_SETTINGS_CHANGED); } + private void handleAlwaysOnNetworkRequest(NetworkRequest networkRequest, @BoolRes int id) { + final boolean enable = mContext.getResources().getBoolean(id); + handleAlwaysOnNetworkRequest(networkRequest, enable); + } + private void handleAlwaysOnNetworkRequest( NetworkRequest networkRequest, String settingName, boolean defaultValue) { final boolean enable = toBool(Settings.Global.getInt( mContext.getContentResolver(), settingName, encodeBool(defaultValue))); + handleAlwaysOnNetworkRequest(networkRequest, enable); + } + + private void handleAlwaysOnNetworkRequest(NetworkRequest networkRequest, boolean enable) { final boolean isEnabled = (mNetworkRequests.get(networkRequest) != null); if (enable == isEnabled) { return; // Nothing to do. @@ -1209,9 +1223,11 @@ public class ConnectivityService extends IConnectivityManager.Stub private void handleConfigureAlwaysOnNetworks() { handleAlwaysOnNetworkRequest( - mDefaultMobileDataRequest,Settings.Global.MOBILE_DATA_ALWAYS_ON, true); + mDefaultMobileDataRequest, Settings.Global.MOBILE_DATA_ALWAYS_ON, true); handleAlwaysOnNetworkRequest(mDefaultWifiRequest, Settings.Global.WIFI_ALWAYS_REQUESTED, false); + handleAlwaysOnNetworkRequest(mDefaultVehicleRequest, + com.android.internal.R.bool.config_vehicleInternalNetworkAlwaysRequested); } private void registerSettingsCallbacks() { @@ -1565,7 +1581,7 @@ public class ConnectivityService extends IConnectivityManager.Stub if (nc != null) { result.put( nai.network, - maybeSanitizeLocationInfoForCaller( + createWithLocationInfoSanitizedIfNecessaryWhenParceled( nc, mDeps.getCallingUid(), callingPackageName)); } @@ -1575,7 +1591,9 @@ public class ConnectivityService extends IConnectivityManager.Stub for (Network network : networks) { nc = getNetworkCapabilitiesInternal(network); if (nc != null) { - result.put(network, maybeSanitizeLocationInfoForCaller( + result.put( + network, + createWithLocationInfoSanitizedIfNecessaryWhenParceled( nc, mDeps.getCallingUid(), callingPackageName)); } } @@ -1657,7 +1675,7 @@ public class ConnectivityService extends IConnectivityManager.Stub public NetworkCapabilities getNetworkCapabilities(Network network, String callingPackageName) { mAppOpsManager.checkPackage(mDeps.getCallingUid(), callingPackageName); enforceAccessPermission(); - return maybeSanitizeLocationInfoForCaller( + return createWithLocationInfoSanitizedIfNecessaryWhenParceled( getNetworkCapabilitiesInternal(network), mDeps.getCallingUid(), callingPackageName); } @@ -1678,37 +1696,51 @@ public class ConnectivityService extends IConnectivityManager.Stub return newNc; } + private boolean hasLocationPermission(int callerUid, @NonNull String callerPkgName) { + final long token = Binder.clearCallingIdentity(); + try { + return mLocationPermissionChecker.checkLocationPermission( + callerPkgName, null /* featureId */, callerUid, null /* message */); + } finally { + Binder.restoreCallingIdentity(token); + } + } + @VisibleForTesting @Nullable - NetworkCapabilities maybeSanitizeLocationInfoForCaller( + NetworkCapabilities createWithLocationInfoSanitizedIfNecessaryWhenParceled( @Nullable NetworkCapabilities nc, int callerUid, @NonNull String callerPkgName) { if (nc == null) { return null; } - final NetworkCapabilities newNc = new NetworkCapabilities(nc); - if (callerUid != newNc.getOwnerUid()) { + Boolean hasLocationPermission = null; + final NetworkCapabilities newNc; + // Avoid doing location permission check if the transport info has no location sensitive + // data. + if (nc.getTransportInfo() != null && nc.getTransportInfo().hasLocationSensitiveFields()) { + hasLocationPermission = hasLocationPermission(callerUid, callerPkgName); + newNc = new NetworkCapabilities(nc, hasLocationPermission); + } else { + newNc = new NetworkCapabilities(nc, false /* parcelLocationSensitiveFields */); + } + // Reset owner uid if not destined for the owner app. + if (callerUid != nc.getOwnerUid()) { newNc.setOwnerUid(INVALID_UID); return newNc; } - // Allow VPNs to see ownership of their own VPN networks - not location sensitive. if (nc.hasTransport(TRANSPORT_VPN)) { // Owner UIDs already checked above. No need to re-check. return newNc; } - - final long token = Binder.clearCallingIdentity(); - try { - if (!mLocationPermissionChecker.checkLocationPermission( - callerPkgName, null /* featureId */, callerUid, null /* message */)) { - // Caller does not have the requisite location permissions. Reset the - // owner's UID in the NetworkCapabilities. - newNc.setOwnerUid(INVALID_UID); - } - } finally { - Binder.restoreCallingIdentity(token); + if (hasLocationPermission == null) { + // Location permission not checked yet, check now for masking owner UID. + hasLocationPermission = hasLocationPermission(callerUid, callerPkgName); + } + // Reset owner uid if the app has no location permission. + if (!hasLocationPermission) { + newNc.setOwnerUid(INVALID_UID); } - return newNc; } @@ -1787,12 +1819,28 @@ public class ConnectivityService extends IConnectivityManager.Stub private INetworkManagementEventObserver mDataActivityObserver = new BaseNetworkObserver() { @Override - public void interfaceClassDataActivityChanged(int networkType, boolean active, long tsNanos, - int uid) { - sendDataActivityBroadcast(networkType, active, tsNanos); + public void interfaceClassDataActivityChanged(int transportType, boolean active, + long tsNanos, int uid) { + sendDataActivityBroadcast(transportTypeToLegacyType(transportType), active, tsNanos); } }; + // This is deprecated and only to support legacy use cases. + private int transportTypeToLegacyType(int type) { + switch (type) { + case NetworkCapabilities.TRANSPORT_CELLULAR: + return ConnectivityManager.TYPE_MOBILE; + case NetworkCapabilities.TRANSPORT_WIFI: + return ConnectivityManager.TYPE_WIFI; + case NetworkCapabilities.TRANSPORT_BLUETOOTH: + return ConnectivityManager.TYPE_BLUETOOTH; + case NetworkCapabilities.TRANSPORT_ETHERNET: + return ConnectivityManager.TYPE_ETHERNET; + default: + loge("Unexpected transport in transportTypeToLegacyType: " + type); + } + return ConnectivityManager.TYPE_NONE; + } /** * Ensures that the system cannot call a particular method. */ @@ -2376,13 +2424,13 @@ public class ConnectivityService extends IConnectivityManager.Stub timeout = Settings.Global.getInt(mContext.getContentResolver(), Settings.Global.DATA_ACTIVITY_TIMEOUT_MOBILE, 10); - type = ConnectivityManager.TYPE_MOBILE; + type = NetworkCapabilities.TRANSPORT_CELLULAR; } else if (networkAgent.networkCapabilities.hasTransport( NetworkCapabilities.TRANSPORT_WIFI)) { timeout = Settings.Global.getInt(mContext.getContentResolver(), Settings.Global.DATA_ACTIVITY_TIMEOUT_WIFI, 15); - type = ConnectivityManager.TYPE_WIFI; + type = NetworkCapabilities.TRANSPORT_WIFI; } else { return; // do not track any other networks } @@ -2957,7 +3005,7 @@ public class ConnectivityService extends IConnectivityManager.Stub case EVENT_CAPPORT_DATA_CHANGED: { final NetworkAgentInfo nai = getNetworkAgentInfoForNetId(msg.arg2); if (nai == null) break; - handleCaptivePortalDataUpdate(nai, (CaptivePortalData) msg.obj); + handleCapportApiDataUpdate(nai, (CaptivePortalData) msg.obj); break; } } @@ -2983,9 +3031,7 @@ public class ConnectivityService extends IConnectivityManager.Stub } if (valid != nai.lastValidated) { if (wasDefault) { - mDeps.getMetricsLogger() - .defaultNetworkMetrics().logDefaultNetworkValidity( - SystemClock.elapsedRealtime(), valid); + mMetricsLog.logDefaultNetworkValidity(valid); } final int oldScore = nai.getCurrentScore(); nai.lastValidated = valid; @@ -3297,9 +3343,9 @@ public class ConnectivityService extends IConnectivityManager.Stub handleUpdateLinkProperties(nai, new LinkProperties(nai.linkProperties)); } - private void handleCaptivePortalDataUpdate(@NonNull final NetworkAgentInfo nai, + private void handleCapportApiDataUpdate(@NonNull final NetworkAgentInfo nai, @Nullable final CaptivePortalData data) { - nai.captivePortalData = data; + nai.capportApiData = data; // CaptivePortalData will be merged into LinkProperties from NetworkAgentInfo handleUpdateLinkProperties(nai, new LinkProperties(nai.linkProperties)); } @@ -3413,7 +3459,9 @@ public class ConnectivityService extends IConnectivityManager.Stub // if there is a fallback. Taken together, the two form a X -> 0, 0 -> Y sequence // whose timestamps tell how long it takes to recover a default network. long now = SystemClock.elapsedRealtime(); - mDeps.getMetricsLogger().defaultNetworkMetrics().logDefaultNetworkEvent(now, null, nai); + mMetricsLog.logDefaultNetworkEvent(null, 0, false, + null /* lp */, null /* nc */, nai.network, nai.getCurrentScore(), + nai.linkProperties, nai.networkCapabilities); } notifyIfacesChangedForNetworkStats(); // TODO - we shouldn't send CALLBACK_LOST to requests that can be satisfied @@ -5965,6 +6013,9 @@ public class ConnectivityService extends IConnectivityManager.Stub // priority networks like ethernet are active. private final NetworkRequest mDefaultWifiRequest; + // Request used to optionally keep vehicle internal network always active + private final NetworkRequest mDefaultVehicleRequest; + private NetworkAgentInfo getDefaultNetwork() { return mDefaultNetworkNai; } @@ -6104,6 +6155,7 @@ public class ConnectivityService extends IConnectivityManager.Stub private void processLinkPropertiesFromAgent(NetworkAgentInfo nai, LinkProperties lp) { lp.ensureDirectlyConnectedRoutes(); nai.clatd.setNat64PrefixFromRa(lp.getNat64Prefix()); + nai.networkAgentPortalData = lp.getCaptivePortalData(); } private void updateLinkProperties(NetworkAgentInfo networkAgent, LinkProperties newLp, @@ -6147,9 +6199,11 @@ public class ConnectivityService extends IConnectivityManager.Stub updateWakeOnLan(newLp); - // Captive portal data is obtained from NetworkMonitor and stored in NetworkAgentInfo, - // it is not contained in LinkProperties sent from NetworkAgents so needs to be merged here. - newLp.setCaptivePortalData(networkAgent.captivePortalData); + // Captive portal data is obtained from NetworkMonitor and stored in NetworkAgentInfo. + // It is not always contained in the LinkProperties sent from NetworkAgents, and if it + // does, it needs to be merged here. + newLp.setCaptivePortalData(mergeCaptivePortalData(networkAgent.networkAgentPortalData, + networkAgent.capportApiData)); // TODO - move this check to cover the whole function if (!Objects.equals(newLp, oldLp)) { @@ -6169,6 +6223,57 @@ public class ConnectivityService extends IConnectivityManager.Stub mKeepaliveTracker.handleCheckKeepalivesStillValid(networkAgent); } + /** + * @param naData captive portal data from NetworkAgent + * @param apiData captive portal data from capport API + */ + @Nullable + private CaptivePortalData mergeCaptivePortalData(CaptivePortalData naData, + CaptivePortalData apiData) { + if (naData == null || apiData == null) { + return naData == null ? apiData : naData; + } + final CaptivePortalData.Builder captivePortalBuilder = + new CaptivePortalData.Builder(naData); + + if (apiData.isCaptive()) { + captivePortalBuilder.setCaptive(true); + } + if (apiData.isSessionExtendable()) { + captivePortalBuilder.setSessionExtendable(true); + } + if (apiData.getExpiryTimeMillis() >= 0 || apiData.getByteLimit() >= 0) { + // Expiry time, bytes remaining, refresh time all need to come from the same source, + // otherwise data would be inconsistent. Prefer the capport API info if present, + // as it can generally be refreshed more often. + captivePortalBuilder.setExpiryTime(apiData.getExpiryTimeMillis()); + captivePortalBuilder.setBytesRemaining(apiData.getByteLimit()); + captivePortalBuilder.setRefreshTime(apiData.getRefreshTimeMillis()); + } else if (naData.getExpiryTimeMillis() < 0 && naData.getByteLimit() < 0) { + // No source has time / bytes remaining information: surface the newest refresh time + // for other fields + captivePortalBuilder.setRefreshTime( + Math.max(naData.getRefreshTimeMillis(), apiData.getRefreshTimeMillis())); + } + + // Prioritize the user portal URL from the network agent. + if (apiData.getUserPortalUrl() != null && (naData.getUserPortalUrl() == null + || TextUtils.isEmpty(naData.getUserPortalUrl().toSafeString()))) { + captivePortalBuilder.setUserPortalUrl(apiData.getUserPortalUrl()); + } + // Prioritize the venue information URL from the network agent. + if (apiData.getVenueInfoUrl() != null && (naData.getVenueInfoUrl() == null + || TextUtils.isEmpty(naData.getVenueInfoUrl().toSafeString()))) { + captivePortalBuilder.setVenueInfoUrl(apiData.getVenueInfoUrl()); + + // Note that venue friendly name can only come from the network agent because it is not + // in use in RFC8908. However, if using the Capport venue URL, make sure that the + // friendly name is not set from the network agent. + captivePortalBuilder.setVenueFriendlyName(null); + } + return captivePortalBuilder.build(); + } + private void wakeupModifyInterface(String iface, NetworkCapabilities caps, boolean add) { // Marks are only available on WiFi interfaces. Checking for // marks on unsupported interfaces is harmless. @@ -6675,6 +6780,39 @@ public class ConnectivityService extends IConnectivityManager.Stub return stableRanges; } + private void maybeCloseSockets(NetworkAgentInfo nai, UidRangeParcel[] ranges, + int[] exemptUids) { + if (nai.isVPN() && !nai.networkAgentConfig.allowBypass) { + try { + mNetd.socketDestroy(ranges, exemptUids); + } catch (Exception e) { + loge("Exception in socket destroy: ", e); + } + } + } + + private void updateUidRanges(boolean add, NetworkAgentInfo nai, Set<UidRange> uidRanges) { + int[] exemptUids = new int[2]; + // TODO: Excluding VPN_UID is necessary in order to not to kill the TCP connection used + // by PPTP. Fix this by making Vpn set the owner UID to VPN_UID instead of system when + // starting a legacy VPN, and remove VPN_UID here. (b/176542831) + exemptUids[0] = VPN_UID; + exemptUids[1] = nai.networkCapabilities.getOwnerUid(); + UidRangeParcel[] ranges = toUidRangeStableParcels(uidRanges); + + maybeCloseSockets(nai, ranges, exemptUids); + try { + if (add) { + mNetd.networkAddUidRanges(nai.network.netId, ranges); + } else { + mNetd.networkRemoveUidRanges(nai.network.netId, ranges); + } + } catch (Exception e) { + loge("Exception while " + (add ? "adding" : "removing") + " uid ranges " + uidRanges + + " on netId " + nai.network.netId + ". " + e); + } + maybeCloseSockets(nai, ranges, exemptUids); + } private void updateUids(NetworkAgentInfo nai, NetworkCapabilities prevNc, NetworkCapabilities newNc) { @@ -6694,12 +6832,21 @@ public class ConnectivityService extends IConnectivityManager.Stub // in both ranges are not subject to any VPN routing rules. Adding new range before // removing old range works because, unlike the filtering rules below, it's possible to // add duplicate UID routing rules. + // TODO: calculate the intersection of add & remove. Imagining that we are trying to + // remove uid 3 from a set containing 1-5. Intersection of the prev and new sets is: + // [1-5] & [1-2],[4-5] == [3] + // Then we can do: + // maybeCloseSockets([3]) + // mNetd.networkAddUidRanges([1-2],[4-5]) + // mNetd.networkRemoveUidRanges([1-5]) + // maybeCloseSockets([3]) + // This can prevent the sockets of uid 1-2, 4-5 from being closed. It also reduce the + // number of binder calls from 6 to 4. if (!newRanges.isEmpty()) { - mNetd.networkAddUidRanges(nai.network.netId, toUidRangeStableParcels(newRanges)); + updateUidRanges(true, nai, newRanges); } if (!prevRanges.isEmpty()) { - mNetd.networkRemoveUidRanges( - nai.network.netId, toUidRangeStableParcels(prevRanges)); + updateUidRanges(false, nai, prevRanges); } final boolean wasFiltering = requiresVpnIsolation(nai, prevNc, nai.linkProperties); final boolean shouldFilter = requiresVpnIsolation(nai, newNc, nai.linkProperties); @@ -6848,7 +6995,7 @@ public class ConnectivityService extends IConnectivityManager.Stub networkAgent.networkCapabilities, nri.mPid, nri.mUid); putParcelable( bundle, - maybeSanitizeLocationInfoForCaller( + createWithLocationInfoSanitizedIfNecessaryWhenParceled( nc, nri.mUid, nri.request.getRequestorPackageName())); putParcelable(bundle, linkPropertiesRestrictedForCallerPermissions( networkAgent.linkProperties, nri.mPid, nri.mUid)); @@ -6867,7 +7014,7 @@ public class ConnectivityService extends IConnectivityManager.Stub networkAgent.networkCapabilities, nri.mPid, nri.mUid); putParcelable( bundle, - maybeSanitizeLocationInfoForCaller( + createWithLocationInfoSanitizedIfNecessaryWhenParceled( netCap, nri.mUid, nri.request.getRequestorPackageName())); break; } @@ -7167,9 +7314,28 @@ public class ConnectivityService extends IConnectivityManager.Stub updateDataActivityTracking(newDefaultNetwork, oldDefaultNetwork); // Notify system services of the new default. makeDefault(newDefaultNetwork); + // Log 0 -> X and Y -> X default network transitions, where X is the new default. - mDeps.getMetricsLogger().defaultNetworkMetrics().logDefaultNetworkEvent( - now, newDefaultNetwork, oldDefaultNetwork); + final Network network = (newDefaultNetwork != null) ? newDefaultNetwork.network : null; + final int score = (newDefaultNetwork != null) ? newDefaultNetwork.getCurrentScore() : 0; + final boolean validated = newDefaultNetwork != null && newDefaultNetwork.lastValidated; + final LinkProperties lp = (newDefaultNetwork != null) + ? newDefaultNetwork.linkProperties : null; + final NetworkCapabilities nc = (newDefaultNetwork != null) + ? newDefaultNetwork.networkCapabilities : null; + + final Network prevNetwork = (oldDefaultNetwork != null) + ? oldDefaultNetwork.network : null; + final int prevScore = (oldDefaultNetwork != null) + ? oldDefaultNetwork.getCurrentScore() : 0; + final LinkProperties prevLp = (oldDefaultNetwork != null) + ? oldDefaultNetwork.linkProperties : null; + final NetworkCapabilities prevNc = (oldDefaultNetwork != null) + ? oldDefaultNetwork.networkCapabilities : null; + + mMetricsLog.logDefaultNetworkEvent(network, score, validated, lp, nc, + prevNetwork, prevScore, prevLp, prevNc); + // Have a new default network, release the transition wakelock in scheduleReleaseNetworkTransitionWakelock(); } diff --git a/services/core/java/com/android/server/DropBoxManagerInternal.java b/services/core/java/com/android/server/DropBoxManagerInternal.java new file mode 100644 index 000000000000..3785a9c011ce --- /dev/null +++ b/services/core/java/com/android/server/DropBoxManagerInternal.java @@ -0,0 +1,40 @@ +/* + * 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; + +import android.annotation.BytesLong; +import android.annotation.NonNull; +import android.os.DropBoxManager; + +import java.io.Closeable; +import java.io.FileDescriptor; +import java.io.IOException; + +public abstract class DropBoxManagerInternal { + public abstract void addEntry(@NonNull String tag, @NonNull EntrySource source, + @DropBoxManager.Flags int flags); + + /** + * Interface which describes a pending entry which knows how to write itself + * to the given FD. This abstraction supports implementations which may want + * to dynamically generate the entry contents. + */ + public interface EntrySource extends Closeable { + public @BytesLong long length(); + public void writeTo(@NonNull FileDescriptor fd) throws IOException; + } +} diff --git a/services/core/java/com/android/server/DropBoxManagerService.java b/services/core/java/com/android/server/DropBoxManagerService.java index 30fc3364f6b7..a6d9bf8bc55b 100644 --- a/services/core/java/com/android/server/DropBoxManagerService.java +++ b/services/core/java/com/android/server/DropBoxManagerService.java @@ -35,6 +35,7 @@ import android.os.FileUtils; import android.os.Handler; import android.os.Looper; import android.os.Message; +import android.os.ParcelFileDescriptor; import android.os.ResultReceiver; import android.os.ShellCallback; import android.os.ShellCommand; @@ -43,6 +44,10 @@ import android.os.SystemClock; import android.os.UserHandle; import android.provider.Settings; import android.service.dropbox.DropBoxManagerServiceDumpProto; +import android.system.ErrnoException; +import android.system.Os; +import android.system.OsConstants; +import android.system.StructStat; import android.text.TextUtils; import android.text.format.TimeMigrationUtils; import android.util.ArrayMap; @@ -56,18 +61,19 @@ import com.android.internal.annotations.VisibleForTesting; import com.android.internal.os.IDropBoxManagerService; import com.android.internal.util.DumpUtils; import com.android.internal.util.ObjectUtils; +import com.android.server.DropBoxManagerInternal.EntrySource; import libcore.io.IoUtils; -import java.io.BufferedOutputStream; +import java.io.ByteArrayInputStream; import java.io.File; import java.io.FileDescriptor; import java.io.FileOutputStream; import java.io.IOException; import java.io.InputStream; import java.io.InputStreamReader; -import java.io.OutputStream; import java.io.PrintWriter; +import java.nio.charset.StandardCharsets; import java.util.ArrayList; import java.util.Arrays; import java.util.SortedSet; @@ -93,6 +99,9 @@ public final class DropBoxManagerService extends SystemService { // Max number of bytes of a dropbox entry to write into protobuf. private static final int PROTO_MAX_DATA_BYTES = 256 * 1024; + // Size beyond which to force-compress newly added entries. + private static final long COMPRESS_THRESHOLD_BYTES = 16_384; + // TODO: This implementation currently uses one file per entry, which is // inefficient for smallish entries -- consider using a single queue file // per tag (or even globally) instead. @@ -149,8 +158,13 @@ public final class DropBoxManagerService extends SystemService { private final IDropBoxManagerService.Stub mStub = new IDropBoxManagerService.Stub() { @Override - public void add(DropBoxManager.Entry entry) { - DropBoxManagerService.this.add(entry); + public void addData(String tag, byte[] data, int flags) { + DropBoxManagerService.this.addData(tag, data, flags); + } + + @Override + public void addFile(String tag, ParcelFileDescriptor fd, int flags) { + DropBoxManagerService.this.addFile(tag, fd, flags); } @Override @@ -333,6 +347,7 @@ public final class DropBoxManagerService extends SystemService { mDropBoxDir = path; mContentResolver = getContext().getContentResolver(); mHandler = new DropBoxManagerBroadcastHandler(looper); + LocalServices.addService(DropBoxManagerInternal.class, new DropBoxManagerInternalImpl()); } @Override @@ -374,77 +389,101 @@ public final class DropBoxManagerService extends SystemService { return mStub; } - public void add(DropBoxManager.Entry entry) { - File temp = null; - InputStream input = null; - OutputStream output = null; - final String tag = entry.getTag(); + public void addData(String tag, byte[] data, int flags) { + addEntry(tag, new ByteArrayInputStream(data), data.length, flags); + } + + public void addFile(String tag, ParcelFileDescriptor fd, int flags) { + final StructStat stat; try { - int flags = entry.getFlags(); - Slog.i(TAG, "add tag=" + tag + " isTagEnabled=" + isTagEnabled(tag) - + " flags=0x" + Integer.toHexString(flags)); - if ((flags & DropBoxManager.IS_EMPTY) != 0) throw new IllegalArgumentException(); + stat = Os.fstat(fd.getFileDescriptor()); - init(); - if (!isTagEnabled(tag)) return; - long max = trimToFit(); - long lastTrim = System.currentTimeMillis(); + // Verify caller isn't playing games with pipes or sockets + if (!OsConstants.S_ISREG(stat.st_mode)) { + throw new IllegalArgumentException(tag + " entry must be real file"); + } + } catch (ErrnoException e) { + throw new IllegalArgumentException(e); + } - byte[] buffer = new byte[mBlockSize]; - input = entry.getInputStream(); + addEntry(tag, new ParcelFileDescriptor.AutoCloseInputStream(fd), stat.st_size, flags); + } - // First, accumulate up to one block worth of data in memory before - // deciding whether to compress the data or not. + public void addEntry(String tag, InputStream in, long length, int flags) { + // If entry being added is large, and if it's not already compressed, + // then we'll force compress it during write + boolean forceCompress = false; + if ((flags & DropBoxManager.IS_GZIPPED) == 0 + && length > COMPRESS_THRESHOLD_BYTES) { + forceCompress = true; + flags |= DropBoxManager.IS_GZIPPED; + } - int read = 0; - while (read < buffer.length) { - int n = input.read(buffer, read, buffer.length - read); - if (n <= 0) break; - read += n; - } + addEntry(tag, new SimpleEntrySource(in, length, forceCompress), flags); + } + + /** + * Simple entry which contains data ready to be written. + */ + public static class SimpleEntrySource implements EntrySource { + private final InputStream in; + private final long length; + private final boolean forceCompress; + + public SimpleEntrySource(InputStream in, long length, boolean forceCompress) { + this.in = in; + this.length = length; + this.forceCompress = forceCompress; + } + + public long length() { + return length; + } - // If we have at least one block, compress it -- otherwise, just write - // the data in uncompressed form. - - temp = new File(mDropBoxDir, "drop" + Thread.currentThread().getId() + ".tmp"); - int bufferSize = mBlockSize; - if (bufferSize > 4096) bufferSize = 4096; - if (bufferSize < 512) bufferSize = 512; - FileOutputStream foutput = new FileOutputStream(temp); - output = new BufferedOutputStream(foutput, bufferSize); - if (read == buffer.length && ((flags & DropBoxManager.IS_GZIPPED) == 0)) { - output = new GZIPOutputStream(output); - flags = flags | DropBoxManager.IS_GZIPPED; + @Override + public void writeTo(FileDescriptor fd) throws IOException { + // No need to buffer the output here, since data is either coming + // from an in-memory buffer, or another file on disk; if we buffered + // we'd lose out on sendfile() optimizations + if (forceCompress) { + FileUtils.copy(in, new GZIPOutputStream(new FileOutputStream(fd))); + } else { + FileUtils.copy(in, new FileOutputStream(fd)); } + } - do { - output.write(buffer, 0, read); + @Override + public void close() throws IOException { + FileUtils.closeQuietly(in); + } + } - long now = System.currentTimeMillis(); - if (now - lastTrim > 30 * 1000) { - max = trimToFit(); // In case data dribbles in slowly - lastTrim = now; - } + public void addEntry(String tag, EntrySource entry, int flags) { + File temp = null; + try { + Slog.i(TAG, "add tag=" + tag + " isTagEnabled=" + isTagEnabled(tag) + + " flags=0x" + Integer.toHexString(flags)); + if ((flags & DropBoxManager.IS_EMPTY) != 0) throw new IllegalArgumentException(); - read = input.read(buffer); - if (read <= 0) { - FileUtils.sync(foutput); - output.close(); // Get a final size measurement - output = null; - } else { - output.flush(); // So the size measurement is pseudo-reasonable - } + init(); - long len = temp.length(); - if (len > max) { - Slog.w(TAG, "Dropping: " + tag + " (" + temp.length() + " > " - + max + " bytes)"); - temp.delete(); - temp = null; // Pass temp = null to createEntry() to leave a tombstone - break; + // Bail early if we know tag is disabled + if (!isTagEnabled(tag)) return; + + // Drop entries which are too large for our quota + final long length = entry.length(); + final long max = trimToFit(); + if (length > max) { + // Log and fall through to create empty tombstone below + Slog.w(TAG, "Dropping: " + tag + " (" + length + " > " + max + " bytes)"); + } else { + temp = new File(mDropBoxDir, "drop" + Thread.currentThread().getId() + ".tmp"); + try (FileOutputStream out = new FileOutputStream(temp)) { + entry.writeTo(out.getFD()); } - } while (read > 0); + } + // Writing above succeeded, so create the finalized entry long time = createEntry(temp, tag, flags); temp = null; @@ -461,9 +500,7 @@ public final class DropBoxManagerService extends SystemService { } catch (IOException e) { Slog.e(TAG, "Can't write: " + tag, e); } finally { - IoUtils.closeQuietly(output); - IoUtils.closeQuietly(input); - entry.close(); + IoUtils.closeQuietly(entry); if (temp != null) temp.delete(); } } @@ -1187,4 +1224,11 @@ public final class DropBoxManagerService extends SystemService { mLowPriorityTags.add(lowPrioritytags[i]); } } + + private final class DropBoxManagerInternalImpl extends DropBoxManagerInternal { + @Override + public void addEntry(String tag, EntrySource entry, int flags) { + DropBoxManagerService.this.addEntry(tag, entry, flags); + } + } } diff --git a/services/core/java/com/android/server/TestNetworkService.java b/services/core/java/com/android/server/TestNetworkService.java index e8687e57a07b..a08d066513c7 100644 --- a/services/core/java/com/android/server/TestNetworkService.java +++ b/services/core/java/com/android/server/TestNetworkService.java @@ -242,6 +242,7 @@ class TestNetworkService extends ITestNetworkManager.Stub { nc.addTransportType(NetworkCapabilities.TRANSPORT_TEST); nc.addCapability(NetworkCapabilities.NET_CAPABILITY_NOT_SUSPENDED); nc.addCapability(NetworkCapabilities.NET_CAPABILITY_NOT_RESTRICTED); + nc.addCapability(NetworkCapabilities.NET_CAPABILITY_NOT_VCN_MANAGED); nc.setNetworkSpecifier(new StringNetworkSpecifier(iface)); nc.setAdministratorUids(administratorUids); if (!isMetered) { diff --git a/services/core/java/com/android/server/am/ActiveServices.java b/services/core/java/com/android/server/am/ActiveServices.java index bb07ee6c37a9..5ce630e3daed 100644 --- a/services/core/java/com/android/server/am/ActiveServices.java +++ b/services/core/java/com/android/server/am/ActiveServices.java @@ -170,6 +170,8 @@ public final class ActiveServices { public static final int FGS_FEATURE_ALLOWED_BY_PROCESS_RECORD = 19; public static final int FGS_FEATURE_ALLOWED_BY_EXEMPTED_PACKAGES = 20; public static final int FGS_FEATURE_ALLOWED_BY_ACTIVITY_STARTER = 21; + public static final int FGS_FEATURE_ALLOWED_BY_COMPANION_APP = 22; + public static final int FGS_FEATURE_ALLOWED_BY_PROFILE_OWNER = 23; @IntDef(flag = true, prefix = { "FGS_FEATURE_" }, value = { FGS_FEATURE_DENIED, @@ -192,7 +194,9 @@ public final class ActiveServices { FGS_FEATURE_ALLOWED_BY_DEVICE_DEMO_MODE, FGS_FEATURE_ALLOWED_BY_PROCESS_RECORD, FGS_FEATURE_ALLOWED_BY_EXEMPTED_PACKAGES, - FGS_FEATURE_ALLOWED_BY_ACTIVITY_STARTER + FGS_FEATURE_ALLOWED_BY_ACTIVITY_STARTER, + FGS_FEATURE_ALLOWED_BY_COMPANION_APP, + FGS_FEATURE_ALLOWED_BY_PROFILE_OWNER }) @Retention(RetentionPolicy.SOURCE) public @interface FgsFeatureRetCode {} @@ -5371,6 +5375,14 @@ public final class ActiveServices { } } + if (ret == FGS_FEATURE_DENIED) { + // Is the calling UID a profile owner app? + final boolean isProfileOwner = mAm.mInternal.isProfileOwner(callingUid); + if (isProfileOwner) { + ret = FGS_FEATURE_ALLOWED_BY_PROFILE_OWNER; + } + } + // NOTE this should always be the last check. if (ret == FGS_FEATURE_DENIED) { if (isPackageExemptedFromFgsRestriction(r.appInfo.packageName, r.appInfo.uid) @@ -5379,6 +5391,14 @@ public final class ActiveServices { } } + if (ret == FGS_FEATURE_DENIED) { + final boolean isCompanionApp = mAm.mInternal.isAssociatedCompanionApp( + UserHandle.getUserId(callingUid), callingUid); + if (isCompanionApp) { + ret = FGS_FEATURE_ALLOWED_BY_COMPANION_APP; + } + } + final String debugInfo = "[callingPackage: " + callingPackage + "; callingUid: " + callingUid @@ -5462,6 +5482,10 @@ public final class ActiveServices { return "FGS_FEATURE_ALLOWED_BY_EXEMPTED_PACKAGES"; case FGS_FEATURE_ALLOWED_BY_ACTIVITY_STARTER: return "ALLOWED_BY_ACTIVITY_STARTER"; + case FGS_FEATURE_ALLOWED_BY_COMPANION_APP: + return "ALLOWED_BY_COMPANION_APP"; + case FGS_FEATURE_ALLOWED_BY_PROFILE_OWNER: + return "ALLOWED_BY_PROFILE_OWNER"; default: return ""; } diff --git a/services/core/java/com/android/server/am/ActivityManagerService.java b/services/core/java/com/android/server/am/ActivityManagerService.java index c1ab5b6a315e..be1d1bcbdfff 100644 --- a/services/core/java/com/android/server/am/ActivityManagerService.java +++ b/services/core/java/com/android/server/am/ActivityManagerService.java @@ -174,7 +174,6 @@ import android.app.ProfilerInfo; import android.app.WaitResult; import android.app.backup.BackupManager.OperationType; import android.app.backup.IBackupManager; -import android.app.compat.CompatChanges; import android.app.usage.UsageEvents; import android.app.usage.UsageEvents.Event; import android.app.usage.UsageStatsManager; @@ -573,6 +572,16 @@ public class ActivityManagerService extends IActivityManager.Stub private int mDeviceOwnerUid = Process.INVALID_UID; + /** + * Map userId to its companion app uids. + */ + private final Map<Integer, Set<Integer>> mCompanionAppUidsMap = new ArrayMap<>(); + + /** + * The profile owner UIDs. + */ + private ArraySet<Integer> mProfileOwnerUids = null; + final UserController mUserController; @VisibleForTesting public final PendingIntentController mPendingIntentController; @@ -13707,34 +13716,10 @@ public class ActivityManagerService extends IActivityManager.Stub false, false, userId, "package unstartable"); break; case Intent.ACTION_CLOSE_SYSTEM_DIALOGS: - if (!canCloseSystemDialogs(callingPid, callingUid, callerApp)) { - // The app can't close system dialogs, throw only if it targets S+ - if (CompatChanges.isChangeEnabled( - ActivityManager.LOCK_DOWN_CLOSE_SYSTEM_DIALOGS, callingUid)) { - throw new SecurityException( - "Permission Denial: " + Intent.ACTION_CLOSE_SYSTEM_DIALOGS - + " broadcast from " + callerPackage + " (pid=" - + callingPid + ", uid=" + callingUid + ")" - + " requires " - + permission.BROADCAST_CLOSE_SYSTEM_DIALOGS + "."); - } else if (CompatChanges.isChangeEnabled( - ActivityManager.DROP_CLOSE_SYSTEM_DIALOGS, callingUid)) { - Slog.w(TAG, "Permission Denial: " + intent.getAction() - + " broadcast from " + callerPackage + " (pid=" + callingPid - + ", uid=" + callingUid + ")" - + " requires " - + permission.BROADCAST_CLOSE_SYSTEM_DIALOGS - + ", dropping broadcast."); - // Returning success seems to be the pattern here - return ActivityManager.BROADCAST_SUCCESS; - } else { - Slog.w(TAG, intent.getAction() - + " broadcast from " + callerPackage + " (pid=" + callingPid - + ", uid=" + callingUid + ")" - + " will require " - + permission.BROADCAST_CLOSE_SYSTEM_DIALOGS - + " in future builds."); - } + if (!mAtmInternal.checkCanCloseSystemDialogs(callingPid, callingUid, + callerPackage)) { + // Returning success seems to be the pattern here + return ActivityManager.BROADCAST_SUCCESS; } break; } @@ -14029,39 +14014,6 @@ public class ActivityManagerService extends IActivityManager.Stub return ActivityManager.BROADCAST_SUCCESS; } - private boolean canCloseSystemDialogs(int pid, int uid, @Nullable ProcessRecord callerApp) { - if (checkPermission(permission.BROADCAST_CLOSE_SYSTEM_DIALOGS, pid, uid) - == PERMISSION_GRANTED) { - return true; - } - if (callerApp == null) { - synchronized (mPidsSelfLocked) { - callerApp = mPidsSelfLocked.get(pid); - } - } - - if (callerApp != null) { - // Check if the instrumentation of the process has the permission. This covers the usual - // test started from the shell (which has the permission) case. This is needed for apps - // targeting SDK level < S but we are also allowing for targetSdk S+ as a convenience to - // avoid breaking a bunch of existing tests and asking them to adopt shell permissions - // to do this. - ActiveInstrumentation instrumentation = callerApp.getActiveInstrumentation(); - if (instrumentation != null && checkPermission( - permission.BROADCAST_CLOSE_SYSTEM_DIALOGS, -1, instrumentation.mSourceUid) - == PERMISSION_GRANTED) { - return true; - } - // This is the notification trampoline use-case for example, where apps use Intent.ACSD - // to close the shade prior to starting an activity. - WindowProcessController wmApp = callerApp.getWindowProcessController(); - if (wmApp.canCloseSystemDialogsByToken()) { - return true; - } - } - return false; - } - /** * @return uid from the extra field {@link Intent#EXTRA_UID} if present, Otherwise -1 */ @@ -16783,6 +16735,37 @@ public class ActivityManagerService extends IActivityManager.Stub } } + + @Override + public void setProfileOwnerUid(ArraySet<Integer> profileOwnerUids) { + synchronized (ActivityManagerService.this) { + mProfileOwnerUids = profileOwnerUids; + } + } + + @Override + public boolean isProfileOwner(int uid) { + synchronized (ActivityManagerService.this) { + return mProfileOwnerUids != null && mProfileOwnerUids.indexOf(uid) >= 0; + } + } + + @Override + public void setCompanionAppUids(int userId, Set<Integer> companionAppUids) { + synchronized (ActivityManagerService.this) { + mCompanionAppUidsMap.put(userId, companionAppUids); + } + } + + @Override + public boolean isAssociatedCompanionApp(int userId, int uid) { + final Set<Integer> allUids = mCompanionAppUidsMap.get(userId); + if (allUids == null) { + return false; + } + return allUids.contains(uid); + } + @Override public void addPendingTopUid(int uid, int pid) { mPendingStartActivityUids.add(uid, pid); diff --git a/services/core/java/com/android/server/am/CachedAppOptimizer.java b/services/core/java/com/android/server/am/CachedAppOptimizer.java index 36d4a38c1624..9eb7c07baaed 100644 --- a/services/core/java/com/android/server/am/CachedAppOptimizer.java +++ b/services/core/java/com/android/server/am/CachedAppOptimizer.java @@ -1101,15 +1101,26 @@ public final class CachedAppOptimizer { } private void freezeProcess(ProcessRecord proc) { - final int pid; - final String name; + final int pid = proc.pid; + final String name = proc.processName; final long unfrozenDuration; final boolean frozen; - synchronized (mAm) { - pid = proc.pid; - name = proc.processName; + try { + // pre-check for locks to avoid unnecessary freeze/unfreeze operations + if (Process.hasFileLocks(pid)) { + if (DEBUG_FREEZER) { + Slog.d(TAG_AM, name + " (" + pid + ") holds file locks, not freezing"); + } + return; + } + } catch (IOException e) { + Slog.e(TAG_AM, "Not freezing. Unable to check file locks for " + name + "(" + pid + + "): " + e); + return; + } + synchronized (mAm) { if (proc.curAdj < ProcessList.CACHED_APP_MIN_ADJ || proc.shouldNotFreeze) { if (DEBUG_FREEZER) { @@ -1141,29 +1152,50 @@ public final class CachedAppOptimizer { frozen = proc.frozen; } - if (frozen) { - if (DEBUG_FREEZER) { - Slog.d(TAG_AM, "froze " + pid + " " + name); - } + if (!frozen) { + return; + } - EventLog.writeEvent(EventLogTags.AM_FREEZE, pid, name); - try { - freezeBinder(pid, true); - } catch (RuntimeException e) { - Slog.e(TAG_AM, "Unable to freeze binder for " + pid + " " + name); - proc.kill("Unable to freeze binder interface", - ApplicationExitInfo.REASON_OTHER, - ApplicationExitInfo.SUBREASON_INVALID_STATE, true); - } + if (DEBUG_FREEZER) { + Slog.d(TAG_AM, "froze " + pid + " " + name); + } + + EventLog.writeEvent(EventLogTags.AM_FREEZE, pid, name); + + try { + freezeBinder(pid, true); + } catch (RuntimeException e) { + Slog.e(TAG_AM, "Unable to freeze binder for " + pid + " " + name); + proc.kill("Unable to freeze binder interface", + ApplicationExitInfo.REASON_OTHER, + ApplicationExitInfo.SUBREASON_INVALID_STATE, true); + } + + // See above for why we're not taking mPhenotypeFlagLock here + if (mRandom.nextFloat() < mFreezerStatsdSampleRate) { + FrameworkStatsLog.write(FrameworkStatsLog.APP_FREEZE_CHANGED, + FrameworkStatsLog.APP_FREEZE_CHANGED__ACTION__FREEZE_APP, + pid, + name, + unfrozenDuration); + } + + try { + // post-check to prevent races + if (Process.hasFileLocks(pid)) { + if (DEBUG_FREEZER) { + Slog.d(TAG_AM, name + " (" + pid + ") holds file locks, reverting freeze"); + } - // See above for why we're not taking mPhenotypeFlagLock here - if (mRandom.nextFloat() < mFreezerStatsdSampleRate) { - FrameworkStatsLog.write(FrameworkStatsLog.APP_FREEZE_CHANGED, - FrameworkStatsLog.APP_FREEZE_CHANGED__ACTION__FREEZE_APP, - pid, - name, - unfrozenDuration); + synchronized (mAm) { + unfreezeAppLocked(proc); + } + } + } catch (IOException e) { + Slog.e(TAG_AM, "Unable to check file locks for " + name + "(" + pid + "): " + e); + synchronized (mAm) { + unfreezeAppLocked(proc); } } } diff --git a/services/core/java/com/android/server/am/ProcessRecord.java b/services/core/java/com/android/server/am/ProcessRecord.java index e90423c2566a..d4d01652e338 100644 --- a/services/core/java/com/android/server/am/ProcessRecord.java +++ b/services/core/java/com/android/server/am/ProcessRecord.java @@ -1431,7 +1431,9 @@ class ProcessRecord implements WindowProcessListener { void setActiveInstrumentation(ActiveInstrumentation instr) { mInstr = instr; boolean isInstrumenting = instr != null; - mWindowProcessController.setInstrumenting(isInstrumenting, + mWindowProcessController.setInstrumenting( + isInstrumenting, + isInstrumenting ? instr.mSourceUid : -1, isInstrumenting && instr.mHasBackgroundActivityStartsPermission); } @@ -2069,6 +2071,20 @@ class ProcessRecord implements WindowProcessListener { } if (!mAllowStartFgs) { + if (mService.mInternal != null) { + mAllowStartFgs = mService.mInternal.isAssociatedCompanionApp( + UserHandle.getUserId(info.uid), info.uid); + } + } + + if (!mAllowStartFgs) { + // Is the calling UID a profile owner app? + if (mService.mInternal != null) { + mAllowStartFgs = mService.mInternal.isProfileOwner(info.uid); + } + } + + if (!mAllowStartFgs) { // uid is on DeviceIdleController's user/system allowlist // or AMS's FgsStartTempAllowList. mAllowStartFgs = mService.isWhitelistedForFgsStartLocked(info.uid); diff --git a/services/core/java/com/android/server/biometrics/AuthService.java b/services/core/java/com/android/server/biometrics/AuthService.java index cf8bfbc547f8..b15a8869b22a 100644 --- a/services/core/java/com/android/server/biometrics/AuthService.java +++ b/services/core/java/com/android/server/biometrics/AuthService.java @@ -38,6 +38,7 @@ import android.hardware.biometrics.IBiometricAuthenticator; import android.hardware.biometrics.IBiometricEnabledOnKeyguardCallback; import android.hardware.biometrics.IBiometricService; import android.hardware.biometrics.IBiometricServiceReceiver; +import android.hardware.biometrics.IInvalidationCallback; import android.hardware.biometrics.ITestSession; import android.hardware.biometrics.PromptInfo; import android.hardware.biometrics.SensorPropertiesInternal; @@ -296,6 +297,19 @@ public class AuthService extends SystemService { } @Override + public void invalidateAuthenticatorIds(int userId, int fromSensorId, + IInvalidationCallback callback) throws RemoteException { + checkInternalPermission(); + + final long identity = Binder.clearCallingIdentity(); + try { + mBiometricService.invalidateAuthenticatorIds(userId, fromSensorId, callback); + } finally { + Binder.restoreCallingIdentity(identity); + } + } + + @Override public long[] getAuthenticatorIds() throws RemoteException { // In this method, we're not checking whether the caller is permitted to use face // API because current authenticator ID is leaked (in a more contrived way) via Android diff --git a/services/core/java/com/android/server/biometrics/BiometricService.java b/services/core/java/com/android/server/biometrics/BiometricService.java index a81abcd3b510..fd5ada0ff9be 100644 --- a/services/core/java/com/android/server/biometrics/BiometricService.java +++ b/services/core/java/com/android/server/biometrics/BiometricService.java @@ -42,6 +42,7 @@ import android.hardware.biometrics.IBiometricSensorReceiver; import android.hardware.biometrics.IBiometricService; import android.hardware.biometrics.IBiometricServiceReceiver; import android.hardware.biometrics.IBiometricSysuiReceiver; +import android.hardware.biometrics.IInvalidationCallback; import android.hardware.biometrics.ITestSession; import android.hardware.biometrics.PromptInfo; import android.hardware.biometrics.SensorPropertiesInternal; @@ -60,6 +61,7 @@ import android.os.UserHandle; import android.provider.Settings; import android.security.KeyStore; import android.text.TextUtils; +import android.util.ArraySet; import android.util.Pair; import android.util.Slog; import android.util.proto.ProtoOutputStream; @@ -78,6 +80,7 @@ import java.util.HashMap; import java.util.List; import java.util.Map; import java.util.Random; +import java.util.Set; /** * System service that arbitrates the modality for BiometricPrompt to use. @@ -240,6 +243,72 @@ public class BiometricService extends SystemService { } }; + /** + * Tracks authenticatorId invalidation. For more details, see + * {@link com.android.server.biometrics.sensors.InvalidationRequesterClient}. + */ + @VisibleForTesting + static class InvalidationTracker { + @NonNull private final IInvalidationCallback mClientCallback; + @NonNull private final Set<Integer> mSensorsPendingInvalidation; + + public static InvalidationTracker start(@NonNull ArrayList<BiometricSensor> sensors, + int userId, int fromSensorId, @NonNull IInvalidationCallback clientCallback) { + return new InvalidationTracker(sensors, userId, fromSensorId, clientCallback); + } + + private InvalidationTracker(@NonNull ArrayList<BiometricSensor> sensors, int userId, + int fromSensorId, @NonNull IInvalidationCallback clientCallback) { + mClientCallback = clientCallback; + mSensorsPendingInvalidation = new ArraySet<>(); + + for (BiometricSensor sensor : sensors) { + if (sensor.id == fromSensorId) { + continue; + } + + if (!Utils.isAtLeastStrength(sensor.oemStrength, Authenticators.BIOMETRIC_STRONG)) { + continue; + } + + Slog.d(TAG, "Requesting authenticatorId invalidation for sensor: " + sensor.id); + + synchronized (this) { + mSensorsPendingInvalidation.add(sensor.id); + } + + try { + sensor.impl.invalidateAuthenticatorId(userId, new IInvalidationCallback.Stub() { + @Override + public void onCompleted() { + onInvalidated(sensor.id); + } + }); + } catch (RemoteException e) { + Slog.d(TAG, "RemoteException", e); + } + } + } + + @VisibleForTesting + void onInvalidated(int sensorId) { + synchronized (this) { + mSensorsPendingInvalidation.remove(sensorId); + + Slog.d(TAG, "Sensor " + sensorId + " invalidated, remaining size: " + + mSensorsPendingInvalidation.size()); + + if (mSensorsPendingInvalidation.isEmpty()) { + try { + mClientCallback.onCompleted(); + } catch (RemoteException e) { + Slog.e(TAG, "Remote Exception", e); + } + } + } + } + } + @VisibleForTesting public static class SettingObserver extends ContentObserver { @@ -668,6 +737,14 @@ public class BiometricService extends SystemService { } } + @Override + public void invalidateAuthenticatorIds(int userId, int fromSensorId, + IInvalidationCallback callback) { + checkInternalPermission(); + + InvalidationTracker.start(mSensors, userId, fromSensorId, callback); + } + @Override // Binder call public long[] getAuthenticatorIds(int callingUserId) { checkInternalPermission(); diff --git a/services/core/java/com/android/server/biometrics/sensors/InvalidationRequesterClient.java b/services/core/java/com/android/server/biometrics/sensors/InvalidationRequesterClient.java index ca34eee17f7f..d95fa236fcef 100644 --- a/services/core/java/com/android/server/biometrics/sensors/InvalidationRequesterClient.java +++ b/services/core/java/com/android/server/biometrics/sensors/InvalidationRequesterClient.java @@ -18,8 +18,10 @@ package com.android.server.biometrics.sensors; import android.annotation.NonNull; import android.content.Context; +import android.hardware.biometrics.BiometricAuthenticator; import android.hardware.biometrics.BiometricManager; import android.hardware.biometrics.BiometricsProtoEnums; +import android.hardware.biometrics.IInvalidationCallback; /** * ClientMonitor subclass responsible for coordination of authenticatorId invalidation of other @@ -53,25 +55,39 @@ import android.hardware.biometrics.BiometricsProtoEnums; * switches, the framework can check if any sensor has the "invalidationInProgress" flag set. If so, * the framework should re-start the invalidation process described above. */ -public abstract class InvalidationRequesterClient<T> extends ClientMonitor<T> { +public abstract class InvalidationRequesterClient<S extends BiometricAuthenticator.Identifier, T> + extends ClientMonitor<T> { private final BiometricManager mBiometricManager; + @NonNull private final BiometricUtils<S> mUtils; + + @NonNull private final IInvalidationCallback mInvalidationCallback = + new IInvalidationCallback.Stub() { + @Override + public void onCompleted() { + mUtils.setInvalidationInProgress(getContext(), getTargetUserId(), + false /* inProgress */); + mCallback.onClientFinished(InvalidationRequesterClient.this, true /* success */); + } + }; public InvalidationRequesterClient(@NonNull Context context, @NonNull LazyDaemon<T> lazyDaemon, - int userId, int sensorId) { + int userId, int sensorId, @NonNull BiometricUtils<S> utils) { super(context, lazyDaemon, null /* token */, null /* listener */, userId, context.getOpPackageName(), 0 /* cookie */, sensorId, BiometricsProtoEnums.MODALITY_UNKNOWN, BiometricsProtoEnums.ACTION_UNKNOWN, BiometricsProtoEnums.CLIENT_UNKNOWN); mBiometricManager = context.getSystemService(BiometricManager.class); + mUtils = utils; } @Override public void start(@NonNull Callback callback) { super.start(callback); - // TODO(b/159667191): Request BiometricManager/BiometricService to invalidate - // authenticatorIds. Be sure to invoke BiometricUtils#setInvalidationInProgress(true) + mUtils.setInvalidationInProgress(getContext(), getTargetUserId(), true /* inProgress */); + mBiometricManager.invalidateAuthenticatorIds(getTargetUserId(), getSensorId(), + mInvalidationCallback); } @Override diff --git a/services/core/java/com/android/server/biometrics/sensors/face/FaceAuthenticator.java b/services/core/java/com/android/server/biometrics/sensors/face/FaceAuthenticator.java index f07bf1e236b8..54ab2e564676 100644 --- a/services/core/java/com/android/server/biometrics/sensors/face/FaceAuthenticator.java +++ b/services/core/java/com/android/server/biometrics/sensors/face/FaceAuthenticator.java @@ -19,13 +19,13 @@ package com.android.server.biometrics.sensors.face; import android.annotation.NonNull; import android.hardware.biometrics.IBiometricAuthenticator; import android.hardware.biometrics.IBiometricSensorReceiver; +import android.hardware.biometrics.IInvalidationCallback; import android.hardware.biometrics.ITestSession; import android.hardware.biometrics.SensorPropertiesInternal; import android.hardware.face.IFaceService; import android.os.IBinder; import android.os.RemoteException; -import com.android.server.biometrics.SensorConfig; import com.android.server.biometrics.sensors.LockoutTracker; /** @@ -87,6 +87,12 @@ public final class FaceAuthenticator extends IBiometricAuthenticator.Stub { } @Override + public void invalidateAuthenticatorId(int userId, IInvalidationCallback callback) + throws RemoteException { + mFaceService.invalidateAuthenticatorId(mSensorId, userId, callback); + } + + @Override public @LockoutTracker.LockoutMode int getLockoutModeForUser(int userId) throws RemoteException { return mFaceService.getLockoutModeForUser(mSensorId, userId); diff --git a/services/core/java/com/android/server/biometrics/sensors/face/FaceService.java b/services/core/java/com/android/server/biometrics/sensors/face/FaceService.java index cb56e8cd4b7f..f055d559cc83 100644 --- a/services/core/java/com/android/server/biometrics/sensors/face/FaceService.java +++ b/services/core/java/com/android/server/biometrics/sensors/face/FaceService.java @@ -29,6 +29,7 @@ import android.hardware.biometrics.BiometricsProtoEnums; import android.hardware.biometrics.IBiometricSensorReceiver; import android.hardware.biometrics.IBiometricService; import android.hardware.biometrics.IBiometricServiceLockoutResetCallback; +import android.hardware.biometrics.IInvalidationCallback; import android.hardware.biometrics.ITestSession; import android.hardware.biometrics.face.IFace; import android.hardware.biometrics.face.SensorProps; @@ -54,10 +55,10 @@ import com.android.internal.widget.LockPatternUtils; import com.android.server.ServiceThread; import com.android.server.SystemService; import com.android.server.biometrics.Utils; +import com.android.server.biometrics.sensors.BiometricServiceCallback; import com.android.server.biometrics.sensors.ClientMonitorCallbackConverter; import com.android.server.biometrics.sensors.LockoutResetDispatcher; import com.android.server.biometrics.sensors.LockoutTracker; -import com.android.server.biometrics.sensors.BiometricServiceCallback; import com.android.server.biometrics.sensors.face.aidl.FaceProvider; import com.android.server.biometrics.sensors.face.hidl.Face10; @@ -503,6 +504,19 @@ public class FaceService extends SystemService implements BiometricServiceCallba return provider.getLockoutModeForUser(sensorId, userId); } + @Override + public void invalidateAuthenticatorId(int sensorId, int userId, + IInvalidationCallback callback) { + Utils.checkPermission(getContext(), USE_BIOMETRIC_INTERNAL); + + final ServiceProvider provider = getProviderForSensor(sensorId); + if (provider == null) { + Slog.w(TAG, "Null provider for invalidateAuthenticatorId"); + return; + } + provider.scheduleInvalidateAuthenticatorId(sensorId, userId, callback); + } + @Override // Binder call public long getAuthenticatorId(int sensorId, int userId) { Utils.checkPermission(getContext(), USE_BIOMETRIC_INTERNAL); diff --git a/services/core/java/com/android/server/biometrics/sensors/face/ServiceProvider.java b/services/core/java/com/android/server/biometrics/sensors/face/ServiceProvider.java index be4e2482acc9..51b427d772a1 100644 --- a/services/core/java/com/android/server/biometrics/sensors/face/ServiceProvider.java +++ b/services/core/java/com/android/server/biometrics/sensors/face/ServiceProvider.java @@ -18,6 +18,7 @@ package com.android.server.biometrics.sensors.face; import android.annotation.NonNull; import android.annotation.Nullable; +import android.hardware.biometrics.IInvalidationCallback; import android.hardware.biometrics.ITestSession; import android.hardware.face.Face; import android.hardware.face.FaceManager; @@ -71,6 +72,16 @@ public interface ServiceProvider { @LockoutTracker.LockoutMode int getLockoutModeForUser(int sensorId, int userId); + /** + * Requests for the authenticatorId (whose source of truth is in the TEE or equivalent) to + * be invalidated. See {@link com.android.server.biometrics.sensors.InvalidationRequesterClient} + */ + default void scheduleInvalidateAuthenticatorId(int sensorId, int userId, + @NonNull IInvalidationCallback callback) { + throw new IllegalStateException("Providers that support invalidation must override" + + " this method"); + } + long getAuthenticatorId(int sensorId, int userId); boolean isHardwareDetected(int sensorId); diff --git a/services/core/java/com/android/server/biometrics/sensors/face/aidl/Sensor.java b/services/core/java/com/android/server/biometrics/sensors/face/aidl/Sensor.java index d9d473722fd5..f9e31063e595 100644 --- a/services/core/java/com/android/server/biometrics/sensors/face/aidl/Sensor.java +++ b/services/core/java/com/android/server/biometrics/sensors/face/aidl/Sensor.java @@ -375,7 +375,7 @@ public class Sensor implements IBinder.DeathRecipient { } @Override - public void onAuthenticatorIdInvalidated() { + public void onAuthenticatorIdInvalidated(long newAuthenticatorId) { // TODO(b/159667191) } @@ -460,6 +460,7 @@ public class Sensor implements IBinder.DeathRecipient { final long sensorToken = proto.start(SensorServiceStateProto.SENSOR_STATES); proto.write(SensorStateProto.SENSOR_ID, mSensorProperties.sensorId); + proto.write(SensorStateProto.MODALITY, SensorStateProto.FACE); proto.write(SensorStateProto.IS_BUSY, mScheduler.getCurrentClient() != null); for (UserInfo user : UserManager.get(mContext).getUsers()) { diff --git a/services/core/java/com/android/server/biometrics/sensors/face/hidl/Face10.java b/services/core/java/com/android/server/biometrics/sensors/face/hidl/Face10.java index c4e4d1fe0f82..10b12cb22e85 100644 --- a/services/core/java/com/android/server/biometrics/sensors/face/hidl/Face10.java +++ b/services/core/java/com/android/server/biometrics/sensors/face/hidl/Face10.java @@ -770,6 +770,7 @@ public class Face10 implements IHwBinder.DeathRecipient, ServiceProvider { final long sensorToken = proto.start(SensorServiceStateProto.SENSOR_STATES); proto.write(SensorStateProto.SENSOR_ID, mSensorProperties.sensorId); + proto.write(SensorStateProto.MODALITY, SensorStateProto.FACE); proto.write(SensorStateProto.IS_BUSY, mScheduler.getCurrentClient() != null); for (UserInfo user : UserManager.get(mContext).getUsers()) { diff --git a/services/core/java/com/android/server/biometrics/sensors/fingerprint/FingerprintAuthenticator.java b/services/core/java/com/android/server/biometrics/sensors/fingerprint/FingerprintAuthenticator.java index d4cdc8b18898..312a3ba80d69 100644 --- a/services/core/java/com/android/server/biometrics/sensors/fingerprint/FingerprintAuthenticator.java +++ b/services/core/java/com/android/server/biometrics/sensors/fingerprint/FingerprintAuthenticator.java @@ -19,13 +19,13 @@ package com.android.server.biometrics.sensors.fingerprint; import android.annotation.NonNull; import android.hardware.biometrics.IBiometricAuthenticator; import android.hardware.biometrics.IBiometricSensorReceiver; +import android.hardware.biometrics.IInvalidationCallback; import android.hardware.biometrics.ITestSession; import android.hardware.biometrics.SensorPropertiesInternal; import android.hardware.fingerprint.IFingerprintService; import android.os.IBinder; import android.os.RemoteException; -import com.android.server.biometrics.SensorConfig; import com.android.server.biometrics.sensors.LockoutTracker; /** @@ -94,6 +94,12 @@ public final class FingerprintAuthenticator extends IBiometricAuthenticator.Stub } @Override + public void invalidateAuthenticatorId(int userId, IInvalidationCallback callback) + throws RemoteException { + mFingerprintService.invalidateAuthenticatorId(mSensorId, userId, callback); + } + + @Override public long getAuthenticatorId(int callingUserId) throws RemoteException { return mFingerprintService.getAuthenticatorId(mSensorId, callingUserId); } diff --git a/services/core/java/com/android/server/biometrics/sensors/fingerprint/FingerprintService.java b/services/core/java/com/android/server/biometrics/sensors/fingerprint/FingerprintService.java index e6cdbd28f9d1..d541eb3bd176 100644 --- a/services/core/java/com/android/server/biometrics/sensors/fingerprint/FingerprintService.java +++ b/services/core/java/com/android/server/biometrics/sensors/fingerprint/FingerprintService.java @@ -36,6 +36,7 @@ import android.hardware.biometrics.BiometricsProtoEnums; import android.hardware.biometrics.IBiometricSensorReceiver; import android.hardware.biometrics.IBiometricService; import android.hardware.biometrics.IBiometricServiceLockoutResetCallback; +import android.hardware.biometrics.IInvalidationCallback; import android.hardware.biometrics.ITestSession; import android.hardware.biometrics.fingerprint.IFingerprint; import android.hardware.biometrics.fingerprint.SensorProps; @@ -571,6 +572,19 @@ public class FingerprintService extends SystemService implements BiometricServic return provider.getLockoutModeForUser(sensorId, userId); } + @Override + public void invalidateAuthenticatorId(int sensorId, int userId, + IInvalidationCallback callback) { + Utils.checkPermission(getContext(), USE_BIOMETRIC_INTERNAL); + + final ServiceProvider provider = getProviderForSensor(sensorId); + if (provider == null) { + Slog.w(TAG, "Null provider for invalidateAuthenticatorId"); + return; + } + provider.scheduleInvalidateAuthenticatorId(sensorId, userId, callback); + } + @Override // Binder call public long getAuthenticatorId(int sensorId, int userId) { Utils.checkPermission(getContext(), USE_BIOMETRIC_INTERNAL); diff --git a/services/core/java/com/android/server/biometrics/sensors/fingerprint/ServiceProvider.java b/services/core/java/com/android/server/biometrics/sensors/fingerprint/ServiceProvider.java index d94c98481eeb..272e2b277941 100644 --- a/services/core/java/com/android/server/biometrics/sensors/fingerprint/ServiceProvider.java +++ b/services/core/java/com/android/server/biometrics/sensors/fingerprint/ServiceProvider.java @@ -18,6 +18,7 @@ package com.android.server.biometrics.sensors.fingerprint; import android.annotation.NonNull; import android.annotation.Nullable; +import android.hardware.biometrics.IInvalidationCallback; import android.hardware.biometrics.ITestSession; import android.hardware.fingerprint.Fingerprint; import android.hardware.fingerprint.FingerprintManager; @@ -109,6 +110,16 @@ public interface ServiceProvider { @LockoutTracker.LockoutMode int getLockoutModeForUser(int sensorId, int userId); + /** + * Requests for the authenticatorId (whose source of truth is in the TEE or equivalent) to + * be invalidated. See {@link com.android.server.biometrics.sensors.InvalidationRequesterClient} + */ + default void scheduleInvalidateAuthenticatorId(int sensorId, int userId, + @NonNull IInvalidationCallback callback) { + throw new IllegalStateException("Providers that support invalidation must override" + + " this method"); + } + long getAuthenticatorId(int sensorId, int userId); void onPointerDown(int sensorId, int x, int y, float minor, float major); diff --git a/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/Sensor.java b/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/Sensor.java index ecb998594d44..bb0f9830603c 100644 --- a/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/Sensor.java +++ b/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/Sensor.java @@ -388,7 +388,7 @@ class Sensor implements IBinder.DeathRecipient { } @Override - public void onAuthenticatorIdInvalidated() { + public void onAuthenticatorIdInvalidated(long newAuthenticatorId) { // TODO(159667191) } } @@ -473,6 +473,7 @@ class Sensor implements IBinder.DeathRecipient { final long sensorToken = proto.start(SensorServiceStateProto.SENSOR_STATES); proto.write(SensorStateProto.SENSOR_ID, mSensorProperties.sensorId); + proto.write(SensorStateProto.MODALITY, SensorStateProto.FINGERPRINT); proto.write(SensorStateProto.IS_BUSY, mScheduler.getCurrentClient() != null); for (UserInfo user : UserManager.get(mContext).getUsers()) { diff --git a/services/core/java/com/android/server/biometrics/sensors/fingerprint/hidl/Fingerprint21.java b/services/core/java/com/android/server/biometrics/sensors/fingerprint/hidl/Fingerprint21.java index f5ce8943c188..7989e6e43d7e 100644 --- a/services/core/java/com/android/server/biometrics/sensors/fingerprint/hidl/Fingerprint21.java +++ b/services/core/java/com/android/server/biometrics/sensors/fingerprint/hidl/Fingerprint21.java @@ -716,6 +716,7 @@ public class Fingerprint21 implements IHwBinder.DeathRecipient, ServiceProvider final long sensorToken = proto.start(SensorServiceStateProto.SENSOR_STATES); proto.write(SensorStateProto.SENSOR_ID, mSensorProperties.sensorId); + proto.write(SensorStateProto.MODALITY, SensorStateProto.FINGERPRINT); proto.write(SensorStateProto.IS_BUSY, mScheduler.getCurrentClient() != null); for (UserInfo user : UserManager.get(mContext).getUsers()) { diff --git a/services/core/java/com/android/server/biometrics/sensors/iris/IrisAuthenticator.java b/services/core/java/com/android/server/biometrics/sensors/iris/IrisAuthenticator.java index 6f437a7c31d2..3e5b88cca1e4 100644 --- a/services/core/java/com/android/server/biometrics/sensors/iris/IrisAuthenticator.java +++ b/services/core/java/com/android/server/biometrics/sensors/iris/IrisAuthenticator.java @@ -19,13 +19,13 @@ package com.android.server.biometrics.sensors.iris; import android.annotation.NonNull; import android.hardware.biometrics.IBiometricAuthenticator; import android.hardware.biometrics.IBiometricSensorReceiver; +import android.hardware.biometrics.IInvalidationCallback; import android.hardware.biometrics.ITestSession; import android.hardware.biometrics.SensorPropertiesInternal; import android.hardware.iris.IIrisService; import android.os.IBinder; import android.os.RemoteException; -import com.android.server.biometrics.SensorConfig; import com.android.server.biometrics.sensors.LockoutTracker; /** @@ -86,6 +86,10 @@ public final class IrisAuthenticator extends IBiometricAuthenticator.Stub { } @Override + public void invalidateAuthenticatorId(int userId, IInvalidationCallback callback) { + } + + @Override public long getAuthenticatorId(int callingUserId) throws RemoteException { return 0; } diff --git a/services/core/java/com/android/server/compat/CompatChange.java b/services/core/java/com/android/server/compat/CompatChange.java index 18907a19f96d..9ba957ef27ae 100644 --- a/services/core/java/com/android/server/compat/CompatChange.java +++ b/services/core/java/com/android/server/compat/CompatChange.java @@ -64,7 +64,7 @@ public final class CompatChange extends CompatibilityChangeInfo { private Map<String, Boolean> mDeferredOverrides; public CompatChange(long changeId) { - this(changeId, null, -1, -1, false, false, null); + this(changeId, null, -1, -1, false, false, null, false); } /** @@ -77,9 +77,10 @@ public final class CompatChange extends CompatibilityChangeInfo { * @param disabled If {@code true}, overrides any {@code enableAfterTargetSdk} set. */ public CompatChange(long changeId, @Nullable String name, int enableAfterTargetSdk, - int enableSinceTargetSdk, boolean disabled, boolean loggingOnly, String description) { + int enableSinceTargetSdk, boolean disabled, boolean loggingOnly, String description, + boolean overridable) { super(changeId, name, enableAfterTargetSdk, enableSinceTargetSdk, disabled, loggingOnly, - description); + description, overridable); } /** @@ -88,7 +89,7 @@ public final class CompatChange extends CompatibilityChangeInfo { public CompatChange(Change change) { super(change.getId(), change.getName(), change.getEnableAfterTargetSdk(), change.getEnableSinceTargetSdk(), change.getDisabled(), change.getLoggingOnly(), - change.getDescription()); + change.getDescription(), change.getOverridable()); } void registerListener(ChangeListener listener) { @@ -274,6 +275,9 @@ public final class CompatChange extends CompatibilityChangeInfo { if (mDeferredOverrides != null && mDeferredOverrides.size() > 0) { sb.append("; deferredOverrides=").append(mDeferredOverrides); } + if (getOverridable()) { + sb.append("; overridable"); + } return sb.append(")").toString(); } diff --git a/services/core/java/com/android/server/connectivity/DefaultNetworkMetrics.java b/services/core/java/com/android/server/connectivity/DefaultNetworkMetrics.java index 995bb2422de2..8cd1fd6f2b64 100644 --- a/services/core/java/com/android/server/connectivity/DefaultNetworkMetrics.java +++ b/services/core/java/com/android/server/connectivity/DefaultNetworkMetrics.java @@ -17,6 +17,8 @@ package com.android.server.connectivity; import android.net.LinkProperties; +import android.net.Network; +import android.net.NetworkCapabilities; import android.net.metrics.DefaultNetworkEvent; import android.os.SystemClock; @@ -61,7 +63,7 @@ public class DefaultNetworkMetrics { private int mLastTransports; public DefaultNetworkMetrics() { - newDefaultNetwork(creationTimeMs, null); + newDefaultNetwork(creationTimeMs, null, 0, false, null, null); } public synchronized void listEvents(PrintWriter pw) { @@ -117,13 +119,21 @@ public class DefaultNetworkMetrics { mCurrentDefaultNetwork.validatedMs += timeMs - mLastValidationTimeMs; } - public synchronized void logDefaultNetworkEvent( - long timeMs, NetworkAgentInfo newNai, NetworkAgentInfo oldNai) { - logCurrentDefaultNetwork(timeMs, oldNai); - newDefaultNetwork(timeMs, newNai); + /** + * Logs a default network event. + * @see {IpConnectivityLog#logDefaultNetworkEvent}. + */ + public synchronized void logDefaultNetworkEvent(long timeMs, Network defaultNetwork, int score, + boolean validated, LinkProperties lp, NetworkCapabilities nc, + Network previousDefaultNetwork, int previousScore, LinkProperties previousLp, + NetworkCapabilities previousNc) { + logCurrentDefaultNetwork(timeMs, previousDefaultNetwork, previousScore, previousLp, + previousNc); + newDefaultNetwork(timeMs, defaultNetwork, score, validated, lp, nc); } - private void logCurrentDefaultNetwork(long timeMs, NetworkAgentInfo oldNai) { + private void logCurrentDefaultNetwork(long timeMs, Network network, int score, + LinkProperties lp, NetworkCapabilities nc) { if (mIsCurrentlyValid) { updateValidationTime(timeMs); } @@ -131,10 +141,10 @@ public class DefaultNetworkMetrics { ev.updateDuration(timeMs); ev.previousTransports = mLastTransports; // oldNai is null if the system had no default network before the transition. - if (oldNai != null) { + if (network != null) { // The system acquired a new default network. - fillLinkInfo(ev, oldNai); - ev.finalScore = oldNai.getCurrentScore(); + fillLinkInfo(ev, network, lp, nc); + ev.finalScore = score; } // Only change transport of the previous default network if the event currently logged // corresponds to an existing default network, and not to the absence of a default network. @@ -147,14 +157,15 @@ public class DefaultNetworkMetrics { mEventsLog.append(ev); } - private void newDefaultNetwork(long timeMs, NetworkAgentInfo newNai) { + private void newDefaultNetwork(long timeMs, Network network, int score, boolean validated, + LinkProperties lp, NetworkCapabilities nc) { DefaultNetworkEvent ev = new DefaultNetworkEvent(timeMs); ev.durationMs = timeMs; // newNai is null if the system has no default network after the transition. - if (newNai != null) { - fillLinkInfo(ev, newNai); - ev.initialScore = newNai.getCurrentScore(); - if (newNai.lastValidated) { + if (network != null) { + fillLinkInfo(ev, network, lp, nc); + ev.initialScore = score; + if (validated) { mIsCurrentlyValid = true; mLastValidationTimeMs = timeMs; } @@ -164,10 +175,10 @@ public class DefaultNetworkMetrics { mCurrentDefaultNetwork = ev; } - private static void fillLinkInfo(DefaultNetworkEvent ev, NetworkAgentInfo nai) { - LinkProperties lp = nai.linkProperties; - ev.netId = nai.network().getNetId(); - ev.transports |= BitUtils.packBits(nai.networkCapabilities.getTransportTypes()); + private static void fillLinkInfo(DefaultNetworkEvent ev, Network network, LinkProperties lp, + NetworkCapabilities nc) { + ev.netId = network.getNetId(); + ev.transports |= BitUtils.packBits(nc.getTransportTypes()); ev.ipv4 |= lp.hasIpv4Address() && lp.hasIpv4DefaultRoute(); ev.ipv6 |= lp.hasGlobalIpv6Address() && lp.hasIpv6DefaultRoute(); } diff --git a/services/core/java/com/android/server/connectivity/IpConnectivityMetrics.java b/services/core/java/com/android/server/connectivity/IpConnectivityMetrics.java index b5d875d5c162..1024556c17eb 100644 --- a/services/core/java/com/android/server/connectivity/IpConnectivityMetrics.java +++ b/services/core/java/com/android/server/connectivity/IpConnectivityMetrics.java @@ -20,11 +20,15 @@ import android.content.Context; import android.net.ConnectivityMetricsEvent; import android.net.IIpConnectivityMetrics; import android.net.INetdEventCallback; +import android.net.LinkProperties; +import android.net.Network; +import android.net.NetworkCapabilities; import android.net.NetworkStack; import android.net.metrics.ApfProgramEvent; import android.net.metrics.IpConnectivityLog; import android.os.Binder; import android.os.Process; +import android.os.SystemClock; import android.provider.Settings; import android.text.TextUtils; import android.text.format.DateUtils; @@ -122,8 +126,6 @@ final public class IpConnectivityMetrics extends SystemService { public IpConnectivityMetrics(Context ctx, ToIntFunction<Context> capacityGetter) { super(ctx); - // Load JNI libraries used by the IpConnectivityMetrics service and its dependencies - System.loadLibrary("service-connectivity"); mCapacityGetter = capacityGetter; initBuffer(); } @@ -363,6 +365,21 @@ final public class IpConnectivityMetrics extends SystemService { } return mNetdListener.removeNetdEventCallback(callerType); } + + @Override + public void logDefaultNetworkValidity(boolean valid) { + mDefaultNetworkMetrics.logDefaultNetworkValidity(SystemClock.elapsedRealtime(), valid); + } + + @Override + public void logDefaultNetworkEvent(Network defaultNetwork, int score, boolean validated, + LinkProperties lp, NetworkCapabilities nc, Network previousDefaultNetwork, + int previousScore, LinkProperties previousLp, NetworkCapabilities previousNc) { + final long timeMs = SystemClock.elapsedRealtime(); + mDefaultNetworkMetrics.logDefaultNetworkEvent(timeMs, defaultNetwork, score, validated, + lp, nc, previousDefaultNetwork, previousScore, previousLp, previousNc); + } + }; private static final ToIntFunction<Context> READ_BUFFER_SIZE = (ctx) -> { diff --git a/services/core/java/com/android/server/connectivity/NetworkAgentInfo.java b/services/core/java/com/android/server/connectivity/NetworkAgentInfo.java index 55d8279a92d0..b0a73f105725 100644 --- a/services/core/java/com/android/server/connectivity/NetworkAgentInfo.java +++ b/services/core/java/com/android/server/connectivity/NetworkAgentInfo.java @@ -189,13 +189,18 @@ public class NetworkAgentInfo implements Comparable<NetworkAgentInfo> { // Set to true when partial connectivity was detected. public boolean partialConnectivity; - // Captive portal info of the network, if any. + // Captive portal info of the network from RFC8908, if any. // Obtained by ConnectivityService and merged into NetworkAgent-provided information. - public CaptivePortalData captivePortalData; + public CaptivePortalData capportApiData; // The UID of the remote entity that created this Network. public final int creatorUid; + // Network agent portal info of the network, if any. This information is provided from + // non-RFC8908 sources, such as Wi-Fi Passpoint, which can provide information such as Venue + // URL, Terms & Conditions URL, and network friendly name. + public CaptivePortalData networkAgentPortalData; + // Networks are lingered when they become unneeded as a result of their NetworkRequests being // satisfied by a higher-scoring network. so as to allow communication to wrap up before the // network is taken down. This usually only happens to the default network. Lingering ends with diff --git a/services/core/java/com/android/server/display/DisplayModeDirector.java b/services/core/java/com/android/server/display/DisplayModeDirector.java index 95918945eecd..006f87571f82 100644 --- a/services/core/java/com/android/server/display/DisplayModeDirector.java +++ b/services/core/java/com/android/server/display/DisplayModeDirector.java @@ -52,9 +52,12 @@ import com.android.server.display.utils.AmbientFilterFactory; import com.android.server.utils.DeviceConfigInterface; import java.io.PrintWriter; +import java.text.SimpleDateFormat; import java.util.ArrayList; import java.util.Arrays; +import java.util.Date; import java.util.List; +import java.util.Locale; import java.util.Objects; @@ -1299,7 +1302,8 @@ public class DisplayModeDirector { // mShouldObserveAmbientHighChange is true, screen is on, peak refresh rate // changeable and low power mode off. After initialization, these states will // be updated from the same handler thread. - private boolean mDefaultDisplayOn = false; + private int mDefaultDisplayState = Display.STATE_UNKNOWN; + private boolean mIsDeviceActive = false; private boolean mRefreshRateChangeable = false; private boolean mLowPowerModeEnabled = false; @@ -1480,7 +1484,8 @@ public class DisplayModeDirector { pw.println(" BrightnessObserver"); pw.println(" mAmbientLux: " + mAmbientLux); pw.println(" mBrightness: " + mBrightness); - pw.println(" mDefaultDisplayOn: " + mDefaultDisplayOn); + pw.println(" mDefaultDisplayState: " + mDefaultDisplayState); + pw.println(" mIsDeviceActive: " + mIsDeviceActive); pw.println(" mLowPowerModeEnabled: " + mLowPowerModeEnabled); pw.println(" mRefreshRateChangeable: " + mRefreshRateChangeable); pw.println(" mShouldObserveDisplayLowChange: " + mShouldObserveDisplayLowChange); @@ -1706,14 +1711,17 @@ public class DisplayModeDirector { private void updateDefaultDisplayState() { Display display = mContext.getSystemService(DisplayManager.class) .getDisplay(Display.DEFAULT_DISPLAY); - boolean defaultDisplayOn = display != null && display.getState() != Display.STATE_OFF; - setDefaultDisplayState(defaultDisplayOn); + if (display == null) { + return; + } + + setDefaultDisplayState(display.getState()); } @VisibleForTesting - public void setDefaultDisplayState(boolean on) { - if (mDefaultDisplayOn != on) { - mDefaultDisplayOn = on; + public void setDefaultDisplayState(int state) { + if (mDefaultDisplayState != state) { + mDefaultDisplayState = state; updateSensorStatus(); } } @@ -1734,15 +1742,19 @@ public class DisplayModeDirector { } private boolean isDeviceActive() { - return mDefaultDisplayOn && mInjector.isDeviceInteractive(mContext); + mIsDeviceActive = mInjector.isDeviceInteractive(mContext); + return (mDefaultDisplayState == Display.STATE_ON) + && mIsDeviceActive; } private final class LightSensorEventListener implements SensorEventListener { final private static int INJECT_EVENTS_INTERVAL_MS = LIGHT_SENSOR_RATE_MS; private float mLastSensorData; + private long mTimestamp; public void dumpLocked(PrintWriter pw) { pw.println(" mLastSensorData: " + mLastSensorData); + pw.println(" mTimestamp: " + formatTimestamp(mTimestamp)); } @Override @@ -1766,6 +1778,7 @@ public class DisplayModeDirector { } long now = SystemClock.uptimeMillis(); + mTimestamp = System.currentTimeMillis(); if (mAmbientFilter != null) { mAmbientFilter.addValue(now, mLastSensorData); } @@ -1792,6 +1805,12 @@ public class DisplayModeDirector { mHandler.removeCallbacks(mInjectSensorEventRunnable); } + private String formatTimestamp(long time) { + SimpleDateFormat dateFormat = + new SimpleDateFormat("yyyy-MM-dd HH:mm:ss.SSS", Locale.US); + return dateFormat.format(new Date(time)); + } + private void processSensorData(long now) { if (mAmbientFilter != null) { mAmbientLux = mAmbientFilter.getEstimate(now); diff --git a/services/core/java/com/android/server/location/LocationManagerService.java b/services/core/java/com/android/server/location/LocationManagerService.java index 7ec4a5adb33e..e8052289cca4 100644 --- a/services/core/java/com/android/server/location/LocationManagerService.java +++ b/services/core/java/com/android/server/location/LocationManagerService.java @@ -1179,7 +1179,7 @@ public class LocationManagerService extends ILocationManager.Stub { @Override public int handleShellCommand(ParcelFileDescriptor in, ParcelFileDescriptor out, ParcelFileDescriptor err, String[] args) { - return new LocationShellCommand(this).exec( + return new LocationShellCommand(mContext, this).exec( this, in.getFileDescriptor(), out.getFileDescriptor(), err.getFileDescriptor(), args); } diff --git a/services/core/java/com/android/server/location/LocationShellCommand.java b/services/core/java/com/android/server/location/LocationShellCommand.java index 7d3ccbf5a1b4..0fe66e0b4ea1 100644 --- a/services/core/java/com/android/server/location/LocationShellCommand.java +++ b/services/core/java/com/android/server/location/LocationShellCommand.java @@ -16,21 +16,29 @@ package com.android.server.location; +import android.content.Context; +import android.location.Criteria; +import android.location.Location; +import android.location.provider.ProviderProperties; import android.os.UserHandle; import com.android.modules.utils.BasicShellCommandHandler; import java.io.PrintWriter; +import java.util.Arrays; import java.util.Objects; /** * Interprets and executes 'adb shell cmd location [args]'. */ class LocationShellCommand extends BasicShellCommandHandler { + private static final float DEFAULT_TEST_LOCATION_ACCURACY = 100.0f; + private final Context mContext; private final LocationManagerService mService; - LocationShellCommand(LocationManagerService service) { + LocationShellCommand(Context context, LocationManagerService service) { + mContext = context; mService = Objects.requireNonNull(service); } @@ -47,6 +55,44 @@ class LocationShellCommand extends BasicShellCommandHandler { mService.setLocationEnabledForUser(enabled, userId); return 0; } + case "providers": { + String command = getNextArgRequired(); + return parseProvidersCommand(command); + } + default: + return handleDefaultCommands(cmd); + } + } + + private int parseProvidersCommand(String cmd) { + switch (cmd) { + case "add-test-provider": { + String provider = getNextArgRequired(); + ProviderProperties properties = parseTestProviderProviderProperties(); + mService.addTestProvider(provider, properties, mContext.getOpPackageName(), + mContext.getFeatureId()); + return 0; + } + case "remove-test-provider": { + String provider = getNextArgRequired(); + mService.removeTestProvider(provider, mContext.getOpPackageName(), + mContext.getFeatureId()); + return 0; + } + case "set-test-provider-enabled": { + String provider = getNextArgRequired(); + boolean enabled = Boolean.parseBoolean(getNextArgRequired()); + mService.setTestProviderEnabled(provider, enabled, mContext.getOpPackageName(), + mContext.getFeatureId()); + return 0; + } + case "set-test-provider-location": { + String provider = getNextArgRequired(); + Location location = parseTestProviderLocation(provider); + mService.setTestProviderLocation(provider, location, mContext.getOpPackageName(), + mContext.getFeatureId()); + return 0; + } case "send-extra-command": { String provider = getNextArgRequired(); String command = getNextArgRequired(); @@ -72,6 +118,120 @@ class LocationShellCommand extends BasicShellCommandHandler { return UserHandle.USER_CURRENT_OR_SELF; } + private ProviderProperties parseTestProviderProviderProperties() { + boolean requiresNetwork = false; + boolean requiresSatellite = false; + boolean requiresCell = false; + boolean hasMonetaryCost = false; + boolean supportsAltitude = false; + boolean supportsSpeed = false; + boolean supportsBearing = false; + int powerRequirement = Criteria.POWER_LOW; + int accuracy = Criteria.ACCURACY_FINE; + + String option = getNextOption(); + while (option != null) { + switch (option) { + case "--requiresNetwork": { + requiresNetwork = true; + break; + } + case "--requiresSatellite": { + requiresSatellite = true; + break; + } + case "--requiresCell": { + requiresCell = true; + break; + } + case "--hasMonetaryCost": { + hasMonetaryCost = true; + break; + } + case "--supportsAltitude": { + supportsAltitude = true; + break; + } + case "--supportsSpeed": { + supportsSpeed = true; + break; + } + case "--supportsBearing": { + supportsBearing = true; + break; + } + case "--powerRequirement": { + powerRequirement = Integer.parseInt(getNextArgRequired()); + break; + } + case "--accuracy": { + accuracy = Integer.parseInt(getNextArgRequired()); + break; + } + default: + throw new IllegalArgumentException( + "Received unexpected option: " + option); + } + option = getNextOption(); + } + + ProviderProperties properties = new ProviderProperties.Builder() + .setHasNetworkRequirement(requiresNetwork) + .setHasSatelliteRequirement(requiresSatellite) + .setHasCellRequirement(requiresCell) + .setHasMonetaryCost(hasMonetaryCost) + .setHasAltitudeSupport(supportsAltitude) + .setHasSpeedSupport(supportsSpeed) + .setHasBearingSupport(supportsBearing) + .setPowerUsage(powerRequirement) + .setAccuracy(accuracy) + .build(); + + return properties; + } + + private Location parseTestProviderLocation(String provider) { + boolean hasLatitude = false; + boolean hasLongitude = false; + + Location location = new Location(provider); + location.setAccuracy(DEFAULT_TEST_LOCATION_ACCURACY); + location.setTime(System.currentTimeMillis()); + + String option = getNextOption(); + while (option != null) { + switch (option) { + case "--location": { + String[] locationInput = getNextArgRequired().split(","); + if (locationInput.length != 2) { + throw new IllegalArgumentException( + "Unexpected location format: " + Arrays.toString(locationInput)); + } + + location.setLatitude(Double.parseDouble(locationInput[0])); + location.setLongitude(Double.parseDouble(locationInput[1])); + break; + } + case "--accuracy": { + location.setAccuracy(Float.parseFloat(getNextArgRequired())); + break; + } + case "--time": { + location.setTime(Long.parseLong(getNextArgRequired())); + break; + } + default: + throw new IllegalArgumentException( + "Received unexpected option: " + option); + } + option = getNextOption(); + } + + location.setElapsedRealtimeNanos(System.nanoTime()); + + return location; + } + @Override public void onHelp() { PrintWriter pw = getOutPrintWriter(); @@ -80,15 +240,32 @@ class LocationShellCommand extends BasicShellCommandHandler { pw.println(" Print this help text."); pw.println(" set-location-enabled [--user <USER_ID>] true|false"); pw.println(" Sets the master location switch enabled state."); - pw.println(" send-extra-command <PROVIDER> <COMMAND>"); - pw.println(" Sends the given extra command to the given provider."); + pw.println(" providers"); + pw.println(" add-test-provider <PROVIDER> [--requiresNetwork] [--requiresSatellite]"); + pw.println(" [--requiresCell] [--hasMonetaryCost] [--supportsAltitude]"); + pw.println(" [--supportsSpeed] [--supportsBearing]"); + pw.println(" [--powerRequirement <POWER_REQUIREMENT>]"); + pw.println(" Add the given test provider. Requires MOCK_LOCATION permissions which"); + pw.println(" can be enabled by running \"adb shell appops set <uid>"); + pw.println(" android:mock_location allow\". There are optional flags that can be"); + pw.println(" used to configure the provider properties. If no flags are included,"); + pw.println(" then default values will be used."); + pw.println(" remove-test-provider <PROVIDER>"); + pw.println(" Remove the given test provider."); + pw.println(" set-test-provider-enabled <PROVIDER> true|false"); + pw.println(" Sets the given test provider enabled state."); + pw.println(" set-test-provider-location <PROVIDER> [--location <LATITUDE>,<LONGITUDE>]"); + pw.println(" [--accuracy <ACCURACY>] [--time <TIME>]"); + pw.println(" Set location for given test provider. Accuracy and time are optional."); + pw.println(" send-extra-command <PROVIDER> <COMMAND>"); + pw.println(" Sends the given extra command to the given provider."); pw.println(); - pw.println(" Common commands that may be supported by the gps provider, depending on"); - pw.println(" hardware and software configurations:"); - pw.println(" delete_aiding_data - requests deletion of any predictive aiding data"); - pw.println(" force_time_injection - requests NTP time injection to chipset"); - pw.println(" force_psds_injection - " + pw.println(" Common commands that may be supported by the gps provider, depending on"); + pw.println(" hardware and software configurations:"); + pw.println(" delete_aiding_data - requests deletion of any predictive aiding data"); + pw.println(" force_time_injection - requests NTP time injection to chipset"); + pw.println(" force_psds_injection - " + "requests predictive aiding data injection to chipset"); - pw.println(" request_power_stats - requests GNSS power stats update from chipset"); + pw.println(" request_power_stats - requests GNSS power stats update from chipset"); } } diff --git a/services/core/java/com/android/server/net/NetworkPolicyManagerService.java b/services/core/java/com/android/server/net/NetworkPolicyManagerService.java index 7c17356fa3cc..5ed7a9650f6c 100644 --- a/services/core/java/com/android/server/net/NetworkPolicyManagerService.java +++ b/services/core/java/com/android/server/net/NetworkPolicyManagerService.java @@ -3862,6 +3862,13 @@ public class NetworkPolicyManagerService extends INetworkPolicyManager.Stub { } } + @VisibleForTesting + boolean isRestrictedModeEnabled() { + synchronized (mUidRulesFirstLock) { + return mRestrictedNetworkingMode; + } + } + /** * updates restricted mode state / access for all apps * Called on initialization and when restricted mode is enabled / disabled. @@ -4489,26 +4496,26 @@ public class NetworkPolicyManagerService extends INetworkPolicyManager.Stub { final boolean isDenied = (uidPolicy & POLICY_REJECT_METERED_BACKGROUND) != 0; final boolean isAllowed = (uidPolicy & POLICY_ALLOW_METERED_BACKGROUND) != 0; - final int oldRule = oldUidRules & MASK_METERED_NETWORKS; - int newRule = RULE_NONE; + + // copy oldUidRules and clear out METERED_NETWORKS rules. + int newUidRules = oldUidRules & (~MASK_METERED_NETWORKS); // First step: define the new rule based on user restrictions and foreground state. if (isRestrictedByAdmin) { - newRule = RULE_REJECT_METERED; + newUidRules |= RULE_REJECT_METERED; } else if (isForeground) { if (isDenied || (mRestrictBackground && !isAllowed)) { - newRule = RULE_TEMPORARY_ALLOW_METERED; + newUidRules |= RULE_TEMPORARY_ALLOW_METERED; } else if (isAllowed) { - newRule = RULE_ALLOW_METERED; + newUidRules |= RULE_ALLOW_METERED; } } else { if (isDenied) { - newRule = RULE_REJECT_METERED; + newUidRules |= RULE_REJECT_METERED; } else if (mRestrictBackground && isAllowed) { - newRule = RULE_ALLOW_METERED; + newUidRules |= RULE_ALLOW_METERED; } } - final int newUidRules = newRule | (oldUidRules & MASK_ALL_NETWORKS); if (LOGV) { Log.v(TAG, "updateRuleForRestrictBackgroundUL(" + uid + ")" @@ -4516,8 +4523,8 @@ public class NetworkPolicyManagerService extends INetworkPolicyManager.Stub { + ", isDenied=" + isDenied + ", isAllowed=" + isAllowed + ", isRestrictedByAdmin=" + isRestrictedByAdmin - + ", oldRule=" + uidRulesToString(oldRule) - + ", newRule=" + uidRulesToString(newRule) + + ", oldRule=" + uidRulesToString(oldUidRules & MASK_METERED_NETWORKS) + + ", newRule=" + uidRulesToString(newUidRules & MASK_METERED_NETWORKS) + ", newUidRules=" + uidRulesToString(newUidRules) + ", oldUidRules=" + uidRulesToString(oldUidRules)); } @@ -4529,8 +4536,8 @@ public class NetworkPolicyManagerService extends INetworkPolicyManager.Stub { } // Second step: apply bw changes based on change of state. - if (newRule != oldRule) { - if (hasRule(newRule, RULE_TEMPORARY_ALLOW_METERED)) { + if (newUidRules != oldUidRules) { + if (hasRule(newUidRules, RULE_TEMPORARY_ALLOW_METERED)) { // Temporarily allow foreground app, removing from denylist if necessary // (since bw_penalty_box prevails over bw_happy_box). @@ -4541,7 +4548,7 @@ public class NetworkPolicyManagerService extends INetworkPolicyManager.Stub { if (isDenied) { setMeteredNetworkDenylist(uid, false); } - } else if (hasRule(oldRule, RULE_TEMPORARY_ALLOW_METERED)) { + } else if (hasRule(oldUidRules, RULE_TEMPORARY_ALLOW_METERED)) { // Remove temporary exemption from app that is not on foreground anymore. // TODO: if statements below are used to avoid unnecessary calls to netd / iptables, @@ -4554,18 +4561,18 @@ public class NetworkPolicyManagerService extends INetworkPolicyManager.Stub { if (isDenied || isRestrictedByAdmin) { setMeteredNetworkDenylist(uid, true); } - } else if (hasRule(newRule, RULE_REJECT_METERED) - || hasRule(oldRule, RULE_REJECT_METERED)) { + } else if (hasRule(newUidRules, RULE_REJECT_METERED) + || hasRule(oldUidRules, RULE_REJECT_METERED)) { // Flip state because app was explicitly added or removed to denylist. setMeteredNetworkDenylist(uid, (isDenied || isRestrictedByAdmin)); - if (hasRule(oldRule, RULE_REJECT_METERED) && isAllowed) { + if (hasRule(oldUidRules, RULE_REJECT_METERED) && isAllowed) { // Since denial prevails over allowance, we need to handle the special case // where app is allowed and denied at the same time (although such // scenario should be blocked by the UI), then it is removed from the denylist. setMeteredNetworkAllowlist(uid, isAllowed); } - } else if (hasRule(newRule, RULE_ALLOW_METERED) - || hasRule(oldRule, RULE_ALLOW_METERED)) { + } else if (hasRule(newUidRules, RULE_ALLOW_METERED) + || hasRule(oldUidRules, RULE_ALLOW_METERED)) { // Flip state because app was explicitly added or removed to allowlist. setMeteredNetworkAllowlist(uid, isAllowed); } else { @@ -4651,8 +4658,9 @@ public class NetworkPolicyManagerService extends INetworkPolicyManager.Stub { final boolean isForeground = isUidForegroundOnRestrictPowerUL(uid); final boolean isWhitelisted = isWhitelistedFromPowerSaveUL(uid, mDeviceIdleMode); - final int oldRule = oldUidRules & MASK_ALL_NETWORKS; - int newRule = RULE_NONE; + + // Copy existing uid rules and clear ALL_NETWORK rules. + int newUidRules = oldUidRules & (~MASK_ALL_NETWORKS); // First step: define the new rule based on user restrictions and foreground state. @@ -4660,14 +4668,12 @@ public class NetworkPolicyManagerService extends INetworkPolicyManager.Stub { // by considering the foreground and non-foreground states. if (isForeground) { if (restrictMode) { - newRule = RULE_ALLOW_ALL; + newUidRules |= RULE_ALLOW_ALL; } } else if (restrictMode) { - newRule = isWhitelisted ? RULE_ALLOW_ALL : RULE_REJECT_ALL; + newUidRules |= isWhitelisted ? RULE_ALLOW_ALL : RULE_REJECT_ALL; } - final int newUidRules = (oldUidRules & MASK_METERED_NETWORKS) | newRule; - if (LOGV) { Log.v(TAG, "updateRulesForPowerRestrictionsUL(" + uid + ")" + ", isIdle: " + isUidIdle @@ -4675,17 +4681,18 @@ public class NetworkPolicyManagerService extends INetworkPolicyManager.Stub { + ", mDeviceIdleMode: " + mDeviceIdleMode + ", isForeground=" + isForeground + ", isWhitelisted=" + isWhitelisted - + ", oldRule=" + uidRulesToString(oldRule) - + ", newRule=" + uidRulesToString(newRule) + + ", oldRule=" + uidRulesToString(oldUidRules & MASK_ALL_NETWORKS) + + ", newRule=" + uidRulesToString(newUidRules & MASK_ALL_NETWORKS) + ", newUidRules=" + uidRulesToString(newUidRules) + ", oldUidRules=" + uidRulesToString(oldUidRules)); } // Second step: notify listeners if state changed. - if (newRule != oldRule) { - if (newRule == RULE_NONE || hasRule(newRule, RULE_ALLOW_ALL)) { + if (newUidRules != oldUidRules) { + if ((newUidRules & MASK_ALL_NETWORKS) == RULE_NONE || hasRule(newUidRules, + RULE_ALLOW_ALL)) { if (LOGV) Log.v(TAG, "Allowing non-metered access for UID " + uid); - } else if (hasRule(newRule, RULE_REJECT_ALL)) { + } else if (hasRule(newUidRules, RULE_REJECT_ALL)) { if (LOGV) Log.v(TAG, "Rejecting non-metered access for UID " + uid); } else { // All scenarios should have been covered above diff --git a/services/core/java/com/android/server/net/NetworkPolicyManagerShellCommand.java b/services/core/java/com/android/server/net/NetworkPolicyManagerShellCommand.java index 7bcf3183bf69..47bb8f009920 100644 --- a/services/core/java/com/android/server/net/NetworkPolicyManagerShellCommand.java +++ b/services/core/java/com/android/server/net/NetworkPolicyManagerShellCommand.java @@ -119,6 +119,8 @@ class NetworkPolicyManagerShellCommand extends ShellCommand { switch(type) { case "restrict-background": return getRestrictBackground(); + case "restricted-mode": + return getRestrictedModeState(); } pw.println("Error: unknown get type '" + type + "'"); return -1; @@ -255,6 +257,13 @@ class NetworkPolicyManagerShellCommand extends ShellCommand { return listUidList("App Idle whitelisted UIDs", uids); } + private int getRestrictedModeState() { + final PrintWriter pw = getOutPrintWriter(); + pw.print("Restricted mode status: "); + pw.println(mInterface.isRestrictedModeEnabled() ? "enabled" : "disabled"); + return 0; + } + private int getRestrictBackground() throws RemoteException { final PrintWriter pw = getOutPrintWriter(); pw.print("Restrict background status: "); diff --git a/services/core/java/com/android/server/notification/NotificationManagerService.java b/services/core/java/com/android/server/notification/NotificationManagerService.java index 4ab827908e34..ab5238f1ddbf 100755 --- a/services/core/java/com/android/server/notification/NotificationManagerService.java +++ b/services/core/java/com/android/server/notification/NotificationManagerService.java @@ -154,6 +154,7 @@ import android.app.usage.UsageStatsManagerInternal; import android.companion.ICompanionDeviceManager; import android.compat.annotation.ChangeId; import android.compat.annotation.EnabledAfter; +import android.compat.annotation.LoggingOnly; import android.content.BroadcastReceiver; import android.content.ComponentName; import android.content.ContentProvider; @@ -250,6 +251,7 @@ import android.widget.Toast; import com.android.internal.R; import com.android.internal.annotations.GuardedBy; import com.android.internal.annotations.VisibleForTesting; +import com.android.internal.compat.IPlatformCompat; import com.android.internal.config.sysui.SystemUiDeviceConfigFlags; import com.android.internal.logging.InstanceId; import com.android.internal.logging.InstanceIdSequence; @@ -446,6 +448,17 @@ public class NotificationManagerService extends SystemService { @EnabledAfter(targetSdkVersion = Build.VERSION_CODES.R) private static final long NOTIFICATION_TRAMPOLINE_BLOCK = 167676448L; + /** + * Rate limit showing toasts, on a per package basis. + * + * It limits the number of {@link android.widget.Toast#show()} calls to prevent overburdening + * the user with too many toasts in a limited time. Any attempt to show more toasts than allowed + * in a certain time frame will result in the toast being discarded. + */ + @ChangeId + @LoggingOnly + private static final long RATE_LIMIT_TOASTS = 174840628L; + private IActivityManager mAm; private ActivityTaskManagerInternal mAtm; private ActivityManager mActivityManager; @@ -466,6 +479,7 @@ public class NotificationManagerService extends SystemService { private UriGrantsManagerInternal mUgmInternal; private RoleObserver mRoleObserver; private UserManager mUm; + private IPlatformCompat mPlatformCompat; private ShortcutHelper mShortcutHelper; final IBinder mForegroundToken = new Binder(); @@ -1988,6 +2002,8 @@ public class NotificationManagerService extends SystemService { mDeviceIdleManager = getContext().getSystemService(DeviceIdleManager.class); mDpm = dpm; mUm = userManager; + mPlatformCompat = IPlatformCompat.Stub.asInterface( + ServiceManager.getService(Context.PLATFORM_COMPAT_SERVICE)); mUiHandler = new Handler(UiThread.get().getLooper()); String[] extractorNames; @@ -7412,6 +7428,7 @@ public class NotificationManagerService extends SystemService { private boolean tryShowToast(ToastRecord record, boolean rateLimitingEnabled, boolean isWithinQuota) { if (rateLimitingEnabled && !isWithinQuota) { + reportCompatRateLimitingToastsChange(record.uid); Slog.w(TAG, "Package " + record.pkg + " is above allowed toast quota, the " + "following toast was blocked and discarded: " + record); return false; @@ -7424,6 +7441,19 @@ public class NotificationManagerService extends SystemService { return record.show(); } + /** Reports rate limiting toasts compat change (used when the toast was blocked). */ + private void reportCompatRateLimitingToastsChange(int uid) { + final long id = Binder.clearCallingIdentity(); + try { + mPlatformCompat.reportChangeByUid(RATE_LIMIT_TOASTS, uid); + } catch (RemoteException e) { + Slog.e(TAG, "Unexpected exception while reporting toast was blocked due to rate" + + " limiting", e); + } finally { + Binder.restoreCallingIdentity(id); + } + } + @GuardedBy("mToastQueue") void cancelToastLocked(int index) { ToastRecord record = mToastQueue.get(index); diff --git a/services/core/java/com/android/server/om/OverlayManagerService.java b/services/core/java/com/android/server/om/OverlayManagerService.java index 4d4a6c19c7cf..2a9322d2444f 100644 --- a/services/core/java/com/android/server/om/OverlayManagerService.java +++ b/services/core/java/com/android/server/om/OverlayManagerService.java @@ -24,8 +24,6 @@ import static android.content.Intent.ACTION_PACKAGE_REMOVED; import static android.content.Intent.ACTION_USER_ADDED; import static android.content.Intent.ACTION_USER_REMOVED; import static android.content.Intent.EXTRA_REASON; -import static android.content.om.OverlayManagerTransaction.Request.TYPE_SET_DISABLED; -import static android.content.om.OverlayManagerTransaction.Request.TYPE_SET_ENABLED; import static android.content.pm.PackageManager.SIGNATURE_MATCH; import static android.os.Trace.TRACE_TAG_RRO; import static android.os.Trace.traceBegin; @@ -44,7 +42,6 @@ import android.content.Intent; import android.content.IntentFilter; import android.content.om.IOverlayManager; import android.content.om.OverlayInfo; -import android.content.om.OverlayManagerTransaction; import android.content.om.OverlayableInfo; import android.content.pm.IPackageManager; import android.content.pm.PackageInfo; @@ -89,14 +86,11 @@ import java.io.IOException; import java.io.PrintWriter; import java.util.ArrayList; import java.util.Arrays; -import java.util.Collection; import java.util.Collections; import java.util.HashMap; import java.util.HashSet; import java.util.List; import java.util.Map; -import java.util.Optional; -import java.util.Set; import java.util.function.Consumer; /** @@ -246,13 +240,8 @@ public final class OverlayManagerService extends SystemService { private final OverlayActorEnforcer mActorEnforcer; - private final Consumer<PackageAndUser> mPropagateOverlayChange = (pair) -> { - persistSettings(); - FgThread.getHandler().post(() -> { - List<String> affectedTargets = updatePackageManager(pair.packageName, pair.userId); - updateActivityManager(affectedTargets, pair.userId); - broadcastActionOverlayChanged(affectedTargets, pair.userId); - }); + private final Consumer<PackageAndUser> mOnOverlaysChanged = (pair) -> { + onOverlaysChanged(pair.packageName, pair.userId); }; public OverlayManagerService(@NonNull final Context context) { @@ -311,11 +300,11 @@ public final class OverlayManagerService extends SystemService { for (int i = 0; i < userCount; i++) { final UserInfo userInfo = users.get(i); if (!userInfo.supportsSwitchTo() && userInfo.id != UserHandle.USER_SYSTEM) { - // Initialize any users that can't be switched to, as their state would + // Initialize any users that can't be switched to, as there state would // never be setup in onSwitchUser(). We will switch to the system user right // after this, and its state will be setup there. final List<String> targets = mImpl.updateOverlaysForUser(users.get(i).id); - updatePackageManager(targets, users.get(i).id); + updateOverlayPaths(users.get(i).id, targets); } } } @@ -333,8 +322,7 @@ public final class OverlayManagerService extends SystemService { // any asset changes to the rest of the system synchronized (mLock) { final List<String> targets = mImpl.updateOverlaysForUser(newUserId); - final List<String> affectedTargets = updatePackageManager(targets, newUserId); - updateActivityManager(affectedTargets, newUserId); + updateAssets(newUserId, targets); } persistSettings(); } finally { @@ -424,10 +412,10 @@ public final class OverlayManagerService extends SystemService { try { if (pi.isOverlayPackage()) { mImpl.onOverlayPackageAdded(packageName, userId) - .ifPresent(mPropagateOverlayChange); + .ifPresent(mOnOverlaysChanged); } else { mImpl.onTargetPackageAdded(packageName, userId) - .ifPresent(mPropagateOverlayChange); + .ifPresent(mOnOverlaysChanged); } } catch (OperationFailedException e) { Slog.e(TAG, "onPackageAdded internal error", e); @@ -454,10 +442,10 @@ public final class OverlayManagerService extends SystemService { try { if (pi.isOverlayPackage()) { mImpl.onOverlayPackageChanged(packageName, userId) - .ifPresent(mPropagateOverlayChange); + .ifPresent(mOnOverlaysChanged); } else { mImpl.onTargetPackageChanged(packageName, userId) - .ifPresent(mPropagateOverlayChange); + .ifPresent(mOnOverlaysChanged); } } catch (OperationFailedException e) { Slog.e(TAG, "onPackageChanged internal error", e); @@ -481,7 +469,7 @@ public final class OverlayManagerService extends SystemService { if (oi != null) { try { mImpl.onOverlayPackageReplacing(packageName, userId) - .ifPresent(mPropagateOverlayChange); + .ifPresent(mOnOverlaysChanged); } catch (OperationFailedException e) { Slog.e(TAG, "onPackageReplacing internal error", e); } @@ -506,10 +494,10 @@ public final class OverlayManagerService extends SystemService { try { if (pi.isOverlayPackage()) { mImpl.onOverlayPackageReplaced(packageName, userId) - .ifPresent(mPropagateOverlayChange); + .ifPresent(mOnOverlaysChanged); } else { mImpl.onTargetPackageReplaced(packageName, userId) - .ifPresent(mPropagateOverlayChange); + .ifPresent(mOnOverlaysChanged); } } catch (OperationFailedException e) { Slog.e(TAG, "onPackageReplaced internal error", e); @@ -534,10 +522,10 @@ public final class OverlayManagerService extends SystemService { try { if (oi != null) { mImpl.onOverlayPackageRemoved(packageName, userId) - .ifPresent(mPropagateOverlayChange); + .ifPresent(mOnOverlaysChanged); } else { mImpl.onTargetPackageRemoved(packageName, userId) - .ifPresent(mPropagateOverlayChange); + .ifPresent(mOnOverlaysChanged); } } catch (OperationFailedException e) { Slog.e(TAG, "onPackageRemoved internal error", e); @@ -563,7 +551,7 @@ public final class OverlayManagerService extends SystemService { synchronized (mLock) { targets = mImpl.updateOverlaysForUser(userId); } - updatePackageManager(targets, userId); + updateOverlayPaths(userId, targets); } finally { traceEnd(TRACE_TAG_RRO); } @@ -660,7 +648,7 @@ public final class OverlayManagerService extends SystemService { synchronized (mLock) { try { mImpl.setEnabled(packageName, enable, realUserId) - .ifPresent(mPropagateOverlayChange); + .ifPresent(mOnOverlaysChanged); return true; } catch (OperationFailedException e) { return false; @@ -692,7 +680,7 @@ public final class OverlayManagerService extends SystemService { try { mImpl.setEnabledExclusive(packageName, false /* withinCategory */, realUserId) - .ifPresent(mPropagateOverlayChange); + .ifPresent(mOnOverlaysChanged); return true; } catch (OperationFailedException e) { return false; @@ -725,7 +713,7 @@ public final class OverlayManagerService extends SystemService { try { mImpl.setEnabledExclusive(packageName, true /* withinCategory */, realUserId) - .ifPresent(mPropagateOverlayChange); + .ifPresent(mOnOverlaysChanged); return true; } catch (OperationFailedException e) { return false; @@ -757,7 +745,7 @@ public final class OverlayManagerService extends SystemService { synchronized (mLock) { try { mImpl.setPriority(packageName, parentPackageName, realUserId) - .ifPresent(mPropagateOverlayChange); + .ifPresent(mOnOverlaysChanged); return true; } catch (OperationFailedException e) { return false; @@ -787,7 +775,7 @@ public final class OverlayManagerService extends SystemService { synchronized (mLock) { try { mImpl.setHighestPriority(packageName, realUserId) - .ifPresent(mPropagateOverlayChange); + .ifPresent(mOnOverlaysChanged); return true; } catch (OperationFailedException e) { return false; @@ -817,7 +805,7 @@ public final class OverlayManagerService extends SystemService { synchronized (mLock) { try { mImpl.setLowestPriority(packageName, realUserId) - .ifPresent(mPropagateOverlayChange); + .ifPresent(mOnOverlaysChanged); return true; } catch (OperationFailedException e) { return false; @@ -870,120 +858,6 @@ public final class OverlayManagerService extends SystemService { } @Override - public void commit(@NonNull final OverlayManagerTransaction transaction) - throws RemoteException { - try { - traceBegin(TRACE_TAG_RRO, "OMS#commit " + transaction); - try { - executeAllRequests(transaction); - } catch (Exception e) { - final long ident = Binder.clearCallingIdentity(); - try { - restoreSettings(); - } finally { - Binder.restoreCallingIdentity(ident); - } - Slog.d(TAG, "commit failed: " + e.getMessage(), e); - throw new SecurityException("commit failed" - + (DEBUG ? ": " + e.getMessage() : "")); - } - } finally { - traceEnd(TRACE_TAG_RRO); - } - } - - private Optional<PackageAndUser> executeRequest( - @NonNull final OverlayManagerTransaction.Request request) throws Exception { - final int realUserId = handleIncomingUser(request.userId, request.typeToString()); - enforceActor(request.packageName, request.typeToString(), realUserId); - - final long ident = Binder.clearCallingIdentity(); - try { - switch (request.type) { - case TYPE_SET_ENABLED: - Optional<PackageAndUser> opt1 = - mImpl.setEnabled(request.packageName, true, request.userId); - Optional<PackageAndUser> opt2 = - mImpl.setHighestPriority(request.packageName, request.userId); - // Both setEnabled and setHighestPriority affected the same - // target package and user: if both return non-empty - // Optionals, they are identical - return opt1.isPresent() ? opt1 : opt2; - case TYPE_SET_DISABLED: - return mImpl.setEnabled(request.packageName, false, request.userId); - default: - throw new IllegalArgumentException("unsupported request: " + request); - } - } finally { - Binder.restoreCallingIdentity(ident); - } - } - - private void executeAllRequests(@NonNull final OverlayManagerTransaction transaction) - throws Exception { - if (DEBUG) { - Slog.d(TAG, "commit " + transaction); - } - if (transaction == null) { - throw new IllegalArgumentException("null transaction"); - } - - // map: userId -> list<targetPackageName> - SparseArray<List<String>> affectedTargetsToUpdate = new SparseArray<>(); - - synchronized (mLock) { - // map: userId -> set<targetPackageName> - SparseArray<Set<String>> targetsToUpdate = new SparseArray<>(); - - // execute the requests (as calling user) - for (final OverlayManagerTransaction.Request request : transaction) { - executeRequest(request).ifPresent(target -> { - Set<String> userTargets = targetsToUpdate.get(target.userId); - if (userTargets == null) { - userTargets = new ArraySet<String>(); - targetsToUpdate.put(target.userId, userTargets); - } - userTargets.add(target.packageName); - }); - } - - // past the point of no return: the entire transaction has been - // processed successfully, we can no longer fail: continue as - // system_server - final long ident = Binder.clearCallingIdentity(); - try { - persistSettings(); - - // inform the package manager about the new paths - for (int index = 0; index < targetsToUpdate.size(); index++) { - final int userId = targetsToUpdate.keyAt(index); - final List<String> affectedTargets = - updatePackageManager(targetsToUpdate.valueAt(index), userId); - affectedTargetsToUpdate.put(userId, affectedTargets); - } - } finally { - Binder.restoreCallingIdentity(ident); - } - } // synchronized (mLock) - - FgThread.getHandler().post(() -> { - final long ident = Binder.clearCallingIdentity(); - try { - // schedule apps to refresh + broadcast the ACTION_OVERLAY_CHANGED intents - for (int index = 0; index < affectedTargetsToUpdate.size(); index++) { - final int userId = affectedTargetsToUpdate.keyAt(index); - final List<String> packageNames = affectedTargetsToUpdate.valueAt(index); - - updateActivityManager(packageNames, userId); - broadcastActionOverlayChanged(packageNames, userId); - } - } finally { - Binder.restoreCallingIdentity(ident); - } - }); - } - - @Override public void onShellCommand(@NonNull final FileDescriptor in, @NonNull final FileDescriptor out, @NonNull final FileDescriptor err, @NonNull final String[] args, @NonNull final ShellCallback callback, @@ -1104,7 +978,152 @@ public final class OverlayManagerService extends SystemService { } }; - private static final class PackageManagerHelperImpl implements PackageManagerHelper { + private void onOverlaysChanged(@NonNull final String targetPackageName, final int userId) { + persistSettings(); + FgThread.getHandler().post(() -> { + updateAssets(userId, targetPackageName); + + final Intent intent = new Intent(ACTION_OVERLAY_CHANGED, + Uri.fromParts("package", targetPackageName, null)); + intent.setFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY_BEFORE_BOOT); + + if (DEBUG) { + Slog.d(TAG, "send broadcast " + intent); + } + + try { + ActivityManager.getService().broadcastIntent(null, intent, null, null, 0, + null, null, null, android.app.AppOpsManager.OP_NONE, null, false, false, + userId); + } catch (RemoteException e) { + // Intentionally left empty. + } + }); + } + + /** + * Updates the target packages' set of enabled overlays in PackageManager. + */ + private ArrayList<String> updateOverlayPaths(int userId, List<String> targetPackageNames) { + try { + traceBegin(TRACE_TAG_RRO, "OMS#updateOverlayPaths " + targetPackageNames); + if (DEBUG) { + Slog.d(TAG, "Updating overlay assets"); + } + final PackageManagerInternal pm = + LocalServices.getService(PackageManagerInternal.class); + final boolean updateFrameworkRes = targetPackageNames.contains("android"); + if (updateFrameworkRes) { + targetPackageNames = pm.getTargetPackageNames(userId); + } + + final Map<String, List<String>> pendingChanges = + new ArrayMap<>(targetPackageNames.size()); + synchronized (mLock) { + final List<String> frameworkOverlays = + mImpl.getEnabledOverlayPackageNames("android", userId); + final int n = targetPackageNames.size(); + for (int i = 0; i < n; i++) { + final String targetPackageName = targetPackageNames.get(i); + List<String> list = new ArrayList<>(); + if (!"android".equals(targetPackageName)) { + list.addAll(frameworkOverlays); + } + list.addAll(mImpl.getEnabledOverlayPackageNames(targetPackageName, userId)); + pendingChanges.put(targetPackageName, list); + } + } + + final HashSet<String> updatedPackages = new HashSet<>(); + final int n = targetPackageNames.size(); + for (int i = 0; i < n; i++) { + final String targetPackageName = targetPackageNames.get(i); + if (DEBUG) { + Slog.d(TAG, "-> Updating overlay: target=" + targetPackageName + " overlays=[" + + TextUtils.join(",", pendingChanges.get(targetPackageName)) + + "] userId=" + userId); + } + + if (!pm.setEnabledOverlayPackages( + userId, targetPackageName, pendingChanges.get(targetPackageName), + updatedPackages)) { + Slog.e(TAG, String.format("Failed to change enabled overlays for %s user %d", + targetPackageName, userId)); + } + } + return new ArrayList<>(updatedPackages); + } finally { + traceEnd(TRACE_TAG_RRO); + } + } + + private void updateAssets(final int userId, final String targetPackageName) { + updateAssets(userId, Collections.singletonList(targetPackageName)); + } + + private void updateAssets(final int userId, List<String> targetPackageNames) { + final IActivityManager am = ActivityManager.getService(); + try { + final ArrayList<String> updatedPaths = updateOverlayPaths(userId, targetPackageNames); + am.scheduleApplicationInfoChanged(updatedPaths, userId); + } catch (RemoteException e) { + // Intentionally left empty. + } + } + + private void persistSettings() { + if (DEBUG) { + Slog.d(TAG, "Writing overlay settings"); + } + synchronized (mLock) { + FileOutputStream stream = null; + try { + stream = mSettingsFile.startWrite(); + mSettings.persist(stream); + mSettingsFile.finishWrite(stream); + } catch (IOException | XmlPullParserException e) { + mSettingsFile.failWrite(stream); + Slog.e(TAG, "failed to persist overlay state", e); + } + } + } + + private void restoreSettings() { + try { + traceBegin(TRACE_TAG_RRO, "OMS#restoreSettings"); + synchronized (mLock) { + if (!mSettingsFile.getBaseFile().exists()) { + return; + } + try (FileInputStream stream = mSettingsFile.openRead()) { + mSettings.restore(stream); + + // We might have data for dying users if the device was + // restarted before we received USER_REMOVED. Remove data for + // users that will not exist after the system is ready. + + final List<UserInfo> liveUsers = mUserManager.getUsers(true /*excludeDying*/); + final int[] liveUserIds = new int[liveUsers.size()]; + for (int i = 0; i < liveUsers.size(); i++) { + liveUserIds[i] = liveUsers.get(i).getUserHandle().getIdentifier(); + } + Arrays.sort(liveUserIds); + + for (int userId : mSettings.getUsers()) { + if (Arrays.binarySearch(liveUserIds, userId) < 0) { + mSettings.removeUser(userId); + } + } + } catch (IOException | XmlPullParserException e) { + Slog.e(TAG, "failed to restore overlay state", e); + } + } + } finally { + traceEnd(TRACE_TAG_RRO); + } + } + + private static final class PackageManagerHelperImpl implements PackageManagerHelper { private final Context mContext; private final IPackageManager mPackageManager; @@ -1314,151 +1333,4 @@ public final class OverlayManagerService extends SystemService { } } } - - // Helper methods to update other parts of the system or read/write - // settings: these methods should never call into each other! - - private void broadcastActionOverlayChanged(@NonNull final Collection<String> packageNames, - final int userId) { - for (final String packageName : packageNames) { - broadcastActionOverlayChanged(packageName, userId); - } - } - - private void broadcastActionOverlayChanged(@NonNull final String targetPackageName, - final int userId) { - final Intent intent = new Intent(ACTION_OVERLAY_CHANGED, - Uri.fromParts("package", targetPackageName, null)); - intent.setFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY_BEFORE_BOOT); - try { - ActivityManager.getService().broadcastIntent(null, intent, null, null, 0, null, null, - null, android.app.AppOpsManager.OP_NONE, null, false, false, userId); - } catch (RemoteException e) { - // Intentionally left empty. - } - } - - /** - * Tell the activity manager to tell a set of packages to reload their - * resources. - */ - private void updateActivityManager(List<String> targetPackageNames, final int userId) { - final IActivityManager am = ActivityManager.getService(); - try { - am.scheduleApplicationInfoChanged(targetPackageNames, userId); - } catch (RemoteException e) { - // Intentionally left empty. - } - } - - private ArrayList<String> updatePackageManager(String targetPackageNames, final int userId) { - return updatePackageManager(Collections.singletonList(targetPackageNames), userId); - } - - /** - * Updates the target packages' set of enabled overlays in PackageManager. - * @return the package names of affected targets (a superset of - * targetPackageNames: the target themserlves and shared libraries) - */ - private ArrayList<String> updatePackageManager(@NonNull Collection<String> targetPackageNames, - final int userId) { - try { - traceBegin(TRACE_TAG_RRO, "OMS#updatePackageManager " + targetPackageNames); - if (DEBUG) { - Slog.d(TAG, "Update package manager about changed overlays"); - } - final PackageManagerInternal pm = - LocalServices.getService(PackageManagerInternal.class); - final boolean updateFrameworkRes = targetPackageNames.contains("android"); - if (updateFrameworkRes) { - targetPackageNames = pm.getTargetPackageNames(userId); - } - - final Map<String, List<String>> pendingChanges = - new ArrayMap<>(targetPackageNames.size()); - synchronized (mLock) { - final List<String> frameworkOverlays = - mImpl.getEnabledOverlayPackageNames("android", userId); - for (final String targetPackageName : targetPackageNames) { - List<String> list = new ArrayList<>(); - if (!"android".equals(targetPackageName)) { - list.addAll(frameworkOverlays); - } - list.addAll(mImpl.getEnabledOverlayPackageNames(targetPackageName, userId)); - pendingChanges.put(targetPackageName, list); - } - } - - final HashSet<String> updatedPackages = new HashSet<>(); - for (final String targetPackageName : targetPackageNames) { - if (DEBUG) { - Slog.d(TAG, "-> Updating overlay: target=" + targetPackageName + " overlays=[" - + TextUtils.join(",", pendingChanges.get(targetPackageName)) - + "] userId=" + userId); - } - - if (!pm.setEnabledOverlayPackages( - userId, targetPackageName, pendingChanges.get(targetPackageName), - updatedPackages)) { - Slog.e(TAG, String.format("Failed to change enabled overlays for %s user %d", - targetPackageName, userId)); - } - } - return new ArrayList<>(updatedPackages); - } finally { - traceEnd(TRACE_TAG_RRO); - } - } - - private void persistSettings() { - if (DEBUG) { - Slog.d(TAG, "Writing overlay settings"); - } - synchronized (mLock) { - FileOutputStream stream = null; - try { - stream = mSettingsFile.startWrite(); - mSettings.persist(stream); - mSettingsFile.finishWrite(stream); - } catch (IOException | XmlPullParserException e) { - mSettingsFile.failWrite(stream); - Slog.e(TAG, "failed to persist overlay state", e); - } - } - } - - private void restoreSettings() { - try { - traceBegin(TRACE_TAG_RRO, "OMS#restoreSettings"); - synchronized (mLock) { - if (!mSettingsFile.getBaseFile().exists()) { - return; - } - try (FileInputStream stream = mSettingsFile.openRead()) { - mSettings.restore(stream); - - // We might have data for dying users if the device was - // restarted before we received USER_REMOVED. Remove data for - // users that will not exist after the system is ready. - - final List<UserInfo> liveUsers = mUserManager.getUsers(true /*excludeDying*/); - final int[] liveUserIds = new int[liveUsers.size()]; - for (int i = 0; i < liveUsers.size(); i++) { - liveUserIds[i] = liveUsers.get(i).getUserHandle().getIdentifier(); - } - Arrays.sort(liveUserIds); - - for (int userId : mSettings.getUsers()) { - if (Arrays.binarySearch(liveUserIds, userId) < 0) { - mSettings.removeUser(userId); - } - } - } catch (IOException | XmlPullParserException e) { - Slog.e(TAG, "failed to restore overlay state", e); - } - } - } finally { - traceEnd(TRACE_TAG_RRO); - } - } } diff --git a/services/core/java/com/android/server/pm/DumpState.java b/services/core/java/com/android/server/pm/DumpState.java index 520871ff40c8..4f986bd5276b 100644 --- a/services/core/java/com/android/server/pm/DumpState.java +++ b/services/core/java/com/android/server/pm/DumpState.java @@ -44,6 +44,7 @@ public final class DumpState { public static final int DUMP_APEX = 1 << 25; public static final int DUMP_QUERIES = 1 << 26; public static final int DUMP_KNOWN_PACKAGES = 1 << 27; + public static final int DUMP_PER_UID_READ_TIMEOUTS = 1 << 28; public static final int OPTION_SHOW_FILTERS = 1 << 0; public static final int OPTION_DUMP_ALL_COMPONENTS = 1 << 1; diff --git a/services/core/java/com/android/server/pm/MULTIUSER_AND_ENTERPRISE_OWNERS b/services/core/java/com/android/server/pm/MULTIUSER_AND_ENTERPRISE_OWNERS index 48f6bf84c28b..a52e9cf2f4c3 100644 --- a/services/core/java/com/android/server/pm/MULTIUSER_AND_ENTERPRISE_OWNERS +++ b/services/core/java/com/android/server/pm/MULTIUSER_AND_ENTERPRISE_OWNERS @@ -1,6 +1,6 @@ # OWNERS of Multiuser related files related to Enterprise -# TODO: include /MULTIUSER_OWNERS +include /MULTIUSER_OWNERS # Enterprise owners rubinxu@google.com diff --git a/services/core/java/com/android/server/pm/OWNERS b/services/core/java/com/android/server/pm/OWNERS index 004259b7478c..43c5d5e4015e 100644 --- a/services/core/java/com/android/server/pm/OWNERS +++ b/services/core/java/com/android/server/pm/OWNERS @@ -30,13 +30,12 @@ per-file CrossProfileAppsServiceImpl.java = omakoto@google.com, yamasani@google. per-file CrossProfileAppsService.java = omakoto@google.com, yamasani@google.com per-file CrossProfileIntentFilter.java = omakoto@google.com, yamasani@google.com per-file CrossProfileIntentResolver.java = omakoto@google.com, yamasani@google.com -per-file RestrictionsSet.java = bookatz@google.com, omakoto@google.com, yamasani@google.com, rubinxu@google.com, sandness@google.com -per-file UserManagerInternal.java = bookatz@google.com, omakoto@google.com, yamasani@google.com -per-file UserManagerService.java = bookatz@google.com, omakoto@google.com, yamasani@google.com -per-file UserRestrictionsUtils.java = omakoto@google.com, rubinxu@google.com, sandness@google.com, yamasani@google.com -per-file UserSystemPackageInstaller.java = bookatz@google.com, omakoto@google.com, yamasani@google.com -per-file UserTypeDetails.java = bookatz@google.com, omakoto@google.com, yamasani@google.com -per-file UserTypeFactory.java = bookatz@google.com, omakoto@google.com, yamasani@google.com +per-file RestrictionsSet.java = file:MULTIUSER_AND_ENTERPRISE_OWNERS +per-file UserManager* = file:/MULTIUSER_OWNERS +per-file UserRestriction* = file:MULTIUSER_AND_ENTERPRISE_OWNERS +per-file UserSystemPackageInstaller* = file:/MULTIUSER_OWNERS +per-file UserTypeDetails.java = file:/MULTIUSER_OWNERS +per-file UserTypeFactory.java = file:/MULTIUSER_OWNERS # security per-file KeySetHandle.java = cbrubaker@google.com, nnk@google.com diff --git a/services/core/java/com/android/server/pm/PackageInstallerSession.java b/services/core/java/com/android/server/pm/PackageInstallerSession.java index f97a5ee45346..3d04b5607922 100644 --- a/services/core/java/com/android/server/pm/PackageInstallerSession.java +++ b/services/core/java/com/android/server/pm/PackageInstallerSession.java @@ -111,6 +111,7 @@ import android.os.UserHandle; import android.os.incremental.IStorageHealthListener; import android.os.incremental.IncrementalFileStorages; import android.os.incremental.IncrementalManager; +import android.os.incremental.PerUidReadTimeouts; import android.os.incremental.StorageHealthCheckParams; import android.os.storage.StorageManager; import android.provider.Settings.Secure; @@ -3006,7 +3007,7 @@ public class PackageInstallerSession extends IPackageInstallerSession.Stub { final boolean isInstallerShell = (mInstallerUid == Process.SHELL_UID); if (isInstallerShell && isIncrementalInstallation() && mIncrementalFileStorages != null) { if (!packageLite.debuggable && !packageLite.profilableByShell) { - mIncrementalFileStorages.disableReadLogs(); + mIncrementalFileStorages.disallowReadLogs(); } } } @@ -3720,12 +3721,16 @@ public class PackageInstallerSession extends IPackageInstallerSession.Stub { }; if (!manualStartAndDestroy) { + final PerUidReadTimeouts[] perUidReadTimeouts = mPm.getPerUidReadTimeouts(); + final StorageHealthCheckParams healthCheckParams = new StorageHealthCheckParams(); healthCheckParams.blockedTimeoutMs = INCREMENTAL_STORAGE_BLOCKED_TIMEOUT_MS; healthCheckParams.unhealthyTimeoutMs = INCREMENTAL_STORAGE_UNHEALTHY_TIMEOUT_MS; healthCheckParams.unhealthyMonitoringMs = INCREMENTAL_STORAGE_UNHEALTHY_MONITORING_MS; + final boolean systemDataLoader = params.getComponentName().getPackageName() == SYSTEM_DATA_LOADER_PACKAGE; + final IStorageHealthListener healthListener = new IStorageHealthListener.Stub() { @Override public void onHealthStatus(int storageId, int status) { @@ -3760,7 +3765,8 @@ public class PackageInstallerSession extends IPackageInstallerSession.Stub { try { mIncrementalFileStorages = IncrementalFileStorages.initialize(mContext, stageDir, - params, statusListener, healthCheckParams, healthListener, addedFiles); + params, statusListener, healthCheckParams, healthListener, addedFiles, + perUidReadTimeouts); return false; } catch (IOException e) { throw new PackageManagerException(INSTALL_FAILED_MEDIA_UNAVAILABLE, e.getMessage(), diff --git a/services/core/java/com/android/server/pm/PackageManagerService.java b/services/core/java/com/android/server/pm/PackageManagerService.java index 27008d824dd2..4467b5110d08 100644 --- a/services/core/java/com/android/server/pm/PackageManagerService.java +++ b/services/core/java/com/android/server/pm/PackageManagerService.java @@ -107,6 +107,7 @@ import static android.os.incremental.IncrementalManager.isIncrementalPath; import static android.os.storage.StorageManager.FLAG_STORAGE_CE; import static android.os.storage.StorageManager.FLAG_STORAGE_DE; import static android.os.storage.StorageManager.FLAG_STORAGE_EXTERNAL; +import static android.provider.DeviceConfig.NAMESPACE_PACKAGE_MANAGER_SERVICE; import static com.android.internal.annotations.VisibleForTesting.Visibility; import static com.android.internal.app.IntentForwarderActivity.FORWARD_INTENT_TO_MANAGED_PROFILE; @@ -287,6 +288,7 @@ import android.os.UserManager; import android.os.incremental.IStorageHealthListener; import android.os.incremental.IncrementalManager; import android.os.incremental.IncrementalStorage; +import android.os.incremental.PerUidReadTimeouts; import android.os.incremental.StorageHealthCheckParams; import android.os.storage.DiskInfo; import android.os.storage.IStorageManager; @@ -512,6 +514,7 @@ public class PackageManagerService extends IPackageManager.Stub public static final boolean DEBUG_COMPRESSION = Build.IS_DEBUGGABLE; public static final boolean DEBUG_CACHES = false; public static final boolean TRACE_CACHES = false; + private static final boolean DEBUG_PER_UID_READ_TIMEOUTS = false; // Debug output for dexopting. This is shared between PackageManagerService, OtaDexoptService // and PackageDexOptimizer. All these classes have their own flag to allow switching a single @@ -647,6 +650,24 @@ public class PackageManagerService extends IPackageManager.Stub private static final long DEFAULT_ENABLE_ROLLBACK_TIMEOUT_MILLIS = 10 * 1000; /** + * Default IncFs timeouts. Maximum values in IncFs is 1hr. + * + * <p>If flag value is empty, the default value will be assigned. + * + * Flag type: {@code String} + * Namespace: NAMESPACE_PACKAGE_MANAGER_SERVICE + */ + private static final String PROPERTY_INCFS_DEFAULT_TIMEOUTS = "incfs_default_timeouts"; + + /** + * Known digesters with optional timeouts. + * + * Flag type: {@code String} + * Namespace: NAMESPACE_PACKAGE_MANAGER_SERVICE + */ + private static final String PROPERTY_KNOWN_DIGESTERS_LIST = "known_digesters_list"; + + /** * The default response for package verification timeout. * * This can be either PackageManager.VERIFICATION_ALLOW or @@ -909,6 +930,11 @@ public class PackageManagerService extends IPackageManager.Stub final private ArrayList<IPackageChangeObserver> mPackageChangeObservers = new ArrayList<>(); + // Cached parsed flag value. Invalidated on each flag change. + private PerUidReadTimeouts[] mPerUidReadTimeoutsCache; + + private static final PerUidReadTimeouts[] EMPTY_PER_UID_READ_TIMEOUTS_ARRAY = {}; + /** * Unit tests will instantiate, extend and/or mock to mock dependencies / behaviors. * @@ -23738,6 +23764,17 @@ public class PackageManagerService extends IPackageManager.Stub mInstallerService.restoreAndApplyStagedSessionIfNeeded(); mExistingPackages = null; + + // Clear cache on flags changes. + DeviceConfig.addOnPropertiesChangedListener( + NAMESPACE_PACKAGE_MANAGER_SERVICE, FgThread.getExecutor(), + properties -> { + final Set<String> keyset = properties.getKeyset(); + if (keyset.contains(PROPERTY_INCFS_DEFAULT_TIMEOUTS) || keyset.contains( + PROPERTY_KNOWN_DIGESTERS_LIST)) { + mPerUidReadTimeoutsCache = null; + } + }); } public void waitForAppDataPrepared() { @@ -23828,6 +23865,7 @@ public class PackageManagerService extends IPackageManager.Stub pw.println(" v[erifiers]: print package verifier info"); pw.println(" d[omain-preferred-apps]: print domains preferred apps"); pw.println(" i[ntent-filter-verifiers]|ifv: print intent filter verifier info"); + pw.println(" t[imeouts]: print read timeouts for known digesters"); pw.println(" version: print database version info"); pw.println(" write: write current settings now"); pw.println(" installs: details about install sessions"); @@ -23982,6 +24020,8 @@ public class PackageManagerService extends IPackageManager.Stub dumpState.setDump(DumpState.DUMP_SERVICE_PERMISSIONS); } else if ("known-packages".equals(cmd)) { dumpState.setDump(DumpState.DUMP_KNOWN_PACKAGES); + } else if ("t".equals(cmd) || "timeouts".equals(cmd)) { + dumpState.setDump(DumpState.DUMP_PER_UID_READ_TIMEOUTS); } else if ("write".equals(cmd)) { synchronized (mLock) { writeSettingsLPrTEMP(); @@ -24380,6 +24420,25 @@ public class PackageManagerService extends IPackageManager.Stub if (!checkin && dumpState.isDumping(DumpState.DUMP_APEX)) { mApexManager.dump(pw, packageName); } + + if (!checkin && dumpState.isDumping(DumpState.DUMP_PER_UID_READ_TIMEOUTS) + && packageName == null) { + pw.println(); + pw.println("Per UID read timeouts:"); + pw.println(" Default timeouts flag: " + getDefaultTimeouts()); + pw.println(" Known digesters list flag: " + getKnownDigestersList()); + + PerUidReadTimeouts[] items = getPerUidReadTimeouts(); + pw.println(" Timeouts (" + items.length + "):"); + for (PerUidReadTimeouts item : items) { + pw.print(" ("); + pw.print("uid=" + item.uid + ", "); + pw.print("minTimeUs=" + item.minTimeUs + ", "); + pw.print("minPendingTimeUs=" + item.minPendingTimeUs + ", "); + pw.print("maxPendingTimeUs=" + item.maxPendingTimeUs); + pw.println(")"); + } + } } //TODO: b/111402650 @@ -27967,6 +28026,88 @@ public class PackageManagerService extends IPackageManager.Stub SystemClock.sleep(durationMs); } } + + private static String getDefaultTimeouts() { + return DeviceConfig.getString(DeviceConfig.NAMESPACE_PACKAGE_MANAGER_SERVICE, + PROPERTY_INCFS_DEFAULT_TIMEOUTS, ""); + } + + private static String getKnownDigestersList() { + return DeviceConfig.getString(DeviceConfig.NAMESPACE_PACKAGE_MANAGER_SERVICE, + PROPERTY_KNOWN_DIGESTERS_LIST, ""); + } + + /** + * Returns the array containing per-uid timeout configuration. + * This is derived from DeviceConfig flags. + */ + public @NonNull PerUidReadTimeouts[] getPerUidReadTimeouts() { + PerUidReadTimeouts[] result = mPerUidReadTimeoutsCache; + if (result == null) { + result = parsePerUidReadTimeouts(); + mPerUidReadTimeoutsCache = result; + } + return result; + } + + private @NonNull PerUidReadTimeouts[] parsePerUidReadTimeouts() { + final String defaultTimeouts = getDefaultTimeouts(); + final String knownDigestersList = getKnownDigestersList(); + final List<PerPackageReadTimeouts> perPackageReadTimeouts = + PerPackageReadTimeouts.parseDigestersList(defaultTimeouts, knownDigestersList); + + if (perPackageReadTimeouts.size() == 0) { + return EMPTY_PER_UID_READ_TIMEOUTS_ARRAY; + } + + final int[] allUsers = mInjector.getUserManagerService().getUserIds(); + + List<PerUidReadTimeouts> result = new ArrayList<>(perPackageReadTimeouts.size()); + synchronized (mLock) { + for (int i = 0, size = perPackageReadTimeouts.size(); i < size; ++i) { + final PerPackageReadTimeouts perPackage = perPackageReadTimeouts.get(i); + final PackageSetting ps = mSettings.mPackages.get(perPackage.packageName); + if (ps == null) { + if (DEBUG_PER_UID_READ_TIMEOUTS) { + Slog.i(TAG, "PerUidReadTimeouts: package not found = " + + perPackage.packageName); + } + continue; + } + final AndroidPackage pkg = ps.getPkg(); + if (pkg.getLongVersionCode() < perPackage.versionCodes.minVersionCode + || pkg.getLongVersionCode() > perPackage.versionCodes.maxVersionCode) { + if (DEBUG_PER_UID_READ_TIMEOUTS) { + Slog.i(TAG, "PerUidReadTimeouts: version code is not in range = " + + perPackage.packageName + ":" + pkg.getLongVersionCode()); + } + continue; + } + if (perPackage.sha256certificate != null + && !pkg.getSigningDetails().hasSha256Certificate( + perPackage.sha256certificate)) { + if (DEBUG_PER_UID_READ_TIMEOUTS) { + Slog.i(TAG, "PerUidReadTimeouts: invalid certificate = " + + perPackage.packageName + ":" + pkg.getLongVersionCode()); + } + continue; + } + for (int userId : allUsers) { + if (!ps.getInstalled(userId)) { + continue; + } + final int uid = UserHandle.getUid(userId, ps.appId); + final PerUidReadTimeouts perUid = new PerUidReadTimeouts(); + perUid.uid = uid; + perUid.minTimeUs = perPackage.timeouts.minTimeUs; + perUid.minPendingTimeUs = perPackage.timeouts.minPendingTimeUs; + perUid.maxPendingTimeUs = perPackage.timeouts.maxPendingTimeUs; + result.add(perUid); + } + } + } + return result.toArray(new PerUidReadTimeouts[result.size()]); + } } interface PackageSender { diff --git a/services/core/java/com/android/server/pm/PerPackageReadTimeouts.java b/services/core/java/com/android/server/pm/PerPackageReadTimeouts.java new file mode 100644 index 000000000000..3b306a850b64 --- /dev/null +++ b/services/core/java/com/android/server/pm/PerPackageReadTimeouts.java @@ -0,0 +1,187 @@ +/* + * Copyright (C) 2021 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.server.pm; + +import android.annotation.NonNull;; +import android.text.TextUtils; + +import com.android.internal.util.HexDump; + +import java.util.ArrayList; +import java.util.Collections; +import java.util.List; + +class PerPackageReadTimeouts { + static long tryParseLong(String str, long defaultValue) { + try { + return Long.parseLong(str); + } catch (NumberFormatException nfe) { + return defaultValue; + } + } + + static byte[] tryParseSha256(String str) { + if (TextUtils.isEmpty(str)) { + return null; + } + try { + return HexDump.hexStringToByteArray(str); + } catch (RuntimeException e) { + return null; + } + } + + static class Timeouts { + public final long minTimeUs; + public final long minPendingTimeUs; + public final long maxPendingTimeUs; + + // 3600000000us == 1hr + public static final Timeouts DEFAULT = new Timeouts(3600000000L, 3600000000L, 3600000000L); + + private Timeouts(long minTimeUs, long minPendingTimeUs, long maxPendingTimeUs) { + this.minTimeUs = minTimeUs; + this.minPendingTimeUs = minPendingTimeUs; + this.maxPendingTimeUs = maxPendingTimeUs; + } + + static Timeouts parse(String timeouts) { + String[] splits = timeouts.split(":", 3); + if (splits.length != 3) { + return DEFAULT; + } + final long minTimeUs = tryParseLong(splits[0], DEFAULT.minTimeUs); + final long minPendingTimeUs = tryParseLong(splits[1], DEFAULT.minPendingTimeUs); + final long maxPendingTimeUs = tryParseLong(splits[2], DEFAULT.maxPendingTimeUs); + if (0 <= minTimeUs && minTimeUs <= minPendingTimeUs + && minPendingTimeUs <= maxPendingTimeUs) { + // validity check + return new Timeouts(minTimeUs, minPendingTimeUs, maxPendingTimeUs); + } + return DEFAULT; + } + } + + static class VersionCodes { + public final long minVersionCode; + public final long maxVersionCode; + + public static final VersionCodes ALL_VERSION_CODES = new VersionCodes(Long.MIN_VALUE, + Long.MAX_VALUE); + + private VersionCodes(long minVersionCode, long maxVersionCode) { + this.minVersionCode = minVersionCode; + this.maxVersionCode = maxVersionCode; + } + + static VersionCodes parse(String codes) { + if (TextUtils.isEmpty(codes)) { + return ALL_VERSION_CODES; + } + String[] splits = codes.split("-", 2); + switch (splits.length) { + case 1: { + // single version code + try { + final long versionCode = Long.parseLong(splits[0]); + return new VersionCodes(versionCode, versionCode); + } catch (NumberFormatException nfe) { + return ALL_VERSION_CODES; + } + } + case 2: { + final long minVersionCode = tryParseLong(splits[0], + ALL_VERSION_CODES.minVersionCode); + final long maxVersionCode = tryParseLong(splits[1], + ALL_VERSION_CODES.maxVersionCode); + if (minVersionCode <= maxVersionCode) { + return new VersionCodes(minVersionCode, maxVersionCode); + } + break; + } + } + return ALL_VERSION_CODES; + } + } + + public final String packageName; + public final byte[] sha256certificate; + public final VersionCodes versionCodes; + public final Timeouts timeouts; + + private PerPackageReadTimeouts(String packageName, byte[] sha256certificate, + VersionCodes versionCodes, Timeouts timeouts) { + this.packageName = packageName; + this.sha256certificate = sha256certificate; + this.versionCodes = versionCodes; + this.timeouts = timeouts; + } + + @SuppressWarnings("fallthrough") + static PerPackageReadTimeouts parse(String timeoutsStr, VersionCodes defaultVersionCodes, + Timeouts defaultTimeouts) { + String packageName = null; + byte[] sha256certificate = null; + VersionCodes versionCodes = defaultVersionCodes; + Timeouts timeouts = defaultTimeouts; + + final String[] splits = timeoutsStr.split(":", 4); + switch (splits.length) { + case 4: + timeouts = Timeouts.parse(splits[3]); + // fall through + case 3: + versionCodes = VersionCodes.parse(splits[2]); + // fall through + case 2: + sha256certificate = tryParseSha256(splits[1]); + // fall through + case 1: + packageName = splits[0]; + break; + default: + return null; + } + if (TextUtils.isEmpty(packageName)) { + return null; + } + + return new PerPackageReadTimeouts(packageName, sha256certificate, versionCodes, + timeouts); + } + + static @NonNull List<PerPackageReadTimeouts> parseDigestersList(String defaultTimeoutsStr, + String knownDigestersList) { + if (TextUtils.isEmpty(knownDigestersList)) { + return Collections.emptyList(); + } + + final VersionCodes defaultVersionCodes = VersionCodes.ALL_VERSION_CODES; + final Timeouts defaultTimeouts = Timeouts.parse(defaultTimeoutsStr); + + String[] packages = knownDigestersList.split(","); + List<PerPackageReadTimeouts> result = new ArrayList<>(packages.length); + for (int i = 0, size = packages.length; i < size; ++i) { + PerPackageReadTimeouts timeouts = PerPackageReadTimeouts.parse(packages[i], + defaultVersionCodes, defaultTimeouts); + if (timeouts != null) { + result.add(timeouts); + } + } + return result; + } +} diff --git a/services/core/java/com/android/server/tv/TvInputHardwareManager.java b/services/core/java/com/android/server/tv/TvInputHardwareManager.java index 2314afc787c3..a036bd196b23 100755 --- a/services/core/java/com/android/server/tv/TvInputHardwareManager.java +++ b/services/core/java/com/android/server/tv/TvInputHardwareManager.java @@ -380,8 +380,9 @@ class TvInputHardwareManager implements TvInputHal.Callback { return null; } - ResourceClientProfile profile = - new ResourceClientProfile(tvInputSessionId, priorityHint); + ResourceClientProfile profile = new ResourceClientProfile(); + profile.tvInputSessionId = tvInputSessionId; + profile.useCase = priorityHint; ResourceClientProfile holderProfile = connection.getResourceClientProfileLocked(); if (holderProfile != null && trm != null && !trm.isHigherPriority(profile, holderProfile)) { diff --git a/services/core/java/com/android/server/tv/tunerresourcemanager/CasResource.java b/services/core/java/com/android/server/tv/tunerresourcemanager/CasResource.java index 54ad1d268e56..4a81c95f0b8f 100644 --- a/services/core/java/com/android/server/tv/tunerresourcemanager/CasResource.java +++ b/services/core/java/com/android/server/tv/tunerresourcemanager/CasResource.java @@ -25,7 +25,7 @@ import java.util.Set; * * @hide */ -public final class CasResource { +public class CasResource { private final int mSystemId; @@ -38,7 +38,7 @@ public final class CasResource { */ private Map<Integer, Integer> mOwnerClientIdsToSessionNum = new HashMap<>(); - private CasResource(Builder builder) { + CasResource(Builder builder) { this.mSystemId = builder.mSystemId; this.mMaxSessionNum = builder.mMaxSessionNum; this.mAvailableSessionNum = builder.mMaxSessionNum; @@ -111,7 +111,7 @@ public final class CasResource { public static class Builder { private int mSystemId; - private int mMaxSessionNum; + protected int mMaxSessionNum; Builder(int systemId) { this.mSystemId = systemId; @@ -138,7 +138,7 @@ public final class CasResource { } } - private String ownersMapToString() { + protected String ownersMapToString() { StringBuilder string = new StringBuilder("{"); for (int clienId : mOwnerClientIdsToSessionNum.keySet()) { string.append(" clientId=") diff --git a/services/core/java/com/android/server/tv/tunerresourcemanager/CiCamResource.java b/services/core/java/com/android/server/tv/tunerresourcemanager/CiCamResource.java new file mode 100644 index 000000000000..31149f3590b8 --- /dev/null +++ b/services/core/java/com/android/server/tv/tunerresourcemanager/CiCamResource.java @@ -0,0 +1,70 @@ +/* + * Copyright 2020 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.tv.tunerresourcemanager; + +/** + * A CiCam resource object used by the Tuner Resource Manager to record the CiCam + * information. + * + * @hide + */ +public final class CiCamResource extends CasResource { + private CiCamResource(Builder builder) { + super(builder); + } + + @Override + public String toString() { + return "CiCamResource[systemId=" + this.getSystemId() + + ", isFullyUsed=" + (this.isFullyUsed()) + + ", maxSessionNum=" + this.getMaxSessionNum() + + ", ownerClients=" + ownersMapToString() + "]"; + } + + public int getCiCamId() { + return this.getSystemId(); + } + + /** + * Builder class for {@link CiCamResource}. + */ + public static class Builder extends CasResource.Builder { + Builder(int systemId) { + super(systemId); + } + + /** + * Builder for {@link CasResource}. + * + * @param maxSessionNum the max session num the current Cas has. + */ + public Builder maxSessionNum(int maxSessionNum) { + super.mMaxSessionNum = maxSessionNum; + return this; + } + + /** + * Build a {@link CiCamResource}. + * + * @return {@link CiCamResource}. + */ + @Override + public CiCamResource build() { + CiCamResource ciCam = new CiCamResource(this); + return ciCam; + } + } +} diff --git a/services/core/java/com/android/server/tv/tunerresourcemanager/ClientProfile.java b/services/core/java/com/android/server/tv/tunerresourcemanager/ClientProfile.java index edf007d428c1..5723e1dcceb5 100644 --- a/services/core/java/com/android/server/tv/tunerresourcemanager/ClientProfile.java +++ b/services/core/java/com/android/server/tv/tunerresourcemanager/ClientProfile.java @@ -50,6 +50,8 @@ public final class ClientProfile { */ private final int mProcessId; + private boolean mIsForeground; + /** * All the clients that share the same resource would be under the same group id. * @@ -83,6 +85,11 @@ public final class ClientProfile { private int mUsingCasSystemId = INVALID_RESOURCE_ID; /** + * CiCam id that is used by the client. + */ + private int mUsingCiCamId = INVALID_RESOURCE_ID; + + /** * Optional arbitrary priority value given by the client. * * <p>This value can override the default priorotiy calculated from @@ -113,6 +120,20 @@ public final class ClientProfile { return mProcessId; } + /** + * Set the current isForeground status. + */ + public void setForeground(boolean isForeground) { + mIsForeground = isForeground; + } + + /** + * Get the previous recorded isForeground status. + */ + public boolean isForeground() { + return mIsForeground; + } + public int getGroupId() { return mGroupId; } @@ -222,6 +243,26 @@ public final class ClientProfile { } /** + * Set when the client starts to connect to a CiCam. + * + * @param ciCamId ciCam being used. + */ + public void useCiCam(int ciCamId) { + mUsingCiCamId = ciCamId; + } + + public int getInUseCiCamId() { + return mUsingCiCamId; + } + + /** + * Called when the client disconnect to a CiCam. + */ + public void releaseCiCam() { + mUsingCiCamId = INVALID_RESOURCE_ID; + } + + /** * Called to reclaim all the resources being used by the current client. */ public void reclaimAllResources() { @@ -229,6 +270,7 @@ public final class ClientProfile { mShareFeClientIds.clear(); mUsingLnbHandles.clear(); mUsingCasSystemId = INVALID_RESOURCE_ID; + mUsingCiCamId = INVALID_RESOURCE_ID; } @Override diff --git a/services/core/java/com/android/server/tv/tunerresourcemanager/TunerResourceManagerService.java b/services/core/java/com/android/server/tv/tunerresourcemanager/TunerResourceManagerService.java index 8c6e690afe5b..072bdd2d1506 100644 --- a/services/core/java/com/android/server/tv/tunerresourcemanager/TunerResourceManagerService.java +++ b/services/core/java/com/android/server/tv/tunerresourcemanager/TunerResourceManagerService.java @@ -27,6 +27,7 @@ import android.media.tv.tunerresourcemanager.CasSessionRequest; import android.media.tv.tunerresourcemanager.IResourcesReclaimListener; import android.media.tv.tunerresourcemanager.ITunerResourceManager; import android.media.tv.tunerresourcemanager.ResourceClientProfile; +import android.media.tv.tunerresourcemanager.TunerCiCamRequest; import android.media.tv.tunerresourcemanager.TunerDemuxRequest; import android.media.tv.tunerresourcemanager.TunerDescramblerRequest; import android.media.tv.tunerresourcemanager.TunerFrontendInfo; @@ -71,6 +72,8 @@ public class TunerResourceManagerService extends SystemService implements IBinde private Map<Integer, LnbResource> mLnbResources = new HashMap<>(); // Map of the current available Cas resources private Map<Integer, CasResource> mCasResources = new HashMap<>(); + // Map of the current available CiCam resources + private Map<Integer, CiCamResource> mCiCamResources = new HashMap<>(); @GuardedBy("mLock") private Map<Integer, ResourcesReclaimListenerRecord> mListeners = new HashMap<>(); @@ -141,8 +144,8 @@ public class TunerResourceManagerService extends SystemService implements IBinde throw new RemoteException("IResourcesReclaimListener can't be null!"); } - if (!mPriorityCongfig.isDefinedUseCase(profile.getUseCase())) { - throw new RemoteException("Use undefined client use case:" + profile.getUseCase()); + if (!mPriorityCongfig.isDefinedUseCase(profile.useCase)) { + throw new RemoteException("Use undefined client use case:" + profile.useCase); } synchronized (mLock) { @@ -209,14 +212,14 @@ public class TunerResourceManagerService extends SystemService implements IBinde throw new RemoteException("frontendHandle can't be null"); } synchronized (mLock) { - if (!checkClientExists(request.getClientId())) { + if (!checkClientExists(request.clientId)) { throw new RemoteException("Request frontend from unregistered client: " - + request.getClientId()); + + request.clientId); } // If the request client is holding or sharing a frontend, throw an exception. - if (!getClientProfile(request.getClientId()).getInUseFrontendHandles().isEmpty()) { + if (!getClientProfile(request.clientId).getInUseFrontendHandles().isEmpty()) { throw new RemoteException("Release frontend before requesting another one. " - + "Client id: " + request.getClientId()); + + "Client id: " + request.clientId); } return requestFrontendInternal(request, frontendHandle); } @@ -252,9 +255,9 @@ public class TunerResourceManagerService extends SystemService implements IBinde throw new RemoteException("demuxHandle can't be null"); } synchronized (mLock) { - if (!checkClientExists(request.getClientId())) { + if (!checkClientExists(request.clientId)) { throw new RemoteException("Request demux from unregistered client:" - + request.getClientId()); + + request.clientId); } return requestDemuxInternal(request, demuxHandle); } @@ -269,9 +272,9 @@ public class TunerResourceManagerService extends SystemService implements IBinde throw new RemoteException("descramblerHandle can't be null"); } synchronized (mLock) { - if (!checkClientExists(request.getClientId())) { + if (!checkClientExists(request.clientId)) { throw new RemoteException("Request descrambler from unregistered client:" - + request.getClientId()); + + request.clientId); } return requestDescramblerInternal(request, descramblerHandle); } @@ -285,15 +288,31 @@ public class TunerResourceManagerService extends SystemService implements IBinde throw new RemoteException("casSessionHandle can't be null"); } synchronized (mLock) { - if (!checkClientExists(request.getClientId())) { + if (!checkClientExists(request.clientId)) { throw new RemoteException("Request cas from unregistered client:" - + request.getClientId()); + + request.clientId); } return requestCasSessionInternal(request, casSessionHandle); } } @Override + public boolean requestCiCam(@NonNull TunerCiCamRequest request, + @NonNull int[] ciCamHandle) throws RemoteException { + enforceTrmAccessPermission("requestCiCam"); + if (ciCamHandle == null) { + throw new RemoteException("ciCamHandle can't be null"); + } + synchronized (mLock) { + if (!checkClientExists(request.clientId)) { + throw new RemoteException("Request ciCam from unregistered client:" + + request.clientId); + } + return requestCiCamInternal(request, ciCamHandle); + } + } + + @Override public boolean requestLnb(@NonNull TunerLnbRequest request, @NonNull int[] lnbHandle) throws RemoteException { enforceTunerAccessPermission("requestLnb"); @@ -302,9 +321,9 @@ public class TunerResourceManagerService extends SystemService implements IBinde throw new RemoteException("lnbHandle can't be null"); } synchronized (mLock) { - if (!checkClientExists(request.getClientId())) { + if (!checkClientExists(request.clientId)) { throw new RemoteException("Request lnb from unregistered client:" - + request.getClientId()); + + request.clientId); } return requestLnbInternal(request, lnbHandle); } @@ -378,6 +397,34 @@ public class TunerResourceManagerService extends SystemService implements IBinde } @Override + public void releaseCiCam(int ciCamHandle, int clientId) throws RemoteException { + enforceTrmAccessPermission("releaseCiCam"); + if (!validateResourceHandle( + TunerResourceManager.TUNER_RESOURCE_TYPE_FRONTEND_CICAM, ciCamHandle)) { + throw new RemoteException("ciCamHandle can't be invalid"); + } + synchronized (mLock) { + if (!checkClientExists(clientId)) { + throw new RemoteException("Release ciCam from unregistered client:" + clientId); + } + int ciCamId = getClientProfile(clientId).getInUseCiCamId(); + if (ciCamId != getResourceIdFromHandle(ciCamHandle)) { + throw new RemoteException("The client " + clientId + " is not the owner of " + + "the releasing ciCam."); + } + CiCamResource ciCam = getCiCamResource(ciCamId); + if (ciCam == null) { + throw new RemoteException("Releasing ciCam does not exist."); + } + if (!ciCam.getOwnerClientIds().contains(clientId)) { + throw new RemoteException( + "Client is not the current owner of the releasing ciCam."); + } + releaseCiCamInternal(ciCam, clientId); + } + } + + @Override public void releaseLnb(int lnbHandle, int clientId) throws RemoteException { enforceTunerAccessPermission("releaseLnb"); enforceTrmAccessPermission("releaseLnb"); @@ -441,12 +488,12 @@ public class TunerResourceManagerService extends SystemService implements IBinde // TODO tell if the client already exists clientId[0] = mNextUnusedClientId++; - int pid = profile.getTvInputSessionId() == null + int pid = profile.tvInputSessionId == null ? Binder.getCallingPid() /*callingPid*/ - : mTvInputManager.getClientPid(profile.getTvInputSessionId()); /*tvAppId*/ + : mTvInputManager.getClientPid(profile.tvInputSessionId); /*tvAppId*/ // Update Media Resource Manager with the tvAppId - if (profile.getTvInputSessionId() != null && mMediaResourceManager != null) { + if (profile.tvInputSessionId != null && mMediaResourceManager != null) { try { mMediaResourceManager.overridePid(Binder.getCallingPid(), pid); } catch (RemoteException e) { @@ -456,11 +503,13 @@ public class TunerResourceManagerService extends SystemService implements IBinde } ClientProfile clientProfile = new ClientProfile.Builder(clientId[0]) - .tvInputSessionId(profile.getTvInputSessionId()) - .useCase(profile.getUseCase()) + .tvInputSessionId(profile.tvInputSessionId) + .useCase(profile.useCase) .processId(pid) .build(); - clientProfile.setPriority(getClientPriority(profile.getUseCase(), pid)); + clientProfile.setForeground(checkIsForeground(pid)); + clientProfile.setPriority( + getClientPriority(profile.useCase, clientProfile.isForeground())); addClientProfile(clientId[0], clientProfile, listener); } @@ -498,6 +547,7 @@ public class TunerResourceManagerService extends SystemService implements IBinde return false; } + profile.setForeground(checkIsForeground(profile.getProcessId())); profile.setPriority(priority); profile.setNiceValue(niceValue); @@ -520,16 +570,16 @@ public class TunerResourceManagerService extends SystemService implements IBinde // Update frontendResources map and other mappings accordingly for (int i = 0; i < infos.length; i++) { - if (getFrontendResource(infos[i].getHandle()) != null) { + if (getFrontendResource(infos[i].handle) != null) { if (DEBUG) { - Slog.d(TAG, "Frontend handle=" + infos[i].getHandle() + "exists."); + Slog.d(TAG, "Frontend handle=" + infos[i].handle + "exists."); } - updatingFrontendHandles.remove(infos[i].getHandle()); + updatingFrontendHandles.remove(infos[i].handle); } else { // Add a new fe resource - FrontendResource newFe = new FrontendResource.Builder(infos[i].getHandle()) - .type(infos[i].getFrontendType()) - .exclusiveGroupId(infos[i].getExclusiveGroupId()) + FrontendResource newFe = new FrontendResource.Builder(infos[i].handle) + .type(infos[i].frontendType) + .exclusiveGroupId(infos[i].exclusiveGroupId) .build(); addFrontendResource(newFe); } @@ -583,24 +633,33 @@ public class TunerResourceManagerService extends SystemService implements IBinde // If maxSessionNum is 0, removing the Cas Resource. if (maxSessionNum == 0) { removeCasResource(casSystemId); + removeCiCamResource(casSystemId); return; } // If the Cas exists, updates the Cas Resource accordingly. CasResource cas = getCasResource(casSystemId); + CiCamResource ciCam = getCiCamResource(casSystemId); if (cas != null) { if (cas.getUsedSessionNum() > maxSessionNum) { // Sort and release the short number of Cas resources. int releasingCasResourceNum = cas.getUsedSessionNum() - maxSessionNum; - releaseLowerPriorityClientCasResources(releasingCasResourceNum); + // TODO: handle CiCam session update. } cas.updateMaxSessionNum(maxSessionNum); + if (ciCam != null) { + ciCam.updateMaxSessionNum(maxSessionNum); + } return; } // Add the new Cas Resource. cas = new CasResource.Builder(casSystemId) .maxSessionNum(maxSessionNum) .build(); + ciCam = new CiCamResource.Builder(casSystemId) + .maxSessionNum(maxSessionNum) + .build(); addCasResource(cas); + addCiCamResource(ciCam); } @VisibleForTesting @@ -610,13 +669,17 @@ public class TunerResourceManagerService extends SystemService implements IBinde } frontendHandle[0] = TunerResourceManager.INVALID_RESOURCE_HANDLE; - ClientProfile requestClient = getClientProfile(request.getClientId()); + ClientProfile requestClient = getClientProfile(request.clientId); + if (requestClient == null) { + return false; + } + clientPriorityUpdateOnRequest(requestClient); int grantingFrontendHandle = TunerResourceManager.INVALID_RESOURCE_HANDLE; int inUseLowestPriorityFrHandle = TunerResourceManager.INVALID_RESOURCE_HANDLE; // Priority max value is 1000 int currentLowestPriority = MAX_CLIENT_PRIORITY + 1; for (FrontendResource fr : getFrontendResources().values()) { - if (fr.getType() == request.getFrontendType()) { + if (fr.getType() == request.frontendType) { if (!fr.isInUse()) { // Grant unused frontend with no exclusive group members first. if (fr.getExclusiveGroupMemberFeHandles().isEmpty()) { @@ -643,7 +706,7 @@ public class TunerResourceManagerService extends SystemService implements IBinde // Grant frontend when there is unused resource. if (grantingFrontendHandle != TunerResourceManager.INVALID_RESOURCE_HANDLE) { frontendHandle[0] = grantingFrontendHandle; - updateFrontendClientMappingOnNewGrant(grantingFrontendHandle, request.getClientId()); + updateFrontendClientMappingOnNewGrant(grantingFrontendHandle, request.clientId); return true; } @@ -658,7 +721,7 @@ public class TunerResourceManagerService extends SystemService implements IBinde } frontendHandle[0] = inUseLowestPriorityFrHandle; updateFrontendClientMappingOnNewGrant( - inUseLowestPriorityFrHandle, request.getClientId()); + inUseLowestPriorityFrHandle, request.clientId); return true; } @@ -683,7 +746,8 @@ public class TunerResourceManagerService extends SystemService implements IBinde } lnbHandle[0] = TunerResourceManager.INVALID_RESOURCE_HANDLE; - ClientProfile requestClient = getClientProfile(request.getClientId()); + ClientProfile requestClient = getClientProfile(request.clientId); + clientPriorityUpdateOnRequest(requestClient); int grantingLnbHandle = TunerResourceManager.INVALID_RESOURCE_HANDLE; int inUseLowestPriorityLnbHandle = TunerResourceManager.INVALID_RESOURCE_HANDLE; // Priority max value is 1000 @@ -707,7 +771,7 @@ public class TunerResourceManagerService extends SystemService implements IBinde // Grant Lnb when there is unused resource. if (grantingLnbHandle > -1) { lnbHandle[0] = grantingLnbHandle; - updateLnbClientMappingOnNewGrant(grantingLnbHandle, request.getClientId()); + updateLnbClientMappingOnNewGrant(grantingLnbHandle, request.clientId); return true; } @@ -720,7 +784,7 @@ public class TunerResourceManagerService extends SystemService implements IBinde return false; } lnbHandle[0] = inUseLowestPriorityLnbHandle; - updateLnbClientMappingOnNewGrant(inUseLowestPriorityLnbHandle, request.getClientId()); + updateLnbClientMappingOnNewGrant(inUseLowestPriorityLnbHandle, request.clientId); return true; } @@ -732,23 +796,24 @@ public class TunerResourceManagerService extends SystemService implements IBinde if (DEBUG) { Slog.d(TAG, "requestCasSession(request=" + request + ")"); } - CasResource cas = getCasResource(request.getCasSystemId()); + CasResource cas = getCasResource(request.casSystemId); // Unregistered Cas System is treated as having unlimited sessions. if (cas == null) { - cas = new CasResource.Builder(request.getCasSystemId()) + cas = new CasResource.Builder(request.casSystemId) .maxSessionNum(Integer.MAX_VALUE) .build(); addCasResource(cas); } casSessionHandle[0] = TunerResourceManager.INVALID_RESOURCE_HANDLE; - ClientProfile requestClient = getClientProfile(request.getClientId()); + ClientProfile requestClient = getClientProfile(request.clientId); + clientPriorityUpdateOnRequest(requestClient); int lowestPriorityOwnerId = -1; // Priority max value is 1000 int currentLowestPriority = MAX_CLIENT_PRIORITY + 1; if (!cas.isFullyUsed()) { casSessionHandle[0] = generateResourceHandle( TunerResourceManager.TUNER_RESOURCE_TYPE_CAS_SESSION, cas.getSystemId()); - updateCasClientMappingOnNewGrant(request.getCasSystemId(), request.getClientId()); + updateCasClientMappingOnNewGrant(request.casSystemId, request.clientId); return true; } for (int ownerId : cas.getOwnerClientIds()) { @@ -769,7 +834,56 @@ public class TunerResourceManagerService extends SystemService implements IBinde } casSessionHandle[0] = generateResourceHandle( TunerResourceManager.TUNER_RESOURCE_TYPE_CAS_SESSION, cas.getSystemId()); - updateCasClientMappingOnNewGrant(request.getCasSystemId(), request.getClientId()); + updateCasClientMappingOnNewGrant(request.casSystemId, request.clientId); + return true; + } + return false; + } + + @VisibleForTesting + protected boolean requestCiCamInternal(TunerCiCamRequest request, int[] ciCamHandle) { + if (DEBUG) { + Slog.d(TAG, "requestCiCamInternal(TunerCiCamRequest=" + request + ")"); + } + CiCamResource ciCam = getCiCamResource(request.ciCamId); + // Unregistered Cas System is treated as having unlimited sessions. + if (ciCam == null) { + ciCam = new CiCamResource.Builder(request.ciCamId) + .maxSessionNum(Integer.MAX_VALUE) + .build(); + addCiCamResource(ciCam); + } + ciCamHandle[0] = TunerResourceManager.INVALID_RESOURCE_HANDLE; + ClientProfile requestClient = getClientProfile(request.clientId); + clientPriorityUpdateOnRequest(requestClient); + int lowestPriorityOwnerId = -1; + // Priority max value is 1000 + int currentLowestPriority = MAX_CLIENT_PRIORITY + 1; + if (!ciCam.isFullyUsed()) { + ciCamHandle[0] = generateResourceHandle( + TunerResourceManager.TUNER_RESOURCE_TYPE_FRONTEND_CICAM, ciCam.getCiCamId()); + updateCiCamClientMappingOnNewGrant(request.ciCamId, request.clientId); + return true; + } + for (int ownerId : ciCam.getOwnerClientIds()) { + // Record the client id with lowest priority that is using the current Cas system. + int priority = getOwnerClientPriority(ownerId); + if (currentLowestPriority > priority) { + lowestPriorityOwnerId = ownerId; + currentLowestPriority = priority; + } + } + + // When all the CiCam sessions are occupied, reclaim the lowest priority client if the + // request client has higher priority. + if (lowestPriorityOwnerId > -1 && (requestClient.getPriority() > currentLowestPriority)) { + if (!reclaimResource(lowestPriorityOwnerId, + TunerResourceManager.TUNER_RESOURCE_TYPE_FRONTEND_CICAM)) { + return false; + } + ciCamHandle[0] = generateResourceHandle( + TunerResourceManager.TUNER_RESOURCE_TYPE_FRONTEND_CICAM, ciCam.getCiCamId()); + updateCiCamClientMappingOnNewGrant(request.ciCamId, request.clientId); return true; } return false; @@ -790,15 +904,16 @@ public class TunerResourceManagerService extends SystemService implements IBinde return true; } - int challengerPid = challengerProfile.getTvInputSessionId() == null + int challengerPid = challengerProfile.tvInputSessionId == null ? Binder.getCallingPid() /*callingPid*/ - : mTvInputManager.getClientPid(challengerProfile.getTvInputSessionId()); /*tvAppId*/ - int holderPid = holderProfile.getTvInputSessionId() == null + : mTvInputManager.getClientPid(challengerProfile.tvInputSessionId); /*tvAppId*/ + int holderPid = holderProfile.tvInputSessionId == null ? Binder.getCallingPid() /*callingPid*/ - : mTvInputManager.getClientPid(holderProfile.getTvInputSessionId()); /*tvAppId*/ + : mTvInputManager.getClientPid(holderProfile.tvInputSessionId); /*tvAppId*/ - int challengerPriority = getClientPriority(challengerProfile.getUseCase(), challengerPid); - int holderPriority = getClientPriority(holderProfile.getUseCase(), holderPid); + int challengerPriority = getClientPriority( + challengerProfile.useCase, checkIsForeground(challengerPid)); + int holderPriority = getClientPriority(holderProfile.useCase, checkIsForeground(holderPid)); return challengerPriority > holderPriority; } @@ -833,6 +948,14 @@ public class TunerResourceManagerService extends SystemService implements IBinde } @VisibleForTesting + protected void releaseCiCamInternal(CiCamResource ciCam, int ownerClientId) { + if (DEBUG) { + Slog.d(TAG, "releaseCiCamInternal(ciCamId=" + ciCam.getCiCamId() + ")"); + } + updateCiCamClientMappingOnRelease(ciCam, ownerClientId); + } + + @VisibleForTesting protected boolean requestDemuxInternal(TunerDemuxRequest request, int[] demuxHandle) { if (DEBUG) { Slog.d(TAG, "requestDemux(request=" + request + ")"); @@ -843,6 +966,21 @@ public class TunerResourceManagerService extends SystemService implements IBinde } @VisibleForTesting + // This mothod is to sync up the request client's foreground/background status and update + // the client priority accordingly whenever new resource request comes in. + protected void clientPriorityUpdateOnRequest(ClientProfile requestProfile) { + int pid = requestProfile.getProcessId(); + boolean currentIsForeground = checkIsForeground(pid); + if (requestProfile.isForeground() == currentIsForeground) { + // To avoid overriding the priority set through updateClientPriority API. + return; + } + requestProfile.setForeground(currentIsForeground); + requestProfile.setPriority( + getClientPriority(requestProfile.getUseCase(), currentIsForeground)); + } + + @VisibleForTesting protected boolean requestDescramblerInternal( TunerDescramblerRequest request, int[] descramblerHandle) { if (DEBUG) { @@ -933,20 +1071,20 @@ public class TunerResourceManagerService extends SystemService implements IBinde } @VisibleForTesting - protected int getClientPriority(int useCase, int pid) { + protected int getClientPriority(int useCase, boolean isForeground) { if (DEBUG) { Slog.d(TAG, "getClientPriority useCase=" + useCase - + ", pid=" + pid + ")"); + + ", isForeground=" + isForeground + ")"); } - if (isForeground(pid)) { + if (isForeground) { return mPriorityCongfig.getForegroundPriority(useCase); } return mPriorityCongfig.getBackgroundPriority(useCase); } @VisibleForTesting - protected boolean isForeground(int pid) { + protected boolean checkIsForeground(int pid) { if (mActivityManager == null) { return false; } @@ -994,6 +1132,13 @@ public class TunerResourceManagerService extends SystemService implements IBinde ownerProfile.useCas(grantingId); } + private void updateCiCamClientMappingOnNewGrant(int grantingId, int ownerClientId) { + CiCamResource grantingCiCam = getCiCamResource(grantingId); + ClientProfile ownerProfile = getClientProfile(ownerClientId); + grantingCiCam.setOwner(ownerClientId); + ownerProfile.useCiCam(grantingId); + } + private void updateCasClientMappingOnRelease( @NonNull CasResource releasingCas, int ownerClientId) { ClientProfile ownerProfile = getClientProfile(ownerClientId); @@ -1001,6 +1146,13 @@ public class TunerResourceManagerService extends SystemService implements IBinde ownerProfile.releaseCas(); } + private void updateCiCamClientMappingOnRelease( + @NonNull CiCamResource releasingCiCam, int ownerClientId) { + ClientProfile ownerProfile = getClientProfile(ownerClientId); + releasingCiCam.removeOwner(ownerClientId); + ownerProfile.releaseCiCam(); + } + /** * Get the owner client's priority. * @@ -1093,15 +1245,31 @@ public class TunerResourceManagerService extends SystemService implements IBinde } @VisibleForTesting + @Nullable + protected CiCamResource getCiCamResource(int ciCamId) { + return mCiCamResources.get(ciCamId); + } + + @VisibleForTesting protected Map<Integer, CasResource> getCasResources() { return mCasResources; } + @VisibleForTesting + protected Map<Integer, CiCamResource> getCiCamResources() { + return mCiCamResources; + } + private void addCasResource(CasResource newCas) { // Update resource list and available id list mCasResources.put(newCas.getSystemId(), newCas); } + private void addCiCamResource(CiCamResource newCiCam) { + // Update resource list and available id list + mCiCamResources.put(newCiCam.getCiCamId(), newCiCam); + } + private void removeCasResource(int removingId) { CasResource cas = getCasResource(removingId); if (cas == null) { @@ -1113,6 +1281,17 @@ public class TunerResourceManagerService extends SystemService implements IBinde mCasResources.remove(removingId); } + private void removeCiCamResource(int removingId) { + CiCamResource ciCam = getCiCamResource(removingId); + if (ciCam == null) { + return; + } + for (int ownerId : ciCam.getOwnerClientIds()) { + getClientProfile(ownerId).releaseCiCam(); + } + mCiCamResources.remove(removingId); + } + private void releaseLowerPriorityClientCasResources(int releasingCasResourceNum) { // TODO: Sort with a treemap @@ -1161,6 +1340,10 @@ public class TunerResourceManagerService extends SystemService implements IBinde if (profile.getInUseCasSystemId() != ClientProfile.INVALID_RESOURCE_ID) { getCasResource(profile.getInUseCasSystemId()).removeOwner(profile.getId()); } + // Clear CiCam + if (profile.getInUseCiCamId() != ClientProfile.INVALID_RESOURCE_ID) { + getCiCamResource(profile.getInUseCiCamId()).removeOwner(profile.getId()); + } // Clear Frontend clearFrontendAndClientMapping(profile); profile.reclaimAllResources(); diff --git a/services/core/java/com/android/server/wm/ActivityStarter.java b/services/core/java/com/android/server/wm/ActivityStarter.java index c6cc83b4c3bb..7a4bcb1b8c37 100644 --- a/services/core/java/com/android/server/wm/ActivityStarter.java +++ b/services/core/java/com/android/server/wm/ActivityStarter.java @@ -1646,7 +1646,7 @@ class ActivityStarter { if (startedActivityStack != null && startedActivityStack.isAttached() && !startedActivityStack.hasActivity() && !startedActivityStack.isActivityTypeHome()) { - startedActivityStack.removeIfPossible(); + startedActivityStack.removeIfPossible("handleStartResult"); startedActivityStack = null; } return startedActivityStack; @@ -1834,7 +1834,7 @@ class ActivityStarter { return top.getTask(); } else { // Remove the stack if no activity in the stack. - stack.removeIfPossible(); + stack.removeIfPossible("computeTargetTask"); } } return null; diff --git a/services/core/java/com/android/server/wm/ActivityTaskManagerInternal.java b/services/core/java/com/android/server/wm/ActivityTaskManagerInternal.java index 9ffedde8e616..109ea0954be6 100644 --- a/services/core/java/com/android/server/wm/ActivityTaskManagerInternal.java +++ b/services/core/java/com/android/server/wm/ActivityTaskManagerInternal.java @@ -285,6 +285,14 @@ public abstract class ActivityTaskManagerInternal { public abstract void enforceCallerIsRecentsOrHasPermission(String permission, String func); /** + * Returns true if the app can close system dialogs. Otherwise it either throws a {@link + * SecurityException} or returns false with a logcat message depending on whether the app + * targets SDK level {@link android.os.Build.VERSION_CODES#S} or not. + */ + public abstract boolean checkCanCloseSystemDialogs(int pid, int uid, + @Nullable String packageName); + + /** * Called after the voice interaction service has changed. */ public abstract void notifyActiveVoiceInteractionServiceChanged(ComponentName component); @@ -563,8 +571,13 @@ public abstract class ActivityTaskManagerInternal { */ public abstract void setDeviceOwnerUid(int uid); - /** Set all associated companion app that belongs to an userId. */ - public abstract void setCompanionAppPackages(int userId, Set<String> companionAppPackages); + /** + * Set all associated companion app that belongs to a userId. + * @param userId + * @param companionAppUids ActivityTaskManager will take ownership of this Set, the caller + * shouldn't touch the Set after calling this interface. + */ + public abstract void setCompanionAppUids(int userId, Set<Integer> companionAppUids); /** * @param packageName The package to check diff --git a/services/core/java/com/android/server/wm/ActivityTaskManagerService.java b/services/core/java/com/android/server/wm/ActivityTaskManagerService.java index 461bbfb978e4..039f22da86d8 100644 --- a/services/core/java/com/android/server/wm/ActivityTaskManagerService.java +++ b/services/core/java/com/android/server/wm/ActivityTaskManagerService.java @@ -147,6 +147,7 @@ import android.app.WindowConfiguration; import android.app.admin.DevicePolicyCache; import android.app.assist.AssistContent; import android.app.assist.AssistStructure; +import android.app.compat.CompatChanges; import android.app.usage.UsageStatsManagerInternal; import android.content.ActivityNotFoundException; import android.content.ComponentName; @@ -2900,6 +2901,86 @@ public class ActivityTaskManagerService extends IActivityTaskManager.Stub { } } + /** + * Returns true if the app can close system dialogs. Otherwise it either throws a {@link + * SecurityException} or returns false with a logcat message depending on whether the app + * targets SDK level {@link android.os.Build.VERSION_CODES#S} or not. + */ + private boolean checkCanCloseSystemDialogs(int pid, int uid, @Nullable String packageName) { + final WindowProcessController process; + synchronized (mGlobalLock) { + process = mProcessMap.getProcess(pid); + } + if (packageName == null && process != null) { + // WindowProcessController.mInfo is final, so after the synchronized memory barrier + // above, process.mInfo can't change. As for reading mInfo.packageName, + // WindowProcessController doesn't own the ApplicationInfo object referenced by mInfo. + // ProcessRecord for example also holds a reference to that object, so protecting access + // to packageName with the WM lock would not be enough as we'd also need to synchronize + // on the AM lock if we are worried about races, but we can't synchronize on AM lock + // here. Hence, since this is only used for logging, we don't synchronize here. + packageName = process.mInfo.packageName; + } + String caller = "(pid=" + pid + ", uid=" + uid + ")"; + if (packageName != null) { + caller = packageName + " " + caller; + } + if (!canCloseSystemDialogs(pid, uid, process)) { + // The app can't close system dialogs, throw only if it targets S+ + if (CompatChanges.isChangeEnabled( + ActivityManager.LOCK_DOWN_CLOSE_SYSTEM_DIALOGS, uid)) { + throw new SecurityException( + "Permission Denial: " + Intent.ACTION_CLOSE_SYSTEM_DIALOGS + + " broadcast from " + caller + " requires " + + Manifest.permission.BROADCAST_CLOSE_SYSTEM_DIALOGS + "."); + } else if (CompatChanges.isChangeEnabled( + ActivityManager.DROP_CLOSE_SYSTEM_DIALOGS, uid)) { + Slog.e(TAG, + "Permission Denial: " + Intent.ACTION_CLOSE_SYSTEM_DIALOGS + + " broadcast from " + caller + " requires " + + Manifest.permission.BROADCAST_CLOSE_SYSTEM_DIALOGS + + ", dropping broadcast."); + return false; + } else { + Slog.w(TAG, Intent.ACTION_CLOSE_SYSTEM_DIALOGS + + " broadcast from " + caller + " will require " + + Manifest.permission.BROADCAST_CLOSE_SYSTEM_DIALOGS + + " in future builds."); + return true; + } + } + return true; + } + + private boolean canCloseSystemDialogs(int pid, int uid, + @Nullable WindowProcessController process) { + if (checkPermission(Manifest.permission.BROADCAST_CLOSE_SYSTEM_DIALOGS, pid, uid) + == PERMISSION_GRANTED) { + return true; + } + if (process != null) { + // Check if the instrumentation of the process has the permission. This covers the + // usual test started from the shell (which has the permission) case. This is needed + // for apps targeting SDK level < S but we are also allowing for targetSdk S+ as a + // convenience to avoid breaking a bunch of existing tests and asking them to adopt + // shell permissions to do this. + // Note that these getters all read from volatile fields in WindowProcessController, so + // no need to lock. + int sourceUid = process.getInstrumentationSourceUid(); + if (process.isInstrumenting() && sourceUid != -1 && checkPermission( + Manifest.permission.BROADCAST_CLOSE_SYSTEM_DIALOGS, -1, sourceUid) + == PERMISSION_GRANTED) { + return true; + } + // This is the notification trampoline use-case for example, where apps use Intent.ACSD + // to close the shade prior to starting an activity. + if (process.canCloseSystemDialogsByToken()) { + return true; + } + } + return false; + } + static void enforceTaskPermission(String func) { if (checkCallingPermission(MANAGE_ACTIVITY_TASKS) == PackageManager.PERMISSION_GRANTED) { return; @@ -5150,6 +5231,12 @@ public class ActivityTaskManagerService extends IActivityTaskManager.Stub { } @Override + public boolean checkCanCloseSystemDialogs(int pid, int uid, @Nullable String packageName) { + return ActivityTaskManagerService.this.checkCanCloseSystemDialogs(pid, uid, + packageName); + } + + @Override public void notifyActiveVoiceInteractionServiceChanged(ComponentName component) { synchronized (mGlobalLock) { mActiveVoiceInteractionServiceComponent = component; @@ -5649,9 +5736,12 @@ public class ActivityTaskManagerService extends IActivityTaskManager.Stub { @Override public void closeSystemDialogs(String reason) { enforceNotIsolatedCaller("closeSystemDialogs"); - final int pid = Binder.getCallingPid(); final int uid = Binder.getCallingUid(); + if (!checkCanCloseSystemDialogs(pid, uid, null)) { + return; + } + final long origId = Binder.clearCallingIdentity(); try { synchronized (mGlobalLock) { @@ -6272,17 +6362,9 @@ public class ActivityTaskManagerService extends IActivityTaskManager.Stub { } @Override - public void setCompanionAppPackages(int userId, Set<String> companionAppPackages) { - // Translate package names into UIDs - final Set<Integer> result = new HashSet<>(); - for (String pkg : companionAppPackages) { - final int uid = getPackageManagerInternalLocked().getPackageUid(pkg, 0, userId); - if (uid >= 0) { - result.add(uid); - } - } + public void setCompanionAppUids(int userId, Set<Integer> companionAppUids) { synchronized (mGlobalLock) { - mCompanionAppUidsMap.put(userId, result); + mCompanionAppUidsMap.put(userId, companionAppUids); } } diff --git a/services/core/java/com/android/server/wm/DisplayContent.java b/services/core/java/com/android/server/wm/DisplayContent.java index c3a754279f80..5d79eb8d7a61 100644 --- a/services/core/java/com/android/server/wm/DisplayContent.java +++ b/services/core/java/com/android/server/wm/DisplayContent.java @@ -3024,7 +3024,7 @@ class DisplayContent extends RootDisplayArea implements WindowManagerPolicy.Disp public void dump(PrintWriter pw, String prefix, boolean dumpAll) { super.dump(pw, prefix, dumpAll); pw.print(prefix); - pw.println("Display: mDisplayId=" + mDisplayId + " stacks=" + getRootTaskCount()); + pw.println("Display: mDisplayId=" + mDisplayId + " rootTasks=" + getRootTaskCount()); final String subPrefix = " " + prefix; pw.print(subPrefix); pw.print("init="); pw.print(mInitialDisplayWidth); pw.print("x"); pw.print(mInitialDisplayHeight); pw.print(" "); pw.print(mInitialDisplayDensity); @@ -3107,30 +3107,31 @@ class DisplayContent extends RootDisplayArea implements WindowManagerPolicy.Disp pw.println(); - // Dump stack references - final Task homeStack = getDefaultTaskDisplayArea().getRootHomeTask(); - if (homeStack != null) { - pw.println(prefix + "homeStack=" + homeStack.getName()); + // Dump root task references + final Task rootHomeTask = getDefaultTaskDisplayArea().getRootHomeTask(); + if (rootHomeTask != null) { + pw.println(prefix + "rootHomeTask=" + rootHomeTask.getName()); } - final Task pinnedStack = getDefaultTaskDisplayArea().getRootPinnedTask(); - if (pinnedStack != null) { - pw.println(prefix + "pinnedStack=" + pinnedStack.getName()); + final Task rootPinnedTask = getDefaultTaskDisplayArea().getRootPinnedTask(); + if (rootPinnedTask != null) { + pw.println(prefix + "rootPinnedTask=" + rootPinnedTask.getName()); } - final Task splitScreenPrimaryStack = getDefaultTaskDisplayArea() + final Task rootSplitScreenPrimaryTask = getDefaultTaskDisplayArea() .getRootSplitScreenPrimaryTask(); - if (splitScreenPrimaryStack != null) { - pw.println(prefix + "splitScreenPrimaryStack=" + splitScreenPrimaryStack.getName()); + if (rootSplitScreenPrimaryTask != null) { + pw.println( + prefix + "rootSplitScreenPrimaryTask=" + rootSplitScreenPrimaryTask.getName()); } // TODO: Support recents on non-default task containers - final Task recentsStack = getDefaultTaskDisplayArea().getRootTask( + final Task rootRecentsTask = getDefaultTaskDisplayArea().getRootTask( WINDOWING_MODE_UNDEFINED, ACTIVITY_TYPE_RECENTS); - if (recentsStack != null) { - pw.println(prefix + "recentsStack=" + recentsStack.getName()); + if (rootRecentsTask != null) { + pw.println(prefix + "rootRecentsTask=" + rootRecentsTask.getName()); } - final Task dreamStack = + final Task rootDreamTask = getRootTask(WINDOWING_MODE_UNDEFINED, ACTIVITY_TYPE_DREAM); - if (dreamStack != null) { - pw.println(prefix + "dreamStack=" + dreamStack.getName()); + if (rootDreamTask != null) { + pw.println(prefix + "rootDreamTask=" + rootDreamTask.getName()); } pw.println(); @@ -3150,7 +3151,7 @@ class DisplayContent extends RootDisplayArea implements WindowManagerPolicy.Disp @Override public String toString() { - return "Display " + mDisplayId + " info=" + mDisplayInfo + " stacks=" + mChildren; + return "Display " + mDisplayId + " info=" + mDisplayInfo + " rootTasks=" + mChildren; } String getName() { @@ -5490,7 +5491,7 @@ class DisplayContent extends RootDisplayArea implements WindowManagerPolicy.Disp if (!hasNonEmptyHomeRootTask && getRootTaskCount() > 0) { // Release this display if only empty home root task(s) are left. This display will be // released along with the root task(s) removal. - forAllRootTasks(Task::removeIfPossible); + forAllRootTasks(t -> {t.removeIfPossible("releaseSelfIfNeeded");}); } else if (getTopRootTask() == null) { removeIfPossible(); mRootWindowContainer.mTaskSupervisor diff --git a/services/core/java/com/android/server/wm/EventLogTags.logtags b/services/core/java/com/android/server/wm/EventLogTags.logtags index 6c346090578f..40b80f75c814 100644 --- a/services/core/java/com/android/server/wm/EventLogTags.logtags +++ b/services/core/java/com/android/server/wm/EventLogTags.logtags @@ -35,22 +35,18 @@ option java_package com.android.server.wm 30019 wm_relaunch_resume_activity (User|1|5),(Token|1|5),(Task ID|1|5),(Component Name|3) # An activity has been relaunched: 30020 wm_relaunch_activity (User|1|5),(Token|1|5),(Task ID|1|5),(Component Name|3) -# The activity's onPause has been called. -30021 wm_on_paused_called (Token|1|5),(Component Name|3),(Reason|3) -# The activity's onResume has been called. -30022 wm_on_resume_called (Token|1|5),(Component Name|3),(Reason|3) # Activity set to resumed 30043 wm_set_resumed_activity (User|1|5),(Component Name|3),(Reason|3) -# Stack focus -30044 wm_focused_stack (User|1|5),(Display Id|1|5),(Focused Stack Id|1|5),(Last Focused Stack Id|1|5),(Reason|3) +# Root task focus +30044 wm_focused_root_task (User|1|5),(Display Id|1|5),(Focused Root Task Id|1|5),(Last Focused Root Task Id|1|5),(Reason|3) # Attempting to stop an activity 30048 wm_stop_activity (User|1|5),(Token|1|5),(Component Name|3) -# The task is being removed from its parent stack -30061 wm_remove_task (Task ID|1|5), (Stack ID|1|5) +# The task is being removed from its parent task +30061 wm_remove_task (Task ID|1|5), (Root Task ID|1|5) # An activity been add into stopping list 30066 wm_add_to_stopping (User|1|5),(Token|1|5),(Component Name|3),(Reason|3) @@ -61,17 +57,11 @@ option java_package com.android.server.wm # Out of memory for surfaces. 31000 wm_no_surface_memory (Window|3),(PID|1|5),(Operation|3) # Task created. -31001 wm_task_created (TaskId|1|5),(StackId|1|5) +31001 wm_task_created (TaskId|1|5),(RootTaskId|1|5) # Task moved to top (1) or bottom (0). 31002 wm_task_moved (TaskId|1|5),(ToTop|1),(Index|1) # Task removed with source explanation. 31003 wm_task_removed (TaskId|1|5),(Reason|3) -# Stack created. -31004 wm_stack_created (StackId|1|5) -# Home stack moved to top (1) or bottom (0). -31005 wm_home_stack_moved (ToTop|1) -# Stack removed. -31006 wm_stack_removed (StackId|1|5) # bootanim finished: 31007 wm_boot_animation_done (time|2|3) diff --git a/services/core/java/com/android/server/wm/RootWindowContainer.java b/services/core/java/com/android/server/wm/RootWindowContainer.java index 54996a66ecd7..0a95f8f852a9 100644 --- a/services/core/java/com/android/server/wm/RootWindowContainer.java +++ b/services/core/java/com/android/server/wm/RootWindowContainer.java @@ -861,6 +861,9 @@ class RootWindowContainer extends WindowContainer<DisplayContent> "<<< CLOSE TRANSACTION performLayoutAndPlaceSurfaces"); } } + + // Send any pending task-info changes that were queued-up during a layout deferment + mWmService.mAtmService.mTaskOrganizerController.dispatchPendingEvents(); mWmService.mAnimator.executeAfterPrepareSurfacesRunnables(); checkAppTransitionReady(surfacePlacer); @@ -1013,9 +1016,6 @@ class RootWindowContainer extends WindowContainer<DisplayContent> mWmService.scheduleAnimationLocked(); - // Send any pending task-info changes that were queued-up during a layout deferment - mWmService.mAtmService.mTaskOrganizerController.dispatchPendingTaskInfoChanges(); - if (DEBUG_WINDOW_TRACE) Slog.e(TAG, "performSurfacePlacementInner exit"); } @@ -3572,7 +3572,7 @@ class RootWindowContainer extends WindowContainer<DisplayContent> public void dump(PrintWriter pw, String prefix, boolean dumpAll) { super.dump(pw, prefix, dumpAll); pw.print(prefix); - pw.println("topDisplayFocusedStack=" + getTopDisplayFocusedRootTask()); + pw.println("topDisplayFocusedRootTask=" + getTopDisplayFocusedRootTask()); for (int i = getChildCount() - 1; i >= 0; --i) { final DisplayContent display = getChildAt(i); display.dump(pw, prefix, dumpAll); diff --git a/services/core/java/com/android/server/wm/Task.java b/services/core/java/com/android/server/wm/Task.java index a88e4537d1a2..260b6c55b4db 100644 --- a/services/core/java/com/android/server/wm/Task.java +++ b/services/core/java/com/android/server/wm/Task.java @@ -916,21 +916,26 @@ class Task extends WindowContainer<WindowContainer> { mTaskSupervisor.mRecentTasks.remove(this); } - removeIfPossible(); + removeIfPossible("cleanUpResourcesForDestroy"); } @VisibleForTesting @Override void removeIfPossible() { + removeIfPossible("removeTaskIfPossible"); + } + + void removeIfPossible(String reason) { final boolean isRootTask = isRootTask(); if (!isRootTask) { mAtmService.getLockTaskController().clearLockedTask(this); } if (shouldDeferRemoval()) { - if (DEBUG_ROOT_TASK) Slog.i(TAG, "removeTask: deferring removing taskId=" + mTaskId); + if (DEBUG_ROOT_TASK) Slog.i(TAG, + "removeTask:" + reason + " deferring removing taskId=" + mTaskId); return; } - removeImmediately(); + removeImmediately(reason); if (isLeafTask()) { mAtmService.getTaskChangeNotificationController().notifyTaskRemoved(mTaskId); @@ -1402,7 +1407,6 @@ class Task extends WindowContainer<WindowContainer> { // from the display, so we should probably consolidate it there instead. if (getParent() == null && mDisplayContent != null) { - EventLogTags.writeWmStackRemoved(getRootTaskId()); mDisplayContent = null; mWmService.mWindowPlacerLocked.requestTraversal(); } @@ -1774,8 +1778,8 @@ class Task extends WindowContainer<WindowContainer> { getRootTask().removeChild(this, reason); } EventLogTags.writeWmTaskRemoved(mTaskId, - "removeChild: last r=" + r + " in t=" + this); - removeIfPossible(); + "removeChild:" + reason + " last r=" + r + " in t=" + this); + removeIfPossible(reason); } } @@ -1818,7 +1822,7 @@ class Task extends WindowContainer<WindowContainer> { if (r.finishing) return; // Task was restored from persistent storage. r.takeFromHistory(); - removeChild(r); + removeChild(r, reason); }); } else { forAllActivities((r) -> { @@ -3214,8 +3218,12 @@ class Task extends WindowContainer<WindowContainer> { @Override void removeImmediately() { - if (DEBUG_ROOT_TASK) Slog.i(TAG, "removeTask: removing taskId=" + mTaskId); - EventLogTags.writeWmTaskRemoved(mTaskId, "removeTask"); + removeImmediately("removeTask"); + } + + void removeImmediately(String reason) { + if (DEBUG_ROOT_TASK) Slog.i(TAG, "removeTask:" + reason + " removing taskId=" + mTaskId); + EventLogTags.writeWmTaskRemoved(mTaskId, reason); // If applicable let the TaskOrganizer know the Task is vanishing. setTaskOrganizer(null); @@ -4404,7 +4412,8 @@ class Task extends WindowContainer<WindowContainer> { if (mRootProcess != null) { pw.print(prefix); pw.print("mRootProcess="); pw.println(mRootProcess); } - pw.print(prefix); pw.print("taskId=" + mTaskId); pw.println(" stackId=" + getRootTaskId()); + pw.print(prefix); pw.print("taskId=" + mTaskId); + pw.println(" rootTaskId=" + getRootTaskId()); pw.print(prefix); pw.print("mHasBeenVisible="); pw.println(getHasBeenVisible()); pw.print(prefix); pw.print("mResizeMode="); pw.print(ActivityInfo.resizeModeToString(mResizeMode)); @@ -4983,10 +4992,9 @@ class Task extends WindowContainer<WindowContainer> { } } else { // No longer managed by any organizer. - mTaskAppearedSent = false; setForceHidden(FLAG_FORCE_HIDDEN_FOR_TASK_ORG, false /* set */); if (mCreatedByOrganizer) { - removeImmediately(); + removeImmediately("setTaskOrganizer"); } } @@ -5008,11 +5016,6 @@ class Task extends WindowContainer<WindowContainer> { * @return {@code true} if task organizer changed. */ boolean updateTaskOrganizerState(boolean forceUpdate, boolean skipTaskAppeared) { - if (getSurfaceControl() == null) { - // Can't call onTaskAppeared without a surfacecontrol, so defer this until after one - // is created. - return false; - } if (!canBeOrganized()) { return setTaskOrganizer(null); } @@ -5022,6 +5025,10 @@ class Task extends WindowContainer<WindowContainer> { final ITaskOrganizer organizer = controller.getTaskOrganizer(windowingMode); if (!forceUpdate && mTaskOrganizer == organizer) { return false; + } else if (organizer != null && getSurfaceControl() == null) { + // Can't call onTaskAppeared without a surfacecontrol, so defer this until after one + // is created. + return false; } return setTaskOrganizer(organizer, skipTaskAppeared); } @@ -6708,7 +6715,7 @@ class Task extends WindowContainer<WindowContainer> { /** Finish all activities in the stack without waiting. */ void finishAllActivitiesImmediately() { if (!hasChild()) { - removeIfPossible(); + removeIfPossible("finishAllActivitiesImmediately"); return; } forAllActivities((r) -> { @@ -7152,7 +7159,7 @@ class Task extends WindowContainer<WindowContainer> { if (needSep) { pw.println(); } - pw.println(" Stack #" + getRootTaskId() + pw.println(" RootTask #" + getRootTaskId() + ": type=" + activityTypeToString(getActivityType()) + " mode=" + windowingModeToString(getWindowingMode())); pw.println(" isSleeping=" + shouldSleepActivities()); @@ -7662,7 +7669,7 @@ class Task extends WindowContainer<WindowContainer> { void dispatchTaskInfoChangedIfNeeded(boolean force) { if (isOrganized()) { - mAtmService.mTaskOrganizerController.dispatchTaskInfoChanged(this, force); + mAtmService.mTaskOrganizerController.onTaskInfoChanged(this, force); } } @@ -8002,7 +8009,7 @@ class Task extends WindowContainer<WindowContainer> { // Task created by organizer are added as root. final Task launchRootTask = mCreatedByOrganizer - ? null : tda.updateLaunchRootTask(mWindowingMode); + ? null : tda.getLaunchRootTask(mWindowingMode, mActivityType); if (launchRootTask != null) { // Since this task will be put into a root task, its windowingMode will be // inherited. diff --git a/services/core/java/com/android/server/wm/TaskDisplayArea.java b/services/core/java/com/android/server/wm/TaskDisplayArea.java index 3f4150b21178..832fc688d6eb 100644 --- a/services/core/java/com/android/server/wm/TaskDisplayArea.java +++ b/services/core/java/com/android/server/wm/TaskDisplayArea.java @@ -28,7 +28,6 @@ import static android.app.WindowConfiguration.WINDOWING_MODE_PINNED; import static android.app.WindowConfiguration.WINDOWING_MODE_SPLIT_SCREEN_PRIMARY; import static android.app.WindowConfiguration.WINDOWING_MODE_SPLIT_SCREEN_SECONDARY; import static android.app.WindowConfiguration.WINDOWING_MODE_UNDEFINED; -import static android.app.WindowConfiguration.isSplitScreenWindowingMode; import static android.content.pm.ActivityInfo.SCREEN_ORIENTATION_BEHIND; import static android.content.pm.ActivityInfo.SCREEN_ORIENTATION_UNSET; import static android.content.pm.ActivityInfo.SCREEN_ORIENTATION_UNSPECIFIED; @@ -54,6 +53,7 @@ import android.window.WindowContainerTransaction; import com.android.internal.annotations.VisibleForTesting; import com.android.internal.protolog.common.ProtoLog; +import com.android.internal.util.ArrayUtils; import com.android.internal.util.ToBooleanFunction; import com.android.internal.util.function.pooled.PooledLambda; import com.android.internal.util.function.pooled.PooledPredicate; @@ -117,8 +117,18 @@ final class TaskDisplayArea extends DisplayArea<WindowContainer> { private RootWindowContainer mRootWindowContainer; - // When non-null, new tasks get put into this root task. - Task mLaunchRootTask = null; + // Launch root tasks by activityType then by windowingMode. + static private class LaunchRootTaskDef { + Task task; + int[] windowingModes; + int[] activityTypes; + + boolean contains(int windowingMode, int activityType) { + return ArrayUtils.contains(windowingModes, windowingMode) + && ArrayUtils.contains(activityTypes, activityType); + } + } + private final ArrayList<LaunchRootTaskDef> mLaunchRootTasks = new ArrayList<>(); /** * A focusable stack that is purposely to be positioned at the top. Although the stack may not @@ -1017,7 +1027,7 @@ final class TaskDisplayArea extends DisplayArea<WindowContainer> { } else if (candidateTask != null) { final Task stack = candidateTask; final int position = onTop ? POSITION_TOP : POSITION_BOTTOM; - Task launchRootTask = updateLaunchRootTask(windowingMode); + final Task launchRootTask = getLaunchRootTask(windowingMode, activityType); if (launchRootTask != null) { if (stack.getParent() == null) { @@ -1096,40 +1106,41 @@ final class TaskDisplayArea extends DisplayArea<WindowContainer> { .build(); } - /** @return the root task to create the next task in. */ - Task updateLaunchRootTask(int windowingMode) { - if (!isSplitScreenWindowingMode(windowingMode)) { - // Only split-screen windowing modes can do this currently... - return null; + // TODO: Also clear when task is removed from system? + void setLaunchRootTask(Task rootTask, int[] windowingModes, int[] activityTypes) { + if (!rootTask.mCreatedByOrganizer) { + throw new IllegalArgumentException( + "Can't set not mCreatedByOrganizer as launch root tr=" + rootTask); } - for (int i = mChildren.size() - 1; i >= 0; --i) { - final WindowContainer child = mChildren.get(i); - if (child.asTaskDisplayArea() != null) { - final Task t = child.asTaskDisplayArea().updateLaunchRootTask(windowingMode); - if (t != null) { - return t; - } - continue; - } - final Task t = mChildren.get(i).asTask(); - if (t == null || !t.mCreatedByOrganizer - || t.getRequestedOverrideWindowingMode() != windowingMode) { - continue; - } - // If not already set, pick a launch root which is not the one we are launching into. - if (mLaunchRootTask == null) { - for (int j = 0, n = mChildren.size(); j < n; ++j) { - final Task tt = mChildren.get(j).asTask(); - if (tt != null && tt.mCreatedByOrganizer && tt != t) { - mLaunchRootTask = tt; - break; - } - } + LaunchRootTaskDef def = null; + for (int i = mLaunchRootTasks.size() - 1; i >= 0; --i) { + if (mLaunchRootTasks.get(i).task.mTaskId != rootTask.mTaskId) continue; + def = mLaunchRootTasks.get(i); + } + + if (def != null) { + // Remove so we add to the end of the list. + mLaunchRootTasks.remove(def); + } else { + def = new LaunchRootTaskDef(); + def.task = rootTask; + } + + def.activityTypes = activityTypes; + def.windowingModes = windowingModes; + if (!ArrayUtils.isEmpty(windowingModes) || !ArrayUtils.isEmpty(activityTypes)) { + mLaunchRootTasks.add(def); + } + } + + Task getLaunchRootTask(int windowingMode, int activityType) { + for (int i = mLaunchRootTasks.size() - 1; i >= 0; --i) { + if (mLaunchRootTasks.get(i).contains(windowingMode, activityType)) { + return mLaunchRootTasks.get(i).task; } - return t; } - return mLaunchRootTask; + return null; } /** @@ -1248,7 +1259,7 @@ final class TaskDisplayArea extends DisplayArea<WindowContainer> { } mLastFocusedRootTask = prevFocusedTask; - EventLogTags.writeWmFocusedStack(mRootWindowContainer.mCurrentUser, + EventLogTags.writeWmFocusedRootTask(mRootWindowContainer.mCurrentUser, mDisplayContent.mDisplayId, currentFocusedTask == null ? -1 : currentFocusedTask.getRootTaskId(), mLastFocusedRootTask == null ? -1 : mLastFocusedRootTask.getRootTaskId(), @@ -1321,7 +1332,6 @@ final class TaskDisplayArea extends DisplayArea<WindowContainer> { void onSplitScreenModeDismissed(Task toTop) { mAtmService.deferWindowLayout(); try { - mLaunchRootTask = null; moveSplitScreenTasksToFullScreen(); } finally { final Task topFullscreenStack = toTop != null @@ -1919,7 +1929,20 @@ final class TaskDisplayArea extends DisplayArea<WindowContainer> { if (mLastFocusedRootTask != null) { pw.println(doublePrefix + "mLastFocusedRootTask=" + mLastFocusedRootTask); } + final String triplePrefix = doublePrefix + " "; + + if (mLaunchRootTasks.size() > 0) { + pw.println(doublePrefix + "mLaunchRootTasks:"); + for (int i = mLaunchRootTasks.size() - 1; i >= 0; --i) { + final LaunchRootTaskDef def = mLaunchRootTasks.get(i); + pw.println(triplePrefix + + def.activityTypes + " " + + def.windowingModes + " " + + " task=" + def.task); + } + } + pw.println(doublePrefix + "Application tokens in top down Z order:"); for (int index = getChildCount() - 1; index >= 0; --index) { final WindowContainer child = getChildAt(index); diff --git a/services/core/java/com/android/server/wm/TaskOrganizerController.java b/services/core/java/com/android/server/wm/TaskOrganizerController.java index 089071f5de54..383dfd48a07a 100644 --- a/services/core/java/com/android/server/wm/TaskOrganizerController.java +++ b/services/core/java/com/android/server/wm/TaskOrganizerController.java @@ -151,27 +151,23 @@ class TaskOrganizerController extends ITaskOrganizerController.Stub { ProtoLog.v(WM_DEBUG_WINDOW_ORGANIZER, "Task appeared taskId=%d", task.mTaskId); final boolean visible = task.isVisible(); final RunningTaskInfo taskInfo = task.getTaskInfo(); - mDeferTaskOrgCallbacksConsumer.accept(() -> { - try { - mTaskOrganizer.onTaskAppeared(taskInfo, prepareLeash(task, visible, - "TaskOrganizerController.onTaskAppeared")); - } catch (RemoteException e) { - Slog.e(TAG, "Exception sending onTaskAppeared callback", e); - } - }); + try { + mTaskOrganizer.onTaskAppeared(taskInfo, prepareLeash(task, visible, + "TaskOrganizerController.onTaskAppeared")); + } catch (RemoteException e) { + Slog.e(TAG, "Exception sending onTaskAppeared callback", e); + } } void onTaskVanished(Task task) { ProtoLog.v(WM_DEBUG_WINDOW_ORGANIZER, "Task vanished taskId=%d", task.mTaskId); final RunningTaskInfo taskInfo = task.getTaskInfo(); - mDeferTaskOrgCallbacksConsumer.accept(() -> { - try { - mTaskOrganizer.onTaskVanished(taskInfo); - } catch (RemoteException e) { - Slog.e(TAG, "Exception sending onTaskVanished callback", e); - } - }); + try { + mTaskOrganizer.onTaskVanished(taskInfo); + } catch (RemoteException e) { + Slog.e(TAG, "Exception sending onTaskVanished callback", e); + } } void onTaskInfoChanged(Task task, ActivityManager.RunningTaskInfo taskInfo) { @@ -180,20 +176,18 @@ class TaskOrganizerController extends ITaskOrganizerController.Stub { return; } ProtoLog.v(WM_DEBUG_WINDOW_ORGANIZER, "Task info changed taskId=%d", task.mTaskId); - mDeferTaskOrgCallbacksConsumer.accept(() -> { - if (!task.isOrganized()) { - // This is safe to ignore if the task is no longer organized - return; - } - try { - // Purposely notify of task info change immediately instead of deferring (like - // appear and vanish) to allow info changes (such as new PIP params) to flow - // without waiting. - mTaskOrganizer.onTaskInfoChanged(taskInfo); - } catch (RemoteException e) { - Slog.e(TAG, "Exception sending onTaskInfoChanged callback", e); - } - }); + if (!task.isOrganized()) { + // This is safe to ignore if the task is no longer organized + return; + } + try { + // Purposely notify of task info change immediately instead of deferring (like + // appear and vanish) to allow info changes (such as new PIP params) to flow + // without waiting. + mTaskOrganizer.onTaskInfoChanged(taskInfo); + } catch (RemoteException e) { + Slog.e(TAG, "Exception sending onTaskInfoChanged callback", e); + } } void onBackPressedOnTaskRoot(Task task) { @@ -203,17 +197,15 @@ class TaskOrganizerController extends ITaskOrganizerController.Stub { // Skip if the task has not yet received taskAppeared(). return; } - mDeferTaskOrgCallbacksConsumer.accept(() -> { - if (!task.isOrganized()) { - // This is safe to ignore if the task is no longer organized - return; - } - try { - mTaskOrganizer.onBackPressedOnTaskRoot(task.getTaskInfo()); - } catch (Exception e) { - Slog.e(TAG, "Exception sending onBackPressedOnTaskRoot callback", e); - } - }); + if (!task.isOrganized()) { + // This is safe to ignore if the task is no longer organized + return; + } + try { + mTaskOrganizer.onBackPressedOnTaskRoot(task.getTaskInfo()); + } catch (Exception e) { + Slog.e(TAG, "Exception sending onBackPressedOnTaskRoot callback", e); + } } } @@ -258,28 +250,34 @@ class TaskOrganizerController extends ITaskOrganizerController.Stub { return mOrganizer.prepareLeash(t, t.isVisible(), reason); } - void addTask(Task t) { - if (t.mTaskAppearedSent) return; + private boolean addTask(Task t) { + if (t.mTaskAppearedSent) { + return false; + } if (!mOrganizedTasks.contains(t)) { mOrganizedTasks.add(t); } + if (t.taskAppearedReady()) { t.mTaskAppearedSent = true; - mOrganizer.onTaskAppeared(t); + return true; } + return false; } - void removeTask(Task t) { + private boolean removeTask(Task t) { + mOrganizedTasks.remove(t); + mInterceptBackPressedOnRootTasks.remove(t.mTaskId); + if (t.mTaskAppearedSent) { if (t.getSurfaceControl() != null) { t.migrateToNewSurfaceControl(); } t.mTaskAppearedSent = false; - mOrganizer.onTaskVanished(t); + return true; } - mOrganizedTasks.remove(t); - mInterceptBackPressedOnRootTasks.remove(t.mTaskId); + return false; } void dispose() { @@ -291,7 +289,7 @@ class TaskOrganizerController extends ITaskOrganizerController.Stub { while (!mOrganizedTasks.isEmpty()) { final Task t = mOrganizedTasks.get(0); if (!t.updateTaskOrganizerState(true /* forceUpdate */)) { - removeTask(t); + TaskOrganizerController.this.onTaskVanished(mOrganizer.mTaskOrganizer, t); } } @@ -305,6 +303,33 @@ class TaskOrganizerController extends ITaskOrganizerController.Stub { } } + static class PendingTaskEvent { + static final int EVENT_APPEARED = 0; + static final int EVENT_VANISHED = 1; + static final int EVENT_INFO_CHANGED = 2; + static final int EVENT_ROOT_BACK_PRESSED = 3; + + final int mEventType; + final Task mTask; + final ITaskOrganizer mTaskOrg; + boolean mForce; + + PendingTaskEvent(Task task, int event) { + this(task, task.mTaskOrganizer, event); + } + + PendingTaskEvent(Task task, ITaskOrganizer taskOrg, int eventType) { + mTask = task; + mTaskOrg = taskOrg; + mEventType = eventType; + } + + boolean isLifecycleEvent() { + return mEventType == EVENT_APPEARED || mEventType == EVENT_VANISHED + || mEventType == EVENT_INFO_CHANGED; + } + } + private final ActivityTaskManagerService mService; private final WindowManagerGlobalLock mGlobalLock; @@ -312,7 +337,8 @@ class TaskOrganizerController extends ITaskOrganizerController.Stub { private final LinkedList<ITaskOrganizer> mTaskOrganizers = new LinkedList<>(); private final HashMap<IBinder, TaskOrganizerState> mTaskOrganizerStates = new HashMap<>(); private final WeakHashMap<Task, RunningTaskInfo> mLastSentTaskInfos = new WeakHashMap<>(); - private final ArrayList<Task> mPendingTaskInfoChanges = new ArrayList<>(); + // Pending task events due to layout deferred. + private final ArrayList<PendingTaskEvent> mPendingTaskEvents = new ArrayList<>(); // Set of organized tasks (by taskId) that dispatch back pressed to their organizers private final HashSet<Integer> mInterceptBackPressedOnRootTasks = new HashSet(); @@ -337,6 +363,12 @@ class TaskOrganizerController extends ITaskOrganizerController.Stub { public void setDeferTaskOrgCallbacksConsumer(Consumer<Runnable> consumer) { mDeferTaskOrgCallbacksConsumer = consumer; } + + @VisibleForTesting + ArrayList<PendingTaskEvent> getPendingEventList() { + return mPendingTaskEvents; + } + /** * Register a TaskOrganizer to manage tasks as they enter the a supported windowing mode. */ @@ -442,13 +474,33 @@ class TaskOrganizerController extends ITaskOrganizerController.Stub { void onTaskAppeared(ITaskOrganizer organizer, Task task) { final TaskOrganizerState state = mTaskOrganizerStates.get(organizer.asBinder()); - state.addTask(task); + if (state != null && state.addTask(task)) { + PendingTaskEvent pending = getPendingTaskEvent(task, PendingTaskEvent.EVENT_APPEARED); + if (pending == null) { + pending = new PendingTaskEvent(task, PendingTaskEvent.EVENT_APPEARED); + mPendingTaskEvents.add(pending); + } + } } void onTaskVanished(ITaskOrganizer organizer, Task task) { final TaskOrganizerState state = mTaskOrganizerStates.get(organizer.asBinder()); - if (state != null) { - state.removeTask(task); + if (state != null && state.removeTask(task)) { + for (int i = mPendingTaskEvents.size() - 1; i >= 0; i--) { + PendingTaskEvent entry = mPendingTaskEvents.get(i); + if (task.mTaskId == entry.mTask.mTaskId) { + // This task will vanished so remove all pending event of it. + mPendingTaskEvents.remove(i); + if (entry.mEventType == PendingTaskEvent.EVENT_APPEARED) { + // If task still not appeared, ignore this callback. + return; + } + } + } + + PendingTaskEvent pending = + new PendingTaskEvent(task, organizer, PendingTaskEvent.EVENT_VANISHED); + mPendingTaskEvents.add(pending); } } @@ -510,7 +562,7 @@ class TaskOrganizerController extends ITaskOrganizerController.Stub { ProtoLog.v(WM_DEBUG_WINDOW_ORGANIZER, "Delete root task display=%d winMode=%d", task.getDisplayId(), task.getWindowingMode()); - task.removeImmediately(); + task.removeImmediately("deleteRootTask"); return true; } } finally { @@ -518,30 +570,76 @@ class TaskOrganizerController extends ITaskOrganizerController.Stub { } } - void dispatchPendingTaskInfoChanges() { - if (mService.mWindowManager.mWindowPlacerLocked.isLayoutDeferred()) { + void dispatchPendingEvents() { + if (mService.mWindowManager.mWindowPlacerLocked.isLayoutDeferred() + || mPendingTaskEvents.isEmpty()) { return; } - for (int i = 0, n = mPendingTaskInfoChanges.size(); i < n; ++i) { - dispatchTaskInfoChanged(mPendingTaskInfoChanges.get(i), false /* force */); + + for (int i = 0, n = mPendingTaskEvents.size(); i < n; i++) { + PendingTaskEvent event = mPendingTaskEvents.get(i); + final Task task = event.mTask; + final TaskOrganizerState state; + switch (event.mEventType) { + case PendingTaskEvent.EVENT_APPEARED: + state = mTaskOrganizerStates.get(event.mTaskOrg.asBinder()); + if (state != null && task.taskAppearedReady()) { + state.mOrganizer.onTaskAppeared(task); + } + break; + case PendingTaskEvent.EVENT_VANISHED: + state = mTaskOrganizerStates.get(event.mTaskOrg.asBinder()); + if (state != null) { + state.mOrganizer.onTaskVanished(task); + } + break; + case PendingTaskEvent.EVENT_INFO_CHANGED: + dispatchTaskInfoChanged(event.mTask, event.mForce); + break; + case PendingTaskEvent.EVENT_ROOT_BACK_PRESSED: + state = mTaskOrganizerStates.get(event.mTaskOrg.asBinder()); + if (state != null) { + state.mOrganizer.onBackPressedOnTaskRoot(task); + } + break; + } } - mPendingTaskInfoChanges.clear(); + mPendingTaskEvents.clear(); } - void dispatchTaskInfoChanged(Task task, boolean force) { - if (!force && mService.mWindowManager.mWindowPlacerLocked.isLayoutDeferred()) { - // Defer task info reporting while layout is deferred. This is because layout defer - // blocks tend to do lots of re-ordering which can mess up animations in receivers. - mPendingTaskInfoChanges.remove(task); - mPendingTaskInfoChanges.add(task); + void onTaskInfoChanged(Task task, boolean force) { + if (!task.mTaskAppearedSent) { + // Skip if task still not appeared. return; } + + // Defer task info reporting while layout is deferred. This is because layout defer + // blocks tend to do lots of re-ordering which can mess up animations in receivers. + PendingTaskEvent pending = getPendingLifecycleTaskEvent(task); + if (pending == null) { + pending = new PendingTaskEvent(task, PendingTaskEvent.EVENT_INFO_CHANGED); + } else { + if (pending.mEventType != PendingTaskEvent.EVENT_INFO_CHANGED) { + // If queued event is appeared, it means task still not appeared so ignore + // this info changed. If queued event is vanished, it means task should + // will vanished early so do not need this info changed. + return; + } + // Remove and add for re-ordering. + mPendingTaskEvents.remove(pending); + } + pending.mForce = force; + mPendingTaskEvents.add(pending); + } + + private void dispatchTaskInfoChanged(Task task, boolean force) { RunningTaskInfo lastInfo = mLastSentTaskInfos.get(task); if (mTmpTaskInfo == null) { mTmpTaskInfo = new RunningTaskInfo(); } mTmpTaskInfo.configuration.unset(); task.fillTaskInfo(mTmpTaskInfo); + boolean changed = !mTmpTaskInfo.equalsForTaskOrganizer(lastInfo); if (!changed) { int cfgChanges = mTmpTaskInfo.configuration.diff(lastInfo.configuration); @@ -601,45 +699,6 @@ class TaskOrganizerController extends ITaskOrganizerController.Stub { } @Override - public void setLaunchRoot(int displayId, @Nullable WindowContainerToken token) { - enforceTaskPermission("setLaunchRoot()"); - final long origId = Binder.clearCallingIdentity(); - try { - synchronized (mGlobalLock) { - TaskDisplayArea defaultTaskDisplayArea = mService.mRootWindowContainer - .getDisplayContent(displayId).getDefaultTaskDisplayArea(); - if (defaultTaskDisplayArea == null) { - return; - } - WindowContainer wc = null; - if (token != null) { - wc = WindowContainer.fromBinder(token.asBinder()); - if (wc == null) { - throw new IllegalArgumentException("Can't resolve window from token"); - } - } - final Task task = wc == null ? null : wc.asTask(); - if (task == null) { - defaultTaskDisplayArea.mLaunchRootTask = null; - return; - } - if (!task.mCreatedByOrganizer) { - throw new IllegalArgumentException("Attempt to set task not created by " - + "organizer as launch root task=" + task); - } - if (task.getDisplayArea() == null - || task.getDisplayArea().getDisplayId() != displayId) { - throw new RuntimeException("Can't set launch root for display " + displayId - + " to task on display " + task.getDisplayContent().getDisplayId()); - } - task.getDisplayArea().mLaunchRootTask = task; - } - } finally { - Binder.restoreCallingIdentity(origId); - } - } - - @Override public List<RunningTaskInfo> getChildTasks(WindowContainerToken parent, @Nullable int[] activityTypes) { enforceTaskPermission("getChildTasks()"); @@ -743,11 +802,48 @@ class TaskOrganizerController extends ITaskOrganizerController.Stub { return false; } - final TaskOrganizerState state = mTaskOrganizerStates.get(task.mTaskOrganizer.asBinder()); - state.mOrganizer.onBackPressedOnTaskRoot(task); + PendingTaskEvent pendingVanished = + getPendingTaskEvent(task, PendingTaskEvent.EVENT_VANISHED); + if (pendingVanished != null) { + // This task will vanish before this callback so just ignore. + return false; + } + + PendingTaskEvent pending = getPendingTaskEvent( + task, PendingTaskEvent.EVENT_ROOT_BACK_PRESSED); + if (pending == null) { + pending = new PendingTaskEvent(task, PendingTaskEvent.EVENT_ROOT_BACK_PRESSED); + } else { + // Pending already exist, remove and add for re-ordering. + mPendingTaskEvents.remove(pending); + } + mPendingTaskEvents.add(pending); return true; } + @Nullable + private PendingTaskEvent getPendingTaskEvent(Task task, int type) { + for (int i = mPendingTaskEvents.size() - 1; i >= 0; i--) { + PendingTaskEvent entry = mPendingTaskEvents.get(i); + if (task.mTaskId == entry.mTask.mTaskId && type == entry.mEventType) { + return entry; + } + } + return null; + } + + @VisibleForTesting + @Nullable + PendingTaskEvent getPendingLifecycleTaskEvent(Task task) { + for (int i = mPendingTaskEvents.size() - 1; i >= 0; i--) { + PendingTaskEvent entry = mPendingTaskEvents.get(i); + if (task.mTaskId == entry.mTask.mTaskId && entry.isLifecycleEvent()) { + return entry; + } + } + return null; + } + public void dump(PrintWriter pw, String prefix) { final String innerPrefix = prefix + " "; pw.print(prefix); pw.println("TaskOrganizerController:"); diff --git a/services/core/java/com/android/server/wm/WindowAnimator.java b/services/core/java/com/android/server/wm/WindowAnimator.java index f627ca620bc3..83ad437eba29 100644 --- a/services/core/java/com/android/server/wm/WindowAnimator.java +++ b/services/core/java/com/android/server/wm/WindowAnimator.java @@ -224,6 +224,7 @@ public class WindowAnimator { mService.destroyPreservedSurfaceLocked(); + mService.mAtmService.mTaskOrganizerController.dispatchPendingEvents(); executeAfterPrepareSurfacesRunnables(); if (DEBUG_WINDOW_TRACE) { diff --git a/services/core/java/com/android/server/wm/WindowManagerService.java b/services/core/java/com/android/server/wm/WindowManagerService.java index 4eeae6c0710c..a034bac993e0 100644 --- a/services/core/java/com/android/server/wm/WindowManagerService.java +++ b/services/core/java/com/android/server/wm/WindowManagerService.java @@ -3238,6 +3238,11 @@ public class WindowManagerService extends IWindowManager.Stub @Override public void closeSystemDialogs(String reason) { + int callingPid = Binder.getCallingPid(); + int callingUid = Binder.getCallingUid(); + if (!mAtmInternal.checkCanCloseSystemDialogs(callingPid, callingUid, null)) { + return; + } synchronized (mGlobalLock) { mRoot.closeSystemDialogs(reason); } diff --git a/services/core/java/com/android/server/wm/WindowOrganizerController.java b/services/core/java/com/android/server/wm/WindowOrganizerController.java index b0e67ce17711..be1f7e16cd3c 100644 --- a/services/core/java/com/android/server/wm/WindowOrganizerController.java +++ b/services/core/java/com/android/server/wm/WindowOrganizerController.java @@ -17,6 +17,10 @@ package com.android.server.wm; import static android.Manifest.permission.READ_FRAME_BUFFER; +import static android.window.WindowContainerTransaction.HierarchyOp.HIERARCHY_OP_TYPE_CHILDREN_TASKS_REPARENT; +import static android.window.WindowContainerTransaction.HierarchyOp.HIERARCHY_OP_TYPE_REORDER; +import static android.window.WindowContainerTransaction.HierarchyOp.HIERARCHY_OP_TYPE_REPARENT; +import static android.window.WindowContainerTransaction.HierarchyOp.HIERARCHY_OP_TYPE_SET_LAUNCH_ROOT; import static com.android.internal.protolog.ProtoLogGroup.WM_DEBUG_WINDOW_ORGANIZER; import static com.android.server.wm.ActivityTaskManagerService.LAYOUT_REASON_CONFIG_CHANGED; @@ -49,13 +53,16 @@ import android.window.WindowContainerTransaction; import com.android.internal.annotations.VisibleForTesting; import com.android.internal.protolog.common.ProtoLog; +import com.android.internal.util.ArrayUtils; import com.android.internal.util.function.pooled.PooledConsumer; import com.android.internal.util.function.pooled.PooledLambda; +import java.util.ArrayList; import java.util.HashMap; import java.util.Iterator; import java.util.List; import java.util.Map; +import java.util.function.Consumer; /** * Server side implementation for the interface for organizing windows @@ -256,34 +263,46 @@ class WindowOrganizerController extends IWindowOrganizerController.Stub final List<WindowContainerTransaction.HierarchyOp> hops = t.getHierarchyOps(); for (int i = 0, n = hops.size(); i < n; ++i) { final WindowContainerTransaction.HierarchyOp hop = hops.get(i); - final WindowContainer wc = WindowContainer.fromBinder(hop.getContainer()); - if (wc == null || !wc.isAttached()) { - Slog.e(TAG, "Attempt to operate on detached container: " + wc); - continue; - } - if (syncId >= 0) { - addToSyncSet(syncId, wc); - } - if (transition != null) { - transition.collect(wc); - if (hop.isReparent()) { - if (wc.getParent() != null) { - // Collect the current parent. It's visibility may change as a result - // of this reparenting. - transition.collect(wc.getParent()); + switch (hop.getType()) { + case HIERARCHY_OP_TYPE_SET_LAUNCH_ROOT: + final Task task = WindowContainer.fromBinder(hop.getContainer()).asTask(); + task.getDisplayArea().setLaunchRootTask(task, + hop.getWindowingModes(), hop.getActivityTypes()); + break; + case HIERARCHY_OP_TYPE_CHILDREN_TASKS_REPARENT: + effects |= reparentChildrenTasksHierarchyOp(hop, transition, syncId); + break; + case HIERARCHY_OP_TYPE_REORDER: + case HIERARCHY_OP_TYPE_REPARENT: + final WindowContainer wc = WindowContainer.fromBinder(hop.getContainer()); + if (wc == null || !wc.isAttached()) { + Slog.e(TAG, "Attempt to operate on detached container: " + wc); + continue; } - if (hop.getNewParent() != null) { - final WindowContainer parentWc = - WindowContainer.fromBinder(hop.getNewParent()); - if (parentWc == null) { - Slog.e(TAG, "Can't resolve parent window from token"); - continue; + if (syncId >= 0) { + addToSyncSet(syncId, wc); + } + if (transition != null) { + transition.collect(wc); + if (hop.isReparent()) { + if (wc.getParent() != null) { + // Collect the current parent. It's visibility may change as + // a result of this reparenting. + transition.collect(wc.getParent()); + } + if (hop.getNewParent() != null) { + final WindowContainer parentWc = + WindowContainer.fromBinder(hop.getNewParent()); + if (parentWc == null) { + Slog.e(TAG, "Can't resolve parent window from token"); + continue; + } + transition.collect(parentWc); + } } - transition.collect(parentWc); } - } + effects |= sanitizeAndApplyHierarchyOp(wc, hop); } - effects |= sanitizeAndApplyHierarchyOp(wc, hop); } // Queue-up bounds-change transactions for tasks which are now organized. Do // this after hierarchy ops so we have the final organized state. @@ -492,6 +511,85 @@ class WindowOrganizerController extends IWindowOrganizerController.Stub return TRANSACT_EFFECTS_LIFECYCLE; } + private int reparentChildrenTasksHierarchyOp(WindowContainerTransaction.HierarchyOp hop, + @Nullable Transition transition, int syncId) { + WindowContainer currentParent = hop.getContainer() != null + ? WindowContainer.fromBinder(hop.getContainer()) : null; + WindowContainer newParent = hop.getNewParent() != null + ? WindowContainer.fromBinder(hop.getNewParent()) : null; + if (currentParent == null && newParent == null) { + throw new IllegalArgumentException("reparentChildrenTasksHierarchyOp: " + hop); + } else if (currentParent == null) { + currentParent = newParent.asTask().getDisplayContent().getDefaultTaskDisplayArea(); + } else if (newParent == null) { + newParent = currentParent.asTask().getDisplayContent().getDefaultTaskDisplayArea(); + } + + if (currentParent == newParent) { + Slog.e(TAG, "reparentChildrenTasksHierarchyOp parent not changing: " + hop); + return 0; + } + if (!currentParent.isAttached()) { + Slog.e(TAG, "reparentChildrenTasksHierarchyOp currentParent detached=" + + currentParent + " hop=" + hop); + return 0; + } + if (!newParent.isAttached()) { + Slog.e(TAG, "reparentChildrenTasksHierarchyOp newParent detached=" + + newParent + " hop=" + hop); + return 0; + } + + final boolean newParentInMultiWindow = newParent.inMultiWindowMode(); + final WindowContainer finalCurrentParent = currentParent; + Slog.i(TAG, "reparentChildrenTasksHierarchyOp" + + " currentParent=" + currentParent + " newParent=" + newParent + " hop=" + hop); + + // We want to collect the tasks first before re-parenting to avoid array shifting on us. + final ArrayList<Task> tasksToReparent = new ArrayList<>(); + + currentParent.forAllTasks((Consumer<Task>) (task) -> { + Slog.i(TAG, " Processing task=" + task); + if (task.mCreatedByOrganizer + || task.getParent() != finalCurrentParent) { + // We only care about non-organized task that are direct children of the thing we + // are reparenting from. + return; + } + + if (newParentInMultiWindow && !task.isResizeable()) { + Slog.e(TAG, "reparentChildrenTasksHierarchyOp non-resizeable task=" + task); + } + + if (!ArrayUtils.contains(hop.getActivityTypes(), task.getActivityType())) return; + if (!ArrayUtils.contains(hop.getWindowingModes(), task.getWindowingMode())) return; + + tasksToReparent.add(task); + }, !hop.getToTop()); + + final int count = tasksToReparent.size(); + for (int i = 0; i < count; ++i) { + final Task task = tasksToReparent.get(i); + if (syncId >= 0) { + addToSyncSet(syncId, task); + } + if (transition != null) transition.collect(task); + + if (newParent instanceof TaskDisplayArea) { + // For now, reparenting to display area is different from other reparents... + task.reparent((TaskDisplayArea) newParent, hop.getToTop()); + } else { + task.reparent((Task) newParent, + hop.getToTop() ? POSITION_TOP : POSITION_BOTTOM, + false /*moveParents*/, "processChildrenTaskReparentHierarchyOp"); + } + } + + if (transition != null) transition.collect(newParent); + + return TRANSACT_EFFECTS_LIFECYCLE; + } + private void sanitizeWindowContainer(WindowContainer wc) { if (!(wc instanceof Task) && !(wc instanceof DisplayArea)) { throw new RuntimeException("Invalid token in task or displayArea transaction"); diff --git a/services/core/java/com/android/server/wm/WindowProcessController.java b/services/core/java/com/android/server/wm/WindowProcessController.java index 8aa154bb9dd1..663d91ecc82a 100644 --- a/services/core/java/com/android/server/wm/WindowProcessController.java +++ b/services/core/java/com/android/server/wm/WindowProcessController.java @@ -23,6 +23,7 @@ import static android.os.IInputConstants.DEFAULT_DISPATCHING_TIMEOUT_MILLIS; import static android.view.Display.INVALID_DISPLAY; import static com.android.internal.protolog.ProtoLogGroup.WM_DEBUG_CONFIGURATION; +import static com.android.internal.util.Preconditions.checkArgument; import static com.android.server.am.ActivityManagerService.MY_PID; import static com.android.server.wm.ActivityTaskManagerDebugConfig.DEBUG_ACTIVITY_STARTS; import static com.android.server.wm.ActivityTaskManagerDebugConfig.DEBUG_RELEASE; @@ -161,6 +162,8 @@ public class WindowProcessController extends ConfigurationContainer<Configuratio private volatile boolean mDebugging; // Active instrumentation running in process? private volatile boolean mInstrumenting; + // If there is active instrumentation, this is the source + private volatile int mInstrumentationSourceUid = -1; // Active instrumentation with background activity starts privilege running in process? private volatile boolean mInstrumentingWithBackgroundActivityStartPrivileges; // This process it perceptible by the user. @@ -623,9 +626,16 @@ public class WindowProcessController extends ConfigurationContainer<Configuratio mBoundClientUids = boundClientUids; } - public void setInstrumenting(boolean instrumenting, + /** + * Set instrumentation-related info. + * + * If {@code instrumenting} is {@code false}, {@code sourceUid} has to be -1. + */ + public void setInstrumenting(boolean instrumenting, int sourceUid, boolean hasBackgroundActivityStartPrivileges) { + checkArgument(instrumenting || sourceUid == -1); mInstrumenting = instrumenting; + mInstrumentationSourceUid = sourceUid; mInstrumentingWithBackgroundActivityStartPrivileges = hasBackgroundActivityStartPrivileges; } @@ -633,6 +643,11 @@ public class WindowProcessController extends ConfigurationContainer<Configuratio return mInstrumenting; } + /** Returns the uid of the active instrumentation source if there is one, otherwise -1. */ + int getInstrumentationSourceUid() { + return mInstrumentationSourceUid; + } + public void setPerceptible(boolean perceptible) { mPerceptible = perceptible; } diff --git a/services/core/xsd/platform-compat-config.xsd b/services/core/xsd/platform-compat-config.xsd index 992470816068..a62e2c385766 100644 --- a/services/core/xsd/platform-compat-config.xsd +++ b/services/core/xsd/platform-compat-config.xsd @@ -31,6 +31,7 @@ <xs:attribute type="xs:int" name="enableAfterTargetSdk"/> <xs:attribute type="xs:int" name="enableSinceTargetSdk"/> <xs:attribute type="xs:string" name="description"/> + <xs:attribute type="xs:boolean" name="overridable"/> </xs:extension> </xs:simpleContent> </xs:complexType> @@ -48,7 +49,3 @@ </xs:unique> </xs:element> </xs:schema> - - - - diff --git a/services/core/xsd/platform-compat-schema/current.txt b/services/core/xsd/platform-compat-schema/current.txt index e3640edd0201..fb8bbefd8374 100644 --- a/services/core/xsd/platform-compat-schema/current.txt +++ b/services/core/xsd/platform-compat-schema/current.txt @@ -10,6 +10,7 @@ package com.android.server.compat.config { method public long getId(); method public boolean getLoggingOnly(); method public String getName(); + method public boolean getOverridable(); method public String getValue(); method public void setDescription(String); method public void setDisabled(boolean); @@ -18,6 +19,7 @@ package com.android.server.compat.config { method public void setId(long); method public void setLoggingOnly(boolean); method public void setName(String); + method public void setOverridable(boolean); method public void setValue(String); } diff --git a/services/devicepolicy/java/com/android/server/devicepolicy/BaseIDevicePolicyManager.java b/services/devicepolicy/java/com/android/server/devicepolicy/BaseIDevicePolicyManager.java index 22e9725f49ab..8b7f83f4edb5 100644 --- a/services/devicepolicy/java/com/android/server/devicepolicy/BaseIDevicePolicyManager.java +++ b/services/devicepolicy/java/com/android/server/devicepolicy/BaseIDevicePolicyManager.java @@ -55,6 +55,12 @@ abstract class BaseIDevicePolicyManager extends IDevicePolicyManager.Stub { */ abstract void handleUnlockUser(int userId); /** + * To be called by {@link DevicePolicyManagerService#Lifecycle} after a user is being unlocked. + * + * @see {@link SystemService#onUserUnlocked} + */ + abstract void handleOnUserUnlocked(int userId); + /** * To be called by {@link DevicePolicyManagerService#Lifecycle} when a user is being stopped. * * @see {@link SystemService#onUserStopping} diff --git a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyData.java b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyData.java index 7ec5ff0b3492..5e7f984d6726 100644 --- a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyData.java +++ b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyData.java @@ -78,6 +78,12 @@ class DevicePolicyData { private static final String ATTR_DEVICE_PROVISIONING_CONFIG_APPLIED = "device-provisioning-config-applied"; private static final String ATTR_DEVICE_PAIRED = "device-paired"; + private static final String ATTR_NEW_USER_DISCLAIMER = "new-user-disclaimer"; + + // Values of ATTR_NEW_USER_DISCLAIMER + static final String NEW_USER_DISCLAIMER_SHOWN = "shown"; + static final String NEW_USER_DISCLAIMER_NOT_NEEDED = "not_needed"; + static final String NEW_USER_DISCLAIMER_NEEDED = "needed"; private static final String TAG = DevicePolicyManagerService.LOG_TAG; private static final boolean VERBOSE_LOG = false; // DO NOT SUBMIT WITH TRUE @@ -146,6 +152,10 @@ class DevicePolicyData { // apps were suspended or unsuspended. boolean mAppsSuspended = false; + // Whether it's necessary to show a disclaimer (that the device is managed) after the user + // starts. + String mNewUserDisclaimer = NEW_USER_DISCLAIMER_NOT_NEEDED; + DevicePolicyData(@UserIdInt int userId) { mUserId = userId; } @@ -186,6 +196,9 @@ class DevicePolicyData { if (policyData.mPermissionPolicy != DevicePolicyManager.PERMISSION_POLICY_PROMPT) { out.attributeInt(null, ATTR_PERMISSION_POLICY, policyData.mPermissionPolicy); } + if (NEW_USER_DISCLAIMER_NEEDED.equals(policyData.mNewUserDisclaimer)) { + out.attribute(null, ATTR_NEW_USER_DISCLAIMER, policyData.mNewUserDisclaimer); + } // Serialize delegations. for (int i = 0; i < policyData.mDelegationMap.size(); ++i) { @@ -412,6 +425,7 @@ class DevicePolicyData { if (permissionPolicy != -1) { policy.mPermissionPolicy = permissionPolicy; } + policy.mNewUserDisclaimer = parser.getAttributeValue(null, ATTR_NEW_USER_DISCLAIMER); int outerDepth = parser.getDepth(); policy.mLockTaskPackages.clear(); @@ -588,6 +602,7 @@ class DevicePolicyData { pw.print("mAppsSuspended="); pw.println(mAppsSuspended); pw.print("mUserSetupComplete="); pw.println(mUserSetupComplete); pw.print("mAffiliationIds="); pw.println(mAffiliationIds); + pw.print("mNewUserDisclaimer="); pw.println(mNewUserDisclaimer); pw.decreaseIndent(); } } diff --git a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java index 4654fca250a3..8b575fb16a2a 100644 --- a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java +++ b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java @@ -707,6 +707,11 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager { public void onUserStopping(@NonNull TargetUser user) { mService.handleStopUser(user.getUserIdentifier()); } + + @Override + public void onUserUnlocked(@NonNull TargetUser user) { + mService.handleOnUserUnlocked(user.getUserIdentifier()); + } } @GuardedBy("getLockObject()") @@ -886,6 +891,14 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager { } } + private final class UserLifecycleListener implements UserManagerInternal.UserLifecycleListener { + + @Override + public void onUserCreated(UserInfo user) { + mHandler.post(() -> handleNewUserCreated(user)); + } + } + private void handlePackagesChanged(@Nullable String packageName, int userHandle) { boolean removedAdmin = false; if (VERBOSE_LOG) { @@ -1561,6 +1574,7 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager { mSetupContentObserver = new SetupContentObserver(mHandler); mUserManagerInternal.addUserRestrictionsListener(new RestrictionsListener(mContext)); + mUserManagerInternal.addUserLifecycleListener(new UserLifecycleListener()); loadOwners(); } @@ -2902,6 +2916,11 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager { } @Override + void handleOnUserUnlocked(int userId) { + showNewUserDisclaimerIfNecessary(userId); + } + + @Override void handleStopUser(int userId) { stopOwnerService(userId, "stop-user"); } @@ -3432,6 +3451,19 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager { synchronized (getLockObject()) { ActiveAdmin ap = getActiveAdminForCallerLocked( who, DeviceAdminInfo.USES_POLICY_LIMIT_PASSWORD, parent); + + // If setPasswordQuality is called on the parent, ensure that + // the primary admin does not have password complexity state (this is an + // unsupported state). + if (parent) { + final ActiveAdmin primaryAdmin = getActiveAdminForCallerLocked( + who, DeviceAdminInfo.USES_POLICY_LIMIT_PASSWORD, false); + final boolean hasComplexitySet = + primaryAdmin.mPasswordComplexity != PASSWORD_COMPLEXITY_NONE; + Preconditions.checkState(!hasComplexitySet, + "Cannot set password quality when complexity is set on the primary admin." + + " Set the primary admin's complexity to NONE first."); + } mInjector.binderWithCleanCallingIdentity(() -> { final PasswordPolicy passwordPolicy = ap.mPasswordPolicy; if (passwordPolicy.quality != quality) { @@ -4397,6 +4429,20 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager { final ActiveAdmin admin = getParentOfAdminIfRequired( getProfileOwnerOrDeviceOwnerLocked(caller), calledOnParent); if (admin.mPasswordComplexity != passwordComplexity) { + // We require the caller to explicitly clear any password quality requirements set + // on the parent DPM instance, to avoid the case where password requirements are + // specified in the form of quality on the parent but complexity on the profile + // itself. + if (!calledOnParent) { + final boolean hasQualityRequirementsOnParent = admin.hasParentActiveAdmin() + && admin.getParentActiveAdmin().mPasswordPolicy.quality + != PASSWORD_QUALITY_UNSPECIFIED; + Preconditions.checkState(!hasQualityRequirementsOnParent, + "Password quality is set on the parent when attempting to set password" + + "complexity. Clear the quality by setting the password quality " + + "on the parent to PASSWORD_QUALITY_UNSPECIFIED first"); + } + mInjector.binderWithCleanCallingIdentity(() -> { admin.mPasswordComplexity = passwordComplexity; // Reset the password policy. @@ -7660,8 +7706,8 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager { // Sets profile owner on current foreground user since // the human user will complete the DO setup workflow from there. manageUserUnchecked(/* deviceOwner= */ admin, /* profileOwner= */ admin, - /* managedUser= */ currentForegroundUser, - /* adminExtras= */ null); + /* managedUser= */ currentForegroundUser, /* adminExtras= */ null, + /* showDisclaimer= */ false); } return true; } @@ -9713,7 +9759,8 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager { final long id = mInjector.binderClearCallingIdentity(); try { - manageUserUnchecked(admin, profileOwner, userHandle, adminExtras); + manageUserUnchecked(admin, profileOwner, userHandle, adminExtras, + /* showDisclaimer= */ true); if ((flags & DevicePolicyManager.SKIP_SETUP_WIZARD) != 0) { Settings.Secure.putIntForUser(mContext.getContentResolver(), @@ -9735,7 +9782,7 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager { } private void manageUserUnchecked(ComponentName admin, ComponentName profileOwner, - @UserIdInt int userId, PersistableBundle adminExtras) { + @UserIdInt int userId, PersistableBundle adminExtras, boolean showDisclaimer) { final String adminPkg = admin.getPackageName(); try { // Install the profile owner if not present. @@ -9761,11 +9808,60 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager { DevicePolicyData policyData = getUserData(userId); policyData.mInitBundle = adminExtras; policyData.mAdminBroadcastPending = true; + policyData.mNewUserDisclaimer = showDisclaimer + ? DevicePolicyData.NEW_USER_DISCLAIMER_NEEDED + : DevicePolicyData.NEW_USER_DISCLAIMER_NOT_NEEDED; + saveSettingsLocked(userId); + } + } + + private void handleNewUserCreated(UserInfo user) { + if (!mOwners.hasDeviceOwner()) return; + final int userId = user.id; + Log.i(LOG_TAG, "User " + userId + " added on DO mode; setting ShowNewUserDisclaimer"); + + setShowNewUserDisclaimer(userId, DevicePolicyData.NEW_USER_DISCLAIMER_NEEDED); + } + + @Override + public void resetNewUserDisclaimer() { + CallerIdentity callerIdentity = getCallerIdentity(); + canManageUsers(callerIdentity); + + setShowNewUserDisclaimer(callerIdentity.getUserId(), + DevicePolicyData.NEW_USER_DISCLAIMER_SHOWN); + } + + private void setShowNewUserDisclaimer(@UserIdInt int userId, String value) { + Slog.i(LOG_TAG, "Setting new user disclaimer for user " + userId + " as " + value); + synchronized (getLockObject()) { + DevicePolicyData policyData = getUserData(userId); + policyData.mNewUserDisclaimer = value; saveSettingsLocked(userId); } } + private void showNewUserDisclaimerIfNecessary(@UserIdInt int userId) { + boolean mustShow; + synchronized (getLockObject()) { + DevicePolicyData policyData = getUserData(userId); + if (VERBOSE_LOG) { + Slog.v(LOG_TAG, "showNewUserDisclaimerIfNecessary(" + userId + "): " + + policyData.mNewUserDisclaimer + ")"); + } + mustShow = DevicePolicyData.NEW_USER_DISCLAIMER_NEEDED + .equals(policyData.mNewUserDisclaimer); + } + if (!mustShow) return; + + Intent intent = new Intent(DevicePolicyManager.ACTION_SHOW_NEW_USER_DISCLAIMER); + + // TODO(b/172691310): add CTS tests to make sure disclaimer is shown + Slog.i(LOG_TAG, "Dispatching ACTION_SHOW_NEW_USER_DISCLAIMER intent"); + mContext.sendBroadcastAsUser(intent, UserHandle.of(userId)); + } + @Override public boolean removeUser(ComponentName who, UserHandle userHandle) { Objects.requireNonNull(who, "ComponentName is null"); diff --git a/services/devicepolicy/java/com/android/server/devicepolicy/Owners.java b/services/devicepolicy/java/com/android/server/devicepolicy/Owners.java index 79a82b8e7175..809afe01da2d 100644 --- a/services/devicepolicy/java/com/android/server/devicepolicy/Owners.java +++ b/services/devicepolicy/java/com/android/server/devicepolicy/Owners.java @@ -31,6 +31,7 @@ import android.os.Process; import android.os.UserHandle; import android.os.UserManager; import android.util.ArrayMap; +import android.util.ArraySet; import android.util.AtomicFile; import android.util.IndentingPrintWriter; import android.util.Log; @@ -203,6 +204,7 @@ class Owners { } pushToPackageManagerLocked(); pushToActivityTaskManagerLocked(); + pushToActivityManagerLocked(); pushToAppOpsLocked(); } } @@ -218,12 +220,34 @@ class Owners { } private void pushToActivityTaskManagerLocked() { - final int uid = mDeviceOwner != null ? mPackageManagerInternal.getPackageUid( - mDeviceOwner.packageName, - PackageManager.MATCH_ALL | PackageManager.MATCH_KNOWN_PACKAGES, mDeviceOwnerUserId) - : Process.INVALID_UID; - mActivityTaskManagerInternal.setDeviceOwnerUid(uid); - mActivityManagerInternal.setDeviceOwnerUid(uid); + mActivityTaskManagerInternal.setDeviceOwnerUid(getDeviceOwnerUidLocked()); + } + + private void pushToActivityManagerLocked() { + mActivityManagerInternal.setDeviceOwnerUid(getDeviceOwnerUidLocked()); + + final ArraySet<Integer> profileOwners = new ArraySet<>(); + for (int poi = mProfileOwners.size() - 1; poi >= 0; poi--) { + final int userId = mProfileOwners.keyAt(poi); + final int profileOwnerUid = mPackageManagerInternal.getPackageUid( + mProfileOwners.valueAt(poi).packageName, + PackageManager.MATCH_ALL | PackageManager.MATCH_KNOWN_PACKAGES, + userId); + if (profileOwnerUid >= 0) { + profileOwners.add(profileOwnerUid); + } + } + mActivityManagerInternal.setProfileOwnerUid(profileOwners); + } + + int getDeviceOwnerUidLocked() { + if (mDeviceOwner != null) { + return mPackageManagerInternal.getPackageUid(mDeviceOwner.packageName, + PackageManager.MATCH_ALL | PackageManager.MATCH_KNOWN_PACKAGES, + mDeviceOwnerUserId); + } else { + return Process.INVALID_UID; + } } String getDeviceOwnerPackageName() { @@ -301,6 +325,7 @@ class Owners { mUserManagerInternal.setDeviceManaged(true); pushToPackageManagerLocked(); pushToActivityTaskManagerLocked(); + pushToActivityManagerLocked(); pushToAppOpsLocked(); } } @@ -313,6 +338,7 @@ class Owners { mUserManagerInternal.setDeviceManaged(false); pushToPackageManagerLocked(); pushToActivityTaskManagerLocked(); + pushToActivityManagerLocked(); pushToAppOpsLocked(); } } @@ -325,6 +351,7 @@ class Owners { /* remoteBugreportHash =*/ null, /* isOrganizationOwnedDevice =*/ false)); mUserManagerInternal.setUserManaged(userId, true); pushToPackageManagerLocked(); + pushToActivityManagerLocked(); pushToAppOpsLocked(); } } @@ -334,6 +361,7 @@ class Owners { mProfileOwners.remove(userId); mUserManagerInternal.setUserManaged(userId, false); pushToPackageManagerLocked(); + pushToActivityManagerLocked(); pushToAppOpsLocked(); } } @@ -347,6 +375,7 @@ class Owners { ownerInfo.isOrganizationOwnedDevice); mProfileOwners.put(userId, newOwnerInfo); pushToPackageManagerLocked(); + pushToActivityManagerLocked(); pushToAppOpsLocked(); } } @@ -361,6 +390,7 @@ class Owners { mDeviceOwner.isOrganizationOwnedDevice); pushToPackageManagerLocked(); pushToActivityTaskManagerLocked(); + pushToActivityManagerLocked(); pushToAppOpsLocked(); } } @@ -665,9 +695,7 @@ class Owners { try { final SparseIntArray owners = new SparseIntArray(); if (mDeviceOwner != null) { - final int uid = mPackageManagerInternal.getPackageUid(mDeviceOwner.packageName, - PackageManager.MATCH_ALL | PackageManager.MATCH_KNOWN_PACKAGES, - mDeviceOwnerUserId); + final int uid = getDeviceOwnerUidLocked(); if (uid >= 0) { owners.put(mDeviceOwnerUserId, uid); } @@ -695,6 +723,7 @@ class Owners { public void systemReady() { synchronized (mLock) { mSystemReady = true; + pushToActivityManagerLocked(); pushToAppOpsLocked(); } } diff --git a/services/incremental/BinderIncrementalService.cpp b/services/incremental/BinderIncrementalService.cpp index a31aac96eb48..d2244286450b 100644 --- a/services/incremental/BinderIncrementalService.cpp +++ b/services/incremental/BinderIncrementalService.cpp @@ -122,13 +122,14 @@ binder::Status BinderIncrementalService::createStorage( const ::android::sp<::android::content::pm::IDataLoaderStatusListener>& statusListener, const ::android::os::incremental::StorageHealthCheckParams& healthCheckParams, const ::android::sp<::android::os::incremental::IStorageHealthListener>& healthListener, + const ::std::vector<::android::os::incremental::PerUidReadTimeouts>& perUidReadTimeouts, int32_t* _aidl_return) { *_aidl_return = mImpl.createStorage(path, const_cast<content::pm::DataLoaderParamsParcel&&>(params), android::incremental::IncrementalService::CreateOptions(createMode), statusListener, const_cast<StorageHealthCheckParams&&>(healthCheckParams), - healthListener); + healthListener, perUidReadTimeouts); return ok(); } @@ -164,8 +165,8 @@ binder::Status BinderIncrementalService::deleteStorage(int32_t storageId) { return ok(); } -binder::Status BinderIncrementalService::disableReadLogs(int32_t storageId) { - mImpl.disableReadLogs(storageId); +binder::Status BinderIncrementalService::disallowReadLogs(int32_t storageId) { + mImpl.disallowReadLogs(storageId); return ok(); } @@ -254,7 +255,7 @@ binder::Status BinderIncrementalService::isFileFullyLoaded(int32_t storageId, binder::Status BinderIncrementalService::getLoadingProgress(int32_t storageId, float* _aidl_return) { - *_aidl_return = mImpl.getLoadingProgress(storageId); + *_aidl_return = mImpl.getLoadingProgress(storageId).getProgress(); return ok(); } diff --git a/services/incremental/BinderIncrementalService.h b/services/incremental/BinderIncrementalService.h index 8afa0f7bb117..9a4537a15f31 100644 --- a/services/incremental/BinderIncrementalService.h +++ b/services/incremental/BinderIncrementalService.h @@ -45,6 +45,7 @@ public: const ::android::sp<::android::content::pm::IDataLoaderStatusListener>& statusListener, const ::android::os::incremental::StorageHealthCheckParams& healthCheckParams, const ::android::sp<IStorageHealthListener>& healthListener, + const ::std::vector<::android::os::incremental::PerUidReadTimeouts>& perUidReadTimeouts, int32_t* _aidl_return) final; binder::Status createLinkedStorage(const std::string& path, int32_t otherStorageId, int32_t createMode, int32_t* _aidl_return) final; @@ -77,7 +78,7 @@ public: std::vector<uint8_t>* _aidl_return) final; binder::Status startLoading(int32_t storageId, bool* _aidl_return) final; binder::Status deleteStorage(int32_t storageId) final; - binder::Status disableReadLogs(int32_t storageId) final; + binder::Status disallowReadLogs(int32_t storageId) final; binder::Status configureNativeBinaries(int32_t storageId, const std::string& apkFullPath, const std::string& libDirRelativePath, const std::string& abi, bool extractNativeLibs, diff --git a/services/incremental/IncrementalService.cpp b/services/incremental/IncrementalService.cpp index eb6b325050eb..dde70caa797f 100644 --- a/services/incremental/IncrementalService.cpp +++ b/services/incremental/IncrementalService.cpp @@ -38,9 +38,11 @@ using namespace std::literals; -constexpr const char* kDataUsageStats = "android.permission.LOADER_USAGE_STATS"; +constexpr const char* kLoaderUsageStats = "android.permission.LOADER_USAGE_STATS"; constexpr const char* kOpUsage = "android:loader_usage_stats"; +constexpr const char* kInteractAcrossUsers = "android.permission.INTERACT_ACROSS_USERS"; + namespace android::incremental { using content::pm::DataLoaderParamsParcel; @@ -63,6 +65,10 @@ struct Constants { static constexpr auto libSuffix = ".so"sv; static constexpr auto blockSize = 4096; static constexpr auto systemPackage = "android"sv; + + static constexpr auto progressUpdateInterval = 1000ms; + static constexpr auto perUidTimeoutOffset = progressUpdateInterval * 2; + static constexpr auto minPerUidTimeout = progressUpdateInterval * 3; }; static const Constants& constants() { @@ -350,7 +356,8 @@ void IncrementalService::onDump(int fd) { dprintf(fd, " storages (%d): {\n", int(mnt.storages.size())); for (auto&& [storageId, storage] : mnt.storages) { dprintf(fd, " [%d] -> [%s] (%d %% loaded) \n", storageId, storage.name.c_str(), - (int)(getLoadingProgressFromPath(mnt, storage.name.c_str()) * 100)); + (int)(getLoadingProgressFromPath(mnt, storage.name.c_str()).getProgress() * + 100)); } dprintf(fd, " }\n"); @@ -419,12 +426,11 @@ auto IncrementalService::getStorageSlotLocked() -> MountMap::iterator { } } -StorageId IncrementalService::createStorage(std::string_view mountPoint, - content::pm::DataLoaderParamsParcel&& dataLoaderParams, - CreateOptions options, - const DataLoaderStatusListener& statusListener, - StorageHealthCheckParams&& healthCheckParams, - const StorageHealthListener& healthListener) { +StorageId IncrementalService::createStorage( + std::string_view mountPoint, content::pm::DataLoaderParamsParcel&& dataLoaderParams, + CreateOptions options, const DataLoaderStatusListener& statusListener, + StorageHealthCheckParams&& healthCheckParams, const StorageHealthListener& healthListener, + const std::vector<PerUidReadTimeouts>& perUidReadTimeouts) { LOG(INFO) << "createStorage: " << mountPoint << " | " << int(options); if (!path::isAbsolute(mountPoint)) { LOG(ERROR) << "path is not absolute: " << mountPoint; @@ -553,13 +559,14 @@ StorageId IncrementalService::createStorage(std::string_view mountPoint, if (auto err = addBindMount(*ifs, storageIt->first, storageIt->second.name, std::string(storageIt->second.name), std::move(mountNorm), bk, l); err < 0) { - LOG(ERROR) << "adding bind mount failed: " << -err; + LOG(ERROR) << "Adding bind mount failed: " << -err; return kInvalidStorageId; } // Done here as well, all data structures are in good state. secondCleanupOnFailure.release(); + // DataLoader. auto dataLoaderStub = prepareDataLoader(*ifs, std::move(dataLoaderParams), &statusListener, std::move(healthCheckParams), &healthListener); CHECK(dataLoaderStub); @@ -567,6 +574,11 @@ StorageId IncrementalService::createStorage(std::string_view mountPoint, mountIt->second = std::move(ifs); l.unlock(); + // Per Uid timeouts. + if (!perUidReadTimeouts.empty()) { + setUidReadTimeouts(mountId, perUidReadTimeouts); + } + if (mSystemReady.load(std::memory_order_relaxed) && !dataLoaderStub->requestCreate()) { // failed to create data loader LOG(ERROR) << "initializeDataLoader() failed"; @@ -634,17 +646,17 @@ StorageId IncrementalService::findStorageId(std::string_view path) const { return it->second->second.storage; } -void IncrementalService::disableReadLogs(StorageId storageId) { +void IncrementalService::disallowReadLogs(StorageId storageId) { std::unique_lock l(mLock); const auto ifs = getIfsLocked(storageId); if (!ifs) { - LOG(ERROR) << "disableReadLogs failed, invalid storageId: " << storageId; + LOG(ERROR) << "disallowReadLogs failed, invalid storageId: " << storageId; return; } - if (!ifs->readLogsEnabled()) { + if (!ifs->readLogsAllowed()) { return; } - ifs->disableReadLogs(); + ifs->disallowReadLogs(); l.unlock(); const auto metadata = constants().readLogsDisabledMarkerName; @@ -669,15 +681,26 @@ int IncrementalService::setStorageParams(StorageId storageId, bool enableReadLog const auto& params = ifs->dataLoaderStub->params(); if (enableReadLogs) { - if (!ifs->readLogsEnabled()) { + if (!ifs->readLogsAllowed()) { LOG(ERROR) << "setStorageParams failed, readlogs disabled for storageId: " << storageId; return -EPERM; } - if (auto status = mAppOpsManager->checkPermission(kDataUsageStats, kOpUsage, + // Check loader usage stats permission and apop. + if (auto status = mAppOpsManager->checkPermission(kLoaderUsageStats, kOpUsage, params.packageName.c_str()); !status.isOk()) { - LOG(ERROR) << "checkPermission failed: " << status.toString8(); + LOG(ERROR) << " Permission: " << kLoaderUsageStats + << " check failed: " << status.toString8(); + return fromBinderStatus(status); + } + + // Check multiuser permission. + if (auto status = mAppOpsManager->checkPermission(kInteractAcrossUsers, nullptr, + params.packageName.c_str()); + !status.isOk()) { + LOG(ERROR) << " Permission: " << kInteractAcrossUsers + << " check failed: " << status.toString8(); return fromBinderStatus(status); } } @@ -704,7 +727,12 @@ binder::Status IncrementalService::applyStorageParams(IncFsMount& ifs, bool enab } std::lock_guard l(mMountOperationLock); - return mVold->setIncFsMountOptions(control, enableReadLogs); + const auto status = mVold->setIncFsMountOptions(control, enableReadLogs); + if (status.isOk()) { + // Store enabled state. + ifs.setReadLogsEnabled(enableReadLogs); + } + return status; } void IncrementalService::deleteStorage(StorageId storageId) { @@ -1052,6 +1080,74 @@ bool IncrementalService::startLoading(StorageId storage) const { return true; } +void IncrementalService::setUidReadTimeouts( + StorageId storage, const std::vector<PerUidReadTimeouts>& perUidReadTimeouts) { + using microseconds = std::chrono::microseconds; + using milliseconds = std::chrono::milliseconds; + + auto maxPendingTimeUs = microseconds(0); + for (const auto& timeouts : perUidReadTimeouts) { + maxPendingTimeUs = std::max(maxPendingTimeUs, microseconds(timeouts.maxPendingTimeUs)); + } + if (maxPendingTimeUs < Constants::minPerUidTimeout) { + return; + } + + const auto ifs = getIfs(storage); + if (!ifs) { + return; + } + + if (auto err = mIncFs->setUidReadTimeouts(ifs->control, perUidReadTimeouts); err < 0) { + LOG(ERROR) << "Setting read timeouts failed: " << -err; + return; + } + + const auto timeout = std::chrono::duration_cast<milliseconds>(maxPendingTimeUs) - + Constants::perUidTimeoutOffset; + updateUidReadTimeouts(storage, Clock::now() + timeout); +} + +void IncrementalService::clearUidReadTimeouts(StorageId storage) { + const auto ifs = getIfs(storage); + if (!ifs) { + return; + } + + mIncFs->setUidReadTimeouts(ifs->control, {}); +} + +void IncrementalService::updateUidReadTimeouts(StorageId storage, Clock::time_point timeLimit) { + // Reached maximum timeout. + if (Clock::now() >= timeLimit) { + return clearUidReadTimeouts(storage); + } + + // Still loading? + const auto progress = getLoadingProgress(storage); + if (progress.isError()) { + // Something is wrong, abort. + return clearUidReadTimeouts(storage); + } + + if (progress.started() && progress.fullyLoaded()) { + // Fully loaded, check readLogs collection. + const auto ifs = getIfs(storage); + if (!ifs->readLogsEnabled()) { + return clearUidReadTimeouts(storage); + } + } + + const auto timeLeft = timeLimit - Clock::now(); + if (timeLeft < Constants::progressUpdateInterval) { + // Don't bother. + return clearUidReadTimeouts(storage); + } + + addTimedJob(*mTimedQueue, storage, Constants::progressUpdateInterval, + [this, storage, timeLimit]() { updateUidReadTimeouts(storage, timeLimit); }); +} + std::unordered_set<std::string_view> IncrementalService::adoptMountedInstances() { std::unordered_set<std::string_view> mountedRootNames; mIncFs->listExistingMounts([this, &mountedRootNames](auto root, auto backingDir, auto binds) { @@ -1125,7 +1221,7 @@ std::unordered_set<std::string_view> IncrementalService::adoptMountedInstances() // Check if marker file present. if (checkReadLogsDisabledMarker(root)) { - ifs->disableReadLogs(); + ifs->disallowReadLogs(); } std::vector<std::pair<std::string, metadata::BindPoint>> permanentBindPoints; @@ -1301,7 +1397,7 @@ bool IncrementalService::mountExistingImage(std::string_view root) { // Check if marker file present. if (checkReadLogsDisabledMarker(mountTarget)) { - ifs->disableReadLogs(); + ifs->disallowReadLogs(); } // DataLoader params @@ -1705,7 +1801,7 @@ int IncrementalService::setFileContent(const IfsMountPtr& ifs, const incfs::File return 0; } -int IncrementalService::isFileFullyLoaded(StorageId storage, const std::string& path) const { +int IncrementalService::isFileFullyLoaded(StorageId storage, std::string_view filePath) const { std::unique_lock l(mLock); const auto ifs = getIfsLocked(storage); if (!ifs) { @@ -1718,7 +1814,7 @@ int IncrementalService::isFileFullyLoaded(StorageId storage, const std::string& return -EINVAL; } l.unlock(); - return isFileFullyLoadedFromPath(*ifs, path); + return isFileFullyLoadedFromPath(*ifs, filePath); } int IncrementalService::isFileFullyLoadedFromPath(const IncFsMount& ifs, @@ -1736,25 +1832,26 @@ int IncrementalService::isFileFullyLoadedFromPath(const IncFsMount& ifs, return totalBlocks - filledBlocks; } -float IncrementalService::getLoadingProgress(StorageId storage) const { +IncrementalService::LoadingProgress IncrementalService::getLoadingProgress( + StorageId storage) const { std::unique_lock l(mLock); const auto ifs = getIfsLocked(storage); if (!ifs) { LOG(ERROR) << "getLoadingProgress failed, invalid storageId: " << storage; - return -EINVAL; + return {-EINVAL, -EINVAL}; } const auto storageInfo = ifs->storages.find(storage); if (storageInfo == ifs->storages.end()) { LOG(ERROR) << "getLoadingProgress failed, no storage: " << storage; - return -EINVAL; + return {-EINVAL, -EINVAL}; } l.unlock(); return getLoadingProgressFromPath(*ifs, storageInfo->second.name); } -float IncrementalService::getLoadingProgressFromPath(const IncFsMount& ifs, - std::string_view storagePath) const { - size_t totalBlocks = 0, filledBlocks = 0; +IncrementalService::LoadingProgress IncrementalService::getLoadingProgressFromPath( + const IncFsMount& ifs, std::string_view storagePath) const { + ssize_t totalBlocks = 0, filledBlocks = 0; const auto filePaths = mFs->listFilesRecursive(storagePath); for (const auto& filePath : filePaths) { const auto [filledBlocksCount, totalBlocksCount] = @@ -1762,33 +1859,29 @@ float IncrementalService::getLoadingProgressFromPath(const IncFsMount& ifs, if (filledBlocksCount < 0) { LOG(ERROR) << "getLoadingProgress failed to get filled blocks count for: " << filePath << " errno: " << filledBlocksCount; - return filledBlocksCount; + return {filledBlocksCount, filledBlocksCount}; } totalBlocks += totalBlocksCount; filledBlocks += filledBlocksCount; } - if (totalBlocks == 0) { - // No file in the storage or files are empty; regarded as fully loaded - return 1; - } - return (float)filledBlocks / (float)totalBlocks; + return {filledBlocks, totalBlocks}; } bool IncrementalService::updateLoadingProgress( StorageId storage, const StorageLoadingProgressListener& progressListener) { const auto progress = getLoadingProgress(storage); - if (progress < 0) { + if (progress.isError()) { // Failed to get progress from incfs, abort. return false; } - progressListener->onStorageLoadingProgressChanged(storage, progress); - if (progress > 1 - 0.001f) { + progressListener->onStorageLoadingProgressChanged(storage, progress.getProgress()); + if (progress.fullyLoaded()) { // Stop updating progress once it is fully loaded return true; } - static constexpr auto kProgressUpdateInterval = 1000ms; - addTimedJob(*mProgressUpdateJobQueue, storage, kProgressUpdateInterval /* repeat after 1s */, + addTimedJob(*mProgressUpdateJobQueue, storage, + Constants::progressUpdateInterval /* repeat after 1s */, [storage, progressListener, this]() { updateLoadingProgress(storage, progressListener); }); diff --git a/services/incremental/IncrementalService.h b/services/incremental/IncrementalService.h index eb69470c97a7..306612159412 100644 --- a/services/incremental/IncrementalService.h +++ b/services/incremental/IncrementalService.h @@ -23,6 +23,7 @@ #include <android/os/incremental/BnIncrementalServiceConnector.h> #include <android/os/incremental/BnStorageHealthListener.h> #include <android/os/incremental/BnStorageLoadingProgressListener.h> +#include <android/os/incremental/PerUidReadTimeouts.h> #include <android/os/incremental/StorageHealthCheckParams.h> #include <binder/IAppOpsCallback.h> #include <utils/String16.h> @@ -69,6 +70,8 @@ using StorageHealthListener = ::android::sp<IStorageHealthListener>; using IStorageLoadingProgressListener = ::android::os::incremental::IStorageLoadingProgressListener; using StorageLoadingProgressListener = ::android::sp<IStorageLoadingProgressListener>; +using PerUidReadTimeouts = ::android::os::incremental::PerUidReadTimeouts; + class IncrementalService final { public: explicit IncrementalService(ServiceManagerWrapper&& sm, std::string_view rootDir); @@ -98,7 +101,23 @@ public: }; enum StorageFlags { - ReadLogsEnabled = 1, + ReadLogsAllowed = 1 << 0, + ReadLogsEnabled = 1 << 1, + }; + + struct LoadingProgress { + ssize_t filledBlocks; + ssize_t totalBlocks; + + bool isError() const { return totalBlocks < 0; } + bool started() const { return totalBlocks > 0; } + bool fullyLoaded() const { return !isError() && (totalBlocks == filledBlocks); } + + float getProgress() const { + return totalBlocks < 0 + ? totalBlocks + : totalBlocks > 0 ? double(filledBlocks) / double(totalBlocks) : 1.f; + } }; static FileId idFromMetadata(std::span<const uint8_t> metadata); @@ -114,7 +133,8 @@ public: content::pm::DataLoaderParamsParcel&& dataLoaderParams, CreateOptions options, const DataLoaderStatusListener& statusListener, StorageHealthCheckParams&& healthCheckParams, - const StorageHealthListener& healthListener); + const StorageHealthListener& healthListener, + const std::vector<PerUidReadTimeouts>& perUidReadTimeouts); StorageId createLinkedStorage(std::string_view mountPoint, StorageId linkedStorage, CreateOptions options = CreateOptions::Default); StorageId openStorage(std::string_view path); @@ -123,7 +143,7 @@ public: int unbind(StorageId storage, std::string_view target); void deleteStorage(StorageId storage); - void disableReadLogs(StorageId storage); + void disallowReadLogs(StorageId storage); int setStorageParams(StorageId storage, bool enableReadLogs); int makeFile(StorageId storage, std::string_view path, int mode, FileId id, @@ -135,8 +155,8 @@ public: std::string_view newPath); int unlink(StorageId storage, std::string_view path); - int isFileFullyLoaded(StorageId storage, const std::string& path) const; - float getLoadingProgress(StorageId storage) const; + int isFileFullyLoaded(StorageId storage, std::string_view filePath) const; + LoadingProgress getLoadingProgress(StorageId storage) const; bool registerLoadingProgressListener(StorageId storage, const StorageLoadingProgressListener& progressListener); bool unregisterLoadingProgressListener(StorageId storage); @@ -282,7 +302,7 @@ private: const std::string root; Control control; /*const*/ MountId mountId; - int32_t flags = StorageFlags::ReadLogsEnabled; + int32_t flags = StorageFlags::ReadLogsAllowed; StorageMap storages; BindMap bindPoints; DataLoaderStubPtr dataLoaderStub; @@ -301,7 +321,15 @@ private: StorageMap::iterator makeStorage(StorageId id); - void disableReadLogs() { flags &= ~StorageFlags::ReadLogsEnabled; } + void disallowReadLogs() { flags &= ~StorageFlags::ReadLogsAllowed; } + int32_t readLogsAllowed() const { return (flags & StorageFlags::ReadLogsAllowed); } + + void setReadLogsEnabled(bool value) { + if (value) + flags |= StorageFlags::ReadLogsEnabled; + else + flags &= ~StorageFlags::ReadLogsEnabled; + } int32_t readLogsEnabled() const { return (flags & StorageFlags::ReadLogsEnabled); } static void cleanupFilesystem(std::string_view root); @@ -313,6 +341,11 @@ private: static bool perfLoggingEnabled(); + void setUidReadTimeouts(StorageId storage, + const std::vector<PerUidReadTimeouts>& perUidReadTimeouts); + void clearUidReadTimeouts(StorageId storage); + void updateUidReadTimeouts(StorageId storage, Clock::time_point timeLimit); + std::unordered_set<std::string_view> adoptMountedInstances(); void mountExistingImages(const std::unordered_set<std::string_view>& mountedRootNames); bool mountExistingImage(std::string_view root); @@ -355,7 +388,7 @@ private: binder::Status applyStorageParams(IncFsMount& ifs, bool enableReadLogs); int isFileFullyLoadedFromPath(const IncFsMount& ifs, std::string_view filePath) const; - float getLoadingProgressFromPath(const IncFsMount& ifs, std::string_view path) const; + LoadingProgress getLoadingProgressFromPath(const IncFsMount& ifs, std::string_view path) const; int setFileContent(const IfsMountPtr& ifs, const incfs::FileId& fileId, std::string_view debugFilePath, std::span<const uint8_t> data) const; diff --git a/services/incremental/IncrementalServiceValidation.cpp b/services/incremental/IncrementalServiceValidation.cpp index abadbbf10742..9f2639a81666 100644 --- a/services/incremental/IncrementalServiceValidation.cpp +++ b/services/incremental/IncrementalServiceValidation.cpp @@ -56,13 +56,18 @@ binder::Status CheckPermissionForDataDelivery(const char* permission, const char String16 packageName{package}; - // Caller must also have op granted. PermissionController pc; if (auto packageUid = pc.getPackageUid(packageName, 0); packageUid != uid) { return Exception(binder::Status::EX_SECURITY, StringPrintf("UID %d / PID %d does not own package %s", uid, pid, package)); } + + if (!operation) { + return binder::Status::ok(); + } + + // Caller must also have op granted. switch (auto result = pc.noteOp(String16(operation), uid, packageName); result) { case PermissionController::MODE_ALLOWED: case PermissionController::MODE_DEFAULT: diff --git a/services/incremental/ServiceWrappers.cpp b/services/incremental/ServiceWrappers.cpp index dfe9684779fe..b1521b0d4e27 100644 --- a/services/incremental/ServiceWrappers.cpp +++ b/services/incremental/ServiceWrappers.cpp @@ -206,6 +206,11 @@ public: std::vector<incfs::ReadInfo>* pendingReadsBuffer) const final { return incfs::waitForPendingReads(control, timeout, pendingReadsBuffer); } + ErrorCode setUidReadTimeouts(const Control& control, + const std::vector<android::os::incremental::PerUidReadTimeouts>& + perUidReadTimeouts) const final { + return -ENOTSUP; + } }; static JNIEnv* getOrAttachJniEnv(JavaVM* jvm); diff --git a/services/incremental/ServiceWrappers.h b/services/incremental/ServiceWrappers.h index f2d00735bc44..fad8d67e0da7 100644 --- a/services/incremental/ServiceWrappers.h +++ b/services/incremental/ServiceWrappers.h @@ -21,6 +21,7 @@ #include <android/content/pm/FileSystemControlParcel.h> #include <android/content/pm/IDataLoader.h> #include <android/content/pm/IDataLoaderStatusListener.h> +#include <android/os/incremental/PerUidReadTimeouts.h> #include <binder/IAppOpsCallback.h> #include <binder/IServiceManager.h> #include <binder/Status.h> @@ -103,6 +104,10 @@ public: virtual WaitResult waitForPendingReads( const Control& control, std::chrono::milliseconds timeout, std::vector<incfs::ReadInfo>* pendingReadsBuffer) const = 0; + virtual ErrorCode setUidReadTimeouts( + const Control& control, + const std::vector<::android::os::incremental::PerUidReadTimeouts>& perUidReadTimeouts) + const = 0; }; class AppOpsManagerWrapper { diff --git a/services/incremental/test/IncrementalServiceTest.cpp b/services/incremental/test/IncrementalServiceTest.cpp index 9b8cf4084bf1..47b9051970e4 100644 --- a/services/incremental/test/IncrementalServiceTest.cpp +++ b/services/incremental/test/IncrementalServiceTest.cpp @@ -42,6 +42,7 @@ using testing::NiceMock; using namespace android::incfs; using namespace android::content::pm; +using PerUidReadTimeouts = android::os::incremental::PerUidReadTimeouts; namespace android::os::incremental { @@ -307,6 +308,9 @@ public: MOCK_CONST_METHOD3(waitForPendingReads, WaitResult(const Control& control, std::chrono::milliseconds timeout, std::vector<incfs::ReadInfo>* pendingReadsBuffer)); + MOCK_CONST_METHOD2(setUidReadTimeouts, + ErrorCode(const Control& control, + const std::vector<PerUidReadTimeouts>& perUidReadTimeouts)); MockIncFs() { ON_CALL(*this, listExistingMounts(_)).WillByDefault(Return()); } @@ -393,6 +397,15 @@ public: void checkPermissionSuccess() { ON_CALL(*this, checkPermission(_, _, _)).WillByDefault(Return(android::incremental::Ok())); } + void checkPermissionNoCrossUsers() { + ON_CALL(*this, + checkPermission("android.permission.LOADER_USAGE_STATS", + "android:loader_usage_stats", _)) + .WillByDefault(Return(android::incremental::Ok())); + ON_CALL(*this, checkPermission("android.permission.INTERACT_ACROSS_USERS", nullptr, _)) + .WillByDefault( + Return(android::incremental::Exception(binder::Status::EX_SECURITY, {}))); + } void checkPermissionFails() { ON_CALL(*this, checkPermission(_, _, _)) .WillByDefault( @@ -665,7 +678,7 @@ TEST_F(IncrementalServiceTest, testCreateStorageMountIncFsFails) { TemporaryDir tempDir; int storageId = mIncrementalService->createStorage(tempDir.path, std::move(mDataLoaderParcel), IncrementalService::CreateOptions::CreateNew, - {}, {}, {}); + {}, {}, {}, {}); ASSERT_LT(storageId, 0); } @@ -676,7 +689,7 @@ TEST_F(IncrementalServiceTest, testCreateStorageMountIncFsInvalidControlParcel) TemporaryDir tempDir; int storageId = mIncrementalService->createStorage(tempDir.path, std::move(mDataLoaderParcel), IncrementalService::CreateOptions::CreateNew, - {}, {}, {}); + {}, {}, {}, {}); ASSERT_LT(storageId, 0); } @@ -689,7 +702,7 @@ TEST_F(IncrementalServiceTest, testCreateStorageMakeFileFails) { TemporaryDir tempDir; int storageId = mIncrementalService->createStorage(tempDir.path, std::move(mDataLoaderParcel), IncrementalService::CreateOptions::CreateNew, - {}, {}, {}); + {}, {}, {}, {}); ASSERT_LT(storageId, 0); } @@ -703,7 +716,7 @@ TEST_F(IncrementalServiceTest, testCreateStorageBindMountFails) { TemporaryDir tempDir; int storageId = mIncrementalService->createStorage(tempDir.path, std::move(mDataLoaderParcel), IncrementalService::CreateOptions::CreateNew, - {}, {}, {}); + {}, {}, {}, {}); ASSERT_LT(storageId, 0); } @@ -721,7 +734,7 @@ TEST_F(IncrementalServiceTest, testCreateStoragePrepareDataLoaderFails) { TemporaryDir tempDir; int storageId = mIncrementalService->createStorage(tempDir.path, std::move(mDataLoaderParcel), IncrementalService::CreateOptions::CreateNew, - {}, {}, {}); + {}, {}, {}, {}); ASSERT_LT(storageId, 0); } @@ -735,7 +748,7 @@ TEST_F(IncrementalServiceTest, testDeleteStorageSuccess) { TemporaryDir tempDir; int storageId = mIncrementalService->createStorage(tempDir.path, std::move(mDataLoaderParcel), IncrementalService::CreateOptions::CreateNew, - {}, {}, {}); + {}, {}, {}, {}); ASSERT_GE(storageId, 0); mIncrementalService->deleteStorage(storageId); } @@ -750,7 +763,7 @@ TEST_F(IncrementalServiceTest, testDataLoaderDestroyed) { TemporaryDir tempDir; int storageId = mIncrementalService->createStorage(tempDir.path, std::move(mDataLoaderParcel), IncrementalService::CreateOptions::CreateNew, - {}, {}, {}); + {}, {}, {}, {}); ASSERT_GE(storageId, 0); // Simulated crash/other connection breakage. mDataLoaderManager->setDataLoaderStatusDestroyed(); @@ -767,7 +780,7 @@ TEST_F(IncrementalServiceTest, testStartDataLoaderCreate) { TemporaryDir tempDir; int storageId = mIncrementalService->createStorage(tempDir.path, std::move(mDataLoaderParcel), IncrementalService::CreateOptions::CreateNew, - {}, {}, {}); + {}, {}, {}, {}); ASSERT_GE(storageId, 0); mDataLoaderManager->setDataLoaderStatusCreated(); ASSERT_TRUE(mIncrementalService->startLoading(storageId)); @@ -785,7 +798,7 @@ TEST_F(IncrementalServiceTest, testStartDataLoaderPendingStart) { TemporaryDir tempDir; int storageId = mIncrementalService->createStorage(tempDir.path, std::move(mDataLoaderParcel), IncrementalService::CreateOptions::CreateNew, - {}, {}, {}); + {}, {}, {}, {}); ASSERT_GE(storageId, 0); ASSERT_TRUE(mIncrementalService->startLoading(storageId)); mDataLoaderManager->setDataLoaderStatusCreated(); @@ -802,7 +815,7 @@ TEST_F(IncrementalServiceTest, testStartDataLoaderCreateUnavailable) { TemporaryDir tempDir; int storageId = mIncrementalService->createStorage(tempDir.path, std::move(mDataLoaderParcel), IncrementalService::CreateOptions::CreateNew, - {}, {}, {}); + {}, {}, {}, {}); ASSERT_GE(storageId, 0); mDataLoaderManager->setDataLoaderStatusUnavailable(); } @@ -823,7 +836,7 @@ TEST_F(IncrementalServiceTest, testStartDataLoaderRecreateOnPendingReads) { TemporaryDir tempDir; int storageId = mIncrementalService->createStorage(tempDir.path, std::move(mDataLoaderParcel), IncrementalService::CreateOptions::CreateNew, - {}, {}, {}); + {}, {}, {}, {}); ASSERT_GE(storageId, 0); mDataLoaderManager->setDataLoaderStatusUnavailable(); ASSERT_NE(nullptr, mLooper->mCallback); @@ -877,7 +890,7 @@ TEST_F(IncrementalServiceTest, testStartDataLoaderUnhealthyStorage) { TemporaryDir tempDir; int storageId = mIncrementalService->createStorage(tempDir.path, std::move(mDataLoaderParcel), IncrementalService::CreateOptions::CreateNew, - {}, std::move(params), listener); + {}, std::move(params), listener, {}); ASSERT_GE(storageId, 0); // Healthy state, registered for pending reads. @@ -972,7 +985,7 @@ TEST_F(IncrementalServiceTest, testSetIncFsMountOptionsSuccess) { TemporaryDir tempDir; int storageId = mIncrementalService->createStorage(tempDir.path, std::move(mDataLoaderParcel), IncrementalService::CreateOptions::CreateNew, - {}, {}, {}); + {}, {}, {}, {}); ASSERT_GE(storageId, 0); ASSERT_GE(mDataLoader->setStorageParams(true), 0); } @@ -993,11 +1006,11 @@ TEST_F(IncrementalServiceTest, testSetIncFsMountOptionsSuccessAndDisabled) { TemporaryDir tempDir; int storageId = mIncrementalService->createStorage(tempDir.path, std::move(mDataLoaderParcel), IncrementalService::CreateOptions::CreateNew, - {}, {}, {}); + {}, {}, {}, {}); ASSERT_GE(storageId, 0); ASSERT_GE(mDataLoader->setStorageParams(true), 0); // Now disable. - mIncrementalService->disableReadLogs(storageId); + mIncrementalService->disallowReadLogs(storageId); ASSERT_EQ(mDataLoader->setStorageParams(true), -EPERM); } @@ -1019,7 +1032,7 @@ TEST_F(IncrementalServiceTest, testSetIncFsMountOptionsSuccessAndPermissionChang TemporaryDir tempDir; int storageId = mIncrementalService->createStorage(tempDir.path, std::move(mDataLoaderParcel), IncrementalService::CreateOptions::CreateNew, - {}, {}, {}); + {}, {}, {}, {}); ASSERT_GE(storageId, 0); ASSERT_GE(mDataLoader->setStorageParams(true), 0); ASSERT_NE(nullptr, mAppOpsManager->mStoredCallback.get()); @@ -1038,7 +1051,24 @@ TEST_F(IncrementalServiceTest, testSetIncFsMountOptionsCheckPermissionFails) { TemporaryDir tempDir; int storageId = mIncrementalService->createStorage(tempDir.path, std::move(mDataLoaderParcel), IncrementalService::CreateOptions::CreateNew, - {}, {}, {}); + {}, {}, {}, {}); + ASSERT_GE(storageId, 0); + ASSERT_LT(mDataLoader->setStorageParams(true), 0); +} + +TEST_F(IncrementalServiceTest, testSetIncFsMountOptionsCheckPermissionNoCrossUsers) { + mAppOpsManager->checkPermissionNoCrossUsers(); + + EXPECT_CALL(*mDataLoaderManager, unbindFromDataLoader(_)); + EXPECT_CALL(*mVold, unmountIncFs(_)).Times(2); + // checkPermission fails, no calls to set opitions, start or stop WatchingMode. + EXPECT_CALL(*mVold, setIncFsMountOptions(_, true)).Times(0); + EXPECT_CALL(*mAppOpsManager, startWatchingMode(_, _, _)).Times(0); + EXPECT_CALL(*mAppOpsManager, stopWatchingMode(_)).Times(0); + TemporaryDir tempDir; + int storageId = mIncrementalService->createStorage(tempDir.path, std::move(mDataLoaderParcel), + IncrementalService::CreateOptions::CreateNew, + {}, {}, {}, {}); ASSERT_GE(storageId, 0); ASSERT_LT(mDataLoader->setStorageParams(true), 0); } @@ -1057,7 +1087,7 @@ TEST_F(IncrementalServiceTest, testSetIncFsMountOptionsFails) { TemporaryDir tempDir; int storageId = mIncrementalService->createStorage(tempDir.path, std::move(mDataLoaderParcel), IncrementalService::CreateOptions::CreateNew, - {}, {}, {}); + {}, {}, {}, {}); ASSERT_GE(storageId, 0); ASSERT_LT(mDataLoader->setStorageParams(true), 0); } @@ -1066,7 +1096,7 @@ TEST_F(IncrementalServiceTest, testMakeDirectory) { TemporaryDir tempDir; int storageId = mIncrementalService->createStorage(tempDir.path, std::move(mDataLoaderParcel), IncrementalService::CreateOptions::CreateNew, - {}, {}, {}); + {}, {}, {}, {}); std::string dir_path("test"); // Expecting incfs to call makeDir on a path like: @@ -1085,7 +1115,7 @@ TEST_F(IncrementalServiceTest, testMakeDirectories) { TemporaryDir tempDir; int storageId = mIncrementalService->createStorage(tempDir.path, std::move(mDataLoaderParcel), IncrementalService::CreateOptions::CreateNew, - {}, {}, {}); + {}, {}, {}, {}); auto first = "first"sv; auto second = "second"sv; auto third = "third"sv; @@ -1108,7 +1138,7 @@ TEST_F(IncrementalServiceTest, testIsFileFullyLoadedFailsWithNoFile) { TemporaryDir tempDir; int storageId = mIncrementalService->createStorage(tempDir.path, std::move(mDataLoaderParcel), IncrementalService::CreateOptions::CreateNew, - {}, {}, {}); + {}, {}, {}, {}); ASSERT_EQ(-1, mIncrementalService->isFileFullyLoaded(storageId, "base.apk")); } @@ -1119,7 +1149,7 @@ TEST_F(IncrementalServiceTest, testIsFileFullyLoadedFailsWithFailedRanges) { TemporaryDir tempDir; int storageId = mIncrementalService->createStorage(tempDir.path, std::move(mDataLoaderParcel), IncrementalService::CreateOptions::CreateNew, - {}, {}, {}); + {}, {}, {}, {}); EXPECT_CALL(*mIncFs, countFilledBlocks(_, _)).Times(1); ASSERT_EQ(-1, mIncrementalService->isFileFullyLoaded(storageId, "base.apk")); } @@ -1131,7 +1161,7 @@ TEST_F(IncrementalServiceTest, testIsFileFullyLoadedSuccessWithEmptyRanges) { TemporaryDir tempDir; int storageId = mIncrementalService->createStorage(tempDir.path, std::move(mDataLoaderParcel), IncrementalService::CreateOptions::CreateNew, - {}, {}, {}); + {}, {}, {}, {}); EXPECT_CALL(*mIncFs, countFilledBlocks(_, _)).Times(1); ASSERT_EQ(0, mIncrementalService->isFileFullyLoaded(storageId, "base.apk")); } @@ -1143,7 +1173,7 @@ TEST_F(IncrementalServiceTest, testIsFileFullyLoadedSuccess) { TemporaryDir tempDir; int storageId = mIncrementalService->createStorage(tempDir.path, std::move(mDataLoaderParcel), IncrementalService::CreateOptions::CreateNew, - {}, {}, {}); + {}, {}, {}, {}); EXPECT_CALL(*mIncFs, countFilledBlocks(_, _)).Times(1); ASSERT_EQ(0, mIncrementalService->isFileFullyLoaded(storageId, "base.apk")); } @@ -1155,8 +1185,8 @@ TEST_F(IncrementalServiceTest, testGetLoadingProgressSuccessWithNoFile) { TemporaryDir tempDir; int storageId = mIncrementalService->createStorage(tempDir.path, std::move(mDataLoaderParcel), IncrementalService::CreateOptions::CreateNew, - {}, {}, {}); - ASSERT_EQ(1, mIncrementalService->getLoadingProgress(storageId)); + {}, {}, {}, {}); + ASSERT_EQ(1, mIncrementalService->getLoadingProgress(storageId).getProgress()); } TEST_F(IncrementalServiceTest, testGetLoadingProgressFailsWithFailedRanges) { @@ -1166,9 +1196,9 @@ TEST_F(IncrementalServiceTest, testGetLoadingProgressFailsWithFailedRanges) { TemporaryDir tempDir; int storageId = mIncrementalService->createStorage(tempDir.path, std::move(mDataLoaderParcel), IncrementalService::CreateOptions::CreateNew, - {}, {}, {}); + {}, {}, {}, {}); EXPECT_CALL(*mIncFs, countFilledBlocks(_, _)).Times(1); - ASSERT_EQ(-1, mIncrementalService->getLoadingProgress(storageId)); + ASSERT_EQ(-1, mIncrementalService->getLoadingProgress(storageId).getProgress()); } TEST_F(IncrementalServiceTest, testGetLoadingProgressSuccessWithEmptyRanges) { @@ -1178,9 +1208,9 @@ TEST_F(IncrementalServiceTest, testGetLoadingProgressSuccessWithEmptyRanges) { TemporaryDir tempDir; int storageId = mIncrementalService->createStorage(tempDir.path, std::move(mDataLoaderParcel), IncrementalService::CreateOptions::CreateNew, - {}, {}, {}); + {}, {}, {}, {}); EXPECT_CALL(*mIncFs, countFilledBlocks(_, _)).Times(3); - ASSERT_EQ(1, mIncrementalService->getLoadingProgress(storageId)); + ASSERT_EQ(1, mIncrementalService->getLoadingProgress(storageId).getProgress()); } TEST_F(IncrementalServiceTest, testGetLoadingProgressSuccess) { @@ -1190,9 +1220,9 @@ TEST_F(IncrementalServiceTest, testGetLoadingProgressSuccess) { TemporaryDir tempDir; int storageId = mIncrementalService->createStorage(tempDir.path, std::move(mDataLoaderParcel), IncrementalService::CreateOptions::CreateNew, - {}, {}, {}); + {}, {}, {}, {}); EXPECT_CALL(*mIncFs, countFilledBlocks(_, _)).Times(3); - ASSERT_EQ(0.5, mIncrementalService->getLoadingProgress(storageId)); + ASSERT_EQ(0.5, mIncrementalService->getLoadingProgress(storageId).getProgress()); } TEST_F(IncrementalServiceTest, testRegisterLoadingProgressListenerSuccess) { @@ -1202,7 +1232,7 @@ TEST_F(IncrementalServiceTest, testRegisterLoadingProgressListenerSuccess) { TemporaryDir tempDir; int storageId = mIncrementalService->createStorage(tempDir.path, std::move(mDataLoaderParcel), IncrementalService::CreateOptions::CreateNew, - {}, {}, {}); + {}, {}, {}, {}); sp<NiceMock<MockStorageLoadingProgressListener>> listener{ new NiceMock<MockStorageLoadingProgressListener>}; NiceMock<MockStorageLoadingProgressListener>* listenerMock = listener.get(); @@ -1227,7 +1257,7 @@ TEST_F(IncrementalServiceTest, testRegisterLoadingProgressListenerFailsToGetProg TemporaryDir tempDir; int storageId = mIncrementalService->createStorage(tempDir.path, std::move(mDataLoaderParcel), IncrementalService::CreateOptions::CreateNew, - {}, {}, {}); + {}, {}, {}, {}); sp<NiceMock<MockStorageLoadingProgressListener>> listener{ new NiceMock<MockStorageLoadingProgressListener>}; NiceMock<MockStorageLoadingProgressListener>* listenerMock = listener.get(); @@ -1242,9 +1272,10 @@ TEST_F(IncrementalServiceTest, testRegisterStorageHealthListenerSuccess) { NiceMock<MockStorageHealthListener>* newListenerMock = newListener.get(); TemporaryDir tempDir; - int storageId = mIncrementalService->createStorage(tempDir.path, std::move(mDataLoaderParcel), - IncrementalService::CreateOptions::CreateNew, - {}, StorageHealthCheckParams{}, listener); + int storageId = + mIncrementalService->createStorage(tempDir.path, std::move(mDataLoaderParcel), + IncrementalService::CreateOptions::CreateNew, {}, + StorageHealthCheckParams{}, listener, {}); ASSERT_GE(storageId, 0); StorageHealthCheckParams newParams; newParams.blockedTimeoutMs = 10000; @@ -1313,4 +1344,123 @@ TEST_F(IncrementalServiceTest, testRegisterStorageHealthListenerSuccess) { mTimedQueue->clearJob(storageId); } +static std::vector<PerUidReadTimeouts> createPerUidTimeouts( + std::initializer_list<std::tuple<int, int, int, int>> tuples) { + std::vector<PerUidReadTimeouts> result; + for (auto&& tuple : tuples) { + result.emplace_back(); + auto& timeouts = result.back(); + timeouts.uid = std::get<0>(tuple); + timeouts.minTimeUs = std::get<1>(tuple); + timeouts.minPendingTimeUs = std::get<2>(tuple); + timeouts.maxPendingTimeUs = std::get<3>(tuple); + } + return result; +} + +static ErrorCode checkPerUidTimeouts(const Control& control, + const std::vector<PerUidReadTimeouts>& perUidReadTimeouts) { + std::vector<PerUidReadTimeouts> expected = + createPerUidTimeouts({{0, 1, 2, 3}, {1, 2, 3, 4}, {2, 3, 4, 100000000}}); + EXPECT_EQ(expected, perUidReadTimeouts); + return 0; +} + +static ErrorCode checkPerUidTimeoutsEmpty( + const Control& control, const std::vector<PerUidReadTimeouts>& perUidReadTimeouts) { + EXPECT_EQ(0u, perUidReadTimeouts.size()); + return 0; +} + +TEST_F(IncrementalServiceTest, testPerUidTimeoutsTooShort) { + EXPECT_CALL(*mDataLoaderManager, bindToDataLoader(_, _, _, _)).Times(1); + EXPECT_CALL(*mDataLoaderManager, unbindFromDataLoader(_)).Times(1); + EXPECT_CALL(*mDataLoader, create(_, _, _, _)).Times(1); + EXPECT_CALL(*mDataLoader, start(_)).Times(0); + EXPECT_CALL(*mDataLoader, destroy(_)).Times(1); + EXPECT_CALL(*mIncFs, setUidReadTimeouts(_, _)).Times(0); + EXPECT_CALL(*mTimedQueue, addJob(_, _, _)).Times(0); + EXPECT_CALL(*mVold, unmountIncFs(_)).Times(2); + TemporaryDir tempDir; + int storageId = + mIncrementalService->createStorage(tempDir.path, std::move(mDataLoaderParcel), + IncrementalService::CreateOptions::CreateNew, {}, {}, + {}, + createPerUidTimeouts( + {{0, 1, 2, 3}, {1, 2, 3, 4}, {2, 3, 4, 5}})); + ASSERT_GE(storageId, 0); +} + +TEST_F(IncrementalServiceTest, testPerUidTimeoutsSuccess) { + mVold->setIncFsMountOptionsSuccess(); + mAppOpsManager->checkPermissionSuccess(); + mFs->hasFiles(); + + EXPECT_CALL(*mIncFs, setUidReadTimeouts(_, _)) + // First call. + .WillOnce(Invoke(&checkPerUidTimeouts)) + // Fully loaded and no readlogs. + .WillOnce(Invoke(&checkPerUidTimeoutsEmpty)); + EXPECT_CALL(*mTimedQueue, addJob(_, _, _)).Times(3); + + // Empty storage. + mIncFs->countFilledBlocksEmpty(); + + TemporaryDir tempDir; + int storageId = + mIncrementalService->createStorage(tempDir.path, std::move(mDataLoaderParcel), + IncrementalService::CreateOptions::CreateNew, {}, {}, + {}, + createPerUidTimeouts({{0, 1, 2, 3}, + {1, 2, 3, 4}, + {2, 3, 4, 100000000}})); + ASSERT_GE(storageId, 0); + + { + // Timed callback present -> 0 progress. + ASSERT_EQ(storageId, mTimedQueue->mId); + ASSERT_GE(mTimedQueue->mAfter, std::chrono::seconds(1)); + const auto timedCallback = mTimedQueue->mWhat; + mTimedQueue->clearJob(storageId); + + // Still loading. + mIncFs->countFilledBlocksSuccess(); + + // Call it again. + timedCallback(); + } + + { + // Still present -> 0.5 progress. + ASSERT_EQ(storageId, mTimedQueue->mId); + ASSERT_GE(mTimedQueue->mAfter, std::chrono::seconds(1)); + const auto timedCallback = mTimedQueue->mWhat; + mTimedQueue->clearJob(storageId); + + // Fully loaded but readlogs collection enabled. + mIncFs->countFilledBlocksFullyLoaded(); + ASSERT_GE(mDataLoader->setStorageParams(true), 0); + + // Call it again. + timedCallback(); + } + + { + // Still present -> fully loaded + readlogs. + ASSERT_EQ(storageId, mTimedQueue->mId); + ASSERT_GE(mTimedQueue->mAfter, std::chrono::seconds(1)); + const auto timedCallback = mTimedQueue->mWhat; + mTimedQueue->clearJob(storageId); + + // Now disable readlogs. + ASSERT_GE(mDataLoader->setStorageParams(false), 0); + + // Call it again. + timedCallback(); + } + + // No callbacks anymore -> fully loaded and no readlogs. + ASSERT_EQ(mTimedQueue->mAfter, Milliseconds()); +} + } // namespace android::os::incremental diff --git a/services/java/com/android/server/SystemServer.java b/services/java/com/android/server/SystemServer.java index d9350f39ee58..f8cbc355a18c 100644 --- a/services/java/com/android/server/SystemServer.java +++ b/services/java/com/android/server/SystemServer.java @@ -299,6 +299,8 @@ public final class SystemServer implements Dumpable { "com.android.server.autofill.AutofillManagerService"; private static final String CONTENT_CAPTURE_MANAGER_SERVICE_CLASS = "com.android.server.contentcapture.ContentCaptureManagerService"; + private static final String TRANSLATION_MANAGER_SERVICE_CLASS = + "com.android.server.translation.TranslationManagerService"; private static final String MUSIC_RECOGNITION_MANAGER_SERVICE_CLASS = "com.android.server.musicrecognition.MusicRecognitionManagerService"; private static final String SYSTEM_CAPTIONS_MANAGER_SERVICE_CLASS = @@ -1358,8 +1360,7 @@ public final class SystemServer implements Dumpable { } t.traceBegin("IpConnectivityMetrics"); - mSystemServiceManager.startServiceFromJar(IP_CONNECTIVITY_METRICS_CLASS, - CONNECTIVITY_SERVICE_APEX_PATH); + mSystemServiceManager.startService(IP_CONNECTIVITY_METRICS_CLASS); t.traceEnd(); t.traceBegin("NetworkWatchlistService"); @@ -2291,6 +2292,13 @@ public final class SystemServer implements Dumpable { t.traceEnd(); } + // Translation manager service + if (mPackageManager.hasSystemFeature(PackageManager.FEATURE_TRANSLATION)) { + t.traceBegin("StartTranslationManagerService"); + mSystemServiceManager.startService(TRANSLATION_MANAGER_SERVICE_CLASS); + t.traceEnd(); + } + // NOTE: ClipboardService depends on ContentCapture and Autofill t.traceBegin("StartClipboardService"); mSystemServiceManager.startService(ClipboardService.class); diff --git a/services/tests/mockingservicestests/src/com/android/server/AppStateTrackerTest.java b/services/tests/mockingservicestests/src/com/android/server/AppStateTrackerTest.java index 3220dff553d3..a691a8d44e48 100644 --- a/services/tests/mockingservicestests/src/com/android/server/AppStateTrackerTest.java +++ b/services/tests/mockingservicestests/src/com/android/server/AppStateTrackerTest.java @@ -313,29 +313,30 @@ public class AppStateTrackerTest { } } - private static final int NONE = 0; - private static final int ALARMS_ONLY = 1 << 0; - private static final int JOBS_ONLY = 1 << 1; - private static final int JOBS_AND_ALARMS = ALARMS_ONLY | JOBS_ONLY; - - private void areRestricted(AppStateTrackerTestable instance, int uid, String packageName, - int restrictionTypes, boolean exemptFromBatterySaver) { - assertEquals(((restrictionTypes & JOBS_ONLY) != 0), - instance.areJobsRestricted(uid, packageName, exemptFromBatterySaver)); - assertEquals(((restrictionTypes & ALARMS_ONLY) != 0), - instance.areAlarmsRestricted(uid, packageName, exemptFromBatterySaver)); + private void areJobsRestricted(AppStateTrackerTestable instance, int[] uids, String[] packages, + boolean[] restricted, boolean exemption) { + assertTrue(uids.length == packages.length && uids.length == restricted.length); + for (int i = 0; i < uids.length; i++) { + assertEquals(restricted[i], + instance.areJobsRestricted(uids[i], packages[i], exemption)); + } } - private void areRestricted(AppStateTrackerTestable instance, int uid, String packageName, - int restrictionTypes) { - areRestricted(instance, uid, packageName, restrictionTypes, - /*exemptFromBatterySaver=*/ false); + private void areAlarmsRestrictedByFAS(AppStateTrackerTestable instance, int[] uids, + String[] packages, boolean[] restricted) { + assertTrue(uids.length == packages.length && uids.length == restricted.length); + for (int i = 0; i < uids.length; i++) { + assertEquals(restricted[i], instance.areAlarmsRestricted(uids[i], packages[i])); + } } - private void areRestrictedWithExemption(AppStateTrackerTestable instance, - int uid, String packageName, int restrictionTypes) { - areRestricted(instance, uid, packageName, restrictionTypes, - /*exemptFromBatterySaver=*/ true); + private void areAlarmsRestrictedByBatterySaver(AppStateTrackerTestable instance, int[] uids, + String[] packages, boolean[] restricted) { + assertTrue(uids.length == packages.length && uids.length == restricted.length); + for (int i = 0; i < uids.length; i++) { + assertEquals(restricted[i], + instance.areAlarmsRestrictedByBatterySaver(uids[i], packages[i])); + } } @Test @@ -344,30 +345,42 @@ public class AppStateTrackerTest { callStart(instance); assertFalse(instance.isForceAllAppsStandbyEnabled()); - areRestricted(instance, UID_1, PACKAGE_1, NONE); - areRestricted(instance, UID_2, PACKAGE_2, NONE); - areRestricted(instance, Process.SYSTEM_UID, PACKAGE_SYSTEM, NONE); - - areRestrictedWithExemption(instance, UID_1, PACKAGE_1, NONE); - areRestrictedWithExemption(instance, UID_2, PACKAGE_2, NONE); - areRestrictedWithExemption(instance, Process.SYSTEM_UID, PACKAGE_SYSTEM, NONE); + areJobsRestricted(instance, + new int[] {UID_1, UID_2, UID_10_2, Process.SYSTEM_UID}, + new String[] {PACKAGE_1, PACKAGE_2, PACKAGE_2, PACKAGE_SYSTEM}, + new boolean[] {false, false, false, false}, + false); + areJobsRestricted(instance, + new int[] {UID_1, UID_2, UID_10_2, Process.SYSTEM_UID}, + new String[] {PACKAGE_1, PACKAGE_2, PACKAGE_2, PACKAGE_SYSTEM}, + new boolean[] {false, false, false, false}, + true); + areAlarmsRestrictedByBatterySaver(instance, + new int[] {UID_1, UID_2, UID_10_2, Process.SYSTEM_UID}, + new String[] {PACKAGE_1, PACKAGE_2, PACKAGE_2, PACKAGE_SYSTEM}, + new boolean[] {false, false, false, false}); mPowerSaveMode = true; mPowerSaveObserver.accept(getPowerSaveState()); assertTrue(instance.isForceAllAppsStandbyEnabled()); - areRestricted(instance, UID_1, PACKAGE_1, JOBS_AND_ALARMS); - areRestricted(instance, UID_2, PACKAGE_2, JOBS_AND_ALARMS); - areRestricted(instance, Process.SYSTEM_UID, PACKAGE_SYSTEM, NONE); - - areRestrictedWithExemption(instance, UID_1, PACKAGE_1, NONE); - areRestrictedWithExemption(instance, UID_2, PACKAGE_2, NONE); - areRestrictedWithExemption(instance, Process.SYSTEM_UID, PACKAGE_SYSTEM, NONE); + areJobsRestricted(instance, + new int[] {UID_1, UID_2, UID_10_2, Process.SYSTEM_UID}, + new String[] {PACKAGE_1, PACKAGE_2, PACKAGE_2, PACKAGE_SYSTEM}, + new boolean[] {true, true, true, false}, + false); + areJobsRestricted(instance, + new int[] {UID_1, UID_2, UID_10_2, Process.SYSTEM_UID}, + new String[] {PACKAGE_1, PACKAGE_2, PACKAGE_2, PACKAGE_SYSTEM}, + new boolean[] {false, false, false, false}, + true); + areAlarmsRestrictedByBatterySaver(instance, + new int[] {UID_1, UID_2, UID_10_2, Process.SYSTEM_UID}, + new String[] {PACKAGE_1, PACKAGE_2, PACKAGE_2, PACKAGE_SYSTEM}, + new boolean[] {true, true, true, false}); // Toggle the foreground state. - mPowerSaveMode = true; - mPowerSaveObserver.accept(getPowerSaveState()); assertFalse(instance.isUidActive(UID_1)); assertFalse(instance.isUidActive(UID_2)); @@ -376,34 +389,65 @@ public class AppStateTrackerTest { mIUidObserver.onUidActive(UID_1); waitUntilMainHandlerDrain(); waitUntilMainHandlerDrain(); - areRestricted(instance, UID_1, PACKAGE_1, NONE); - areRestricted(instance, UID_2, PACKAGE_2, JOBS_AND_ALARMS); - areRestricted(instance, Process.SYSTEM_UID, PACKAGE_SYSTEM, NONE); + + areJobsRestricted(instance, + new int[] {UID_1, UID_2, UID_10_2, Process.SYSTEM_UID}, + new String[] {PACKAGE_1, PACKAGE_2, PACKAGE_2, PACKAGE_SYSTEM}, + new boolean[] {false, true, true, false}, + false); + areAlarmsRestrictedByBatterySaver(instance, + new int[] {UID_1, UID_2, UID_10_2, Process.SYSTEM_UID}, + new String[] {PACKAGE_1, PACKAGE_2, PACKAGE_2, PACKAGE_SYSTEM}, + new boolean[] {false, true, true, false}); + assertTrue(instance.isUidActive(UID_1)); assertFalse(instance.isUidActive(UID_2)); mIUidObserver.onUidGone(UID_1, /*disable=*/ false); waitUntilMainHandlerDrain(); waitUntilMainHandlerDrain(); - areRestricted(instance, UID_1, PACKAGE_1, JOBS_AND_ALARMS); - areRestricted(instance, UID_2, PACKAGE_2, JOBS_AND_ALARMS); - areRestricted(instance, Process.SYSTEM_UID, PACKAGE_SYSTEM, NONE); + + areJobsRestricted(instance, + new int[] {UID_1, UID_2, UID_10_2, Process.SYSTEM_UID}, + new String[] {PACKAGE_1, PACKAGE_2, PACKAGE_2, PACKAGE_SYSTEM}, + new boolean[] {true, true, true, false}, + false); + areAlarmsRestrictedByBatterySaver(instance, + new int[] {UID_1, UID_2, UID_10_2, Process.SYSTEM_UID}, + new String[] {PACKAGE_1, PACKAGE_2, PACKAGE_2, PACKAGE_SYSTEM}, + new boolean[] {true, true, true, false}); + assertFalse(instance.isUidActive(UID_1)); assertFalse(instance.isUidActive(UID_2)); mIUidObserver.onUidActive(UID_1); waitUntilMainHandlerDrain(); waitUntilMainHandlerDrain(); - areRestricted(instance, UID_1, PACKAGE_1, NONE); - areRestricted(instance, UID_2, PACKAGE_2, JOBS_AND_ALARMS); - areRestricted(instance, Process.SYSTEM_UID, PACKAGE_SYSTEM, NONE); + + areJobsRestricted(instance, + new int[] {UID_1, UID_2, UID_10_2, Process.SYSTEM_UID}, + new String[] {PACKAGE_1, PACKAGE_2, PACKAGE_2, PACKAGE_SYSTEM}, + new boolean[] {false, true, true, false}, + false); + areAlarmsRestrictedByBatterySaver(instance, + new int[] {UID_1, UID_2, UID_10_2, Process.SYSTEM_UID}, + new String[] {PACKAGE_1, PACKAGE_2, PACKAGE_2, PACKAGE_SYSTEM}, + new boolean[] {false, true, true, false}); mIUidObserver.onUidIdle(UID_1, /*disable=*/ false); waitUntilMainHandlerDrain(); waitUntilMainHandlerDrain(); - areRestricted(instance, UID_1, PACKAGE_1, JOBS_AND_ALARMS); - areRestricted(instance, UID_2, PACKAGE_2, JOBS_AND_ALARMS); - areRestricted(instance, Process.SYSTEM_UID, PACKAGE_SYSTEM, NONE); + + areJobsRestricted(instance, + new int[] {UID_1, UID_2, UID_10_2, Process.SYSTEM_UID}, + new String[] {PACKAGE_1, PACKAGE_2, PACKAGE_2, PACKAGE_SYSTEM}, + new boolean[] {true, true, true, false}, + false); + areAlarmsRestrictedByBatterySaver(instance, + new int[] {UID_1, UID_2, UID_10_2, Process.SYSTEM_UID}, + new String[] {PACKAGE_1, PACKAGE_2, PACKAGE_2, PACKAGE_SYSTEM}, + new boolean[] {true, true, true, false}); + assertFalse(instance.isUidActive(UID_1)); assertFalse(instance.isUidActive(UID_2)); @@ -416,11 +460,19 @@ public class AppStateTrackerTest { assertTrue(instance.isRunAnyInBackgroundAppOpsAllowed(UID_2, PACKAGE_2)); assertTrue(instance.isRunAnyInBackgroundAppOpsAllowed(UID_10_2, PACKAGE_2)); - areRestricted(instance, UID_1, PACKAGE_1, NONE); - areRestricted(instance, UID_10_1, PACKAGE_1, NONE); - areRestricted(instance, UID_2, PACKAGE_2, NONE); - areRestricted(instance, UID_10_2, PACKAGE_2, NONE); - areRestricted(instance, Process.SYSTEM_UID, PACKAGE_SYSTEM, NONE); + areJobsRestricted(instance, + new int[] {UID_1, UID_10_1, UID_2, UID_10_2, Process.SYSTEM_UID}, + new String[] {PACKAGE_1, PACKAGE_1, PACKAGE_2, PACKAGE_2, PACKAGE_SYSTEM}, + new boolean[] {false, false, false, false, false}, + false); + areAlarmsRestrictedByBatterySaver(instance, + new int[] {UID_1, UID_10_1, UID_2, UID_10_2, Process.SYSTEM_UID}, + new String[] {PACKAGE_1, PACKAGE_1, PACKAGE_2, PACKAGE_2, PACKAGE_SYSTEM}, + new boolean[] {false, false, false, false, false}); + areAlarmsRestrictedByFAS(instance, + new int[] {UID_1, UID_10_1, UID_2, UID_10_2, Process.SYSTEM_UID}, + new String[] {PACKAGE_1, PACKAGE_1, PACKAGE_2, PACKAGE_2, PACKAGE_SYSTEM}, + new boolean[] {false, false, false, false, false}); setAppOps(UID_1, PACKAGE_1, true); setAppOps(UID_10_2, PACKAGE_2, true); @@ -429,24 +481,72 @@ public class AppStateTrackerTest { assertTrue(instance.isRunAnyInBackgroundAppOpsAllowed(UID_2, PACKAGE_2)); assertFalse(instance.isRunAnyInBackgroundAppOpsAllowed(UID_10_2, PACKAGE_2)); - areRestricted(instance, UID_1, PACKAGE_1, JOBS_AND_ALARMS); - areRestricted(instance, UID_10_1, PACKAGE_1, NONE); - areRestricted(instance, UID_2, PACKAGE_2, NONE); - areRestricted(instance, UID_10_2, PACKAGE_2, JOBS_AND_ALARMS); - areRestricted(instance, Process.SYSTEM_UID, PACKAGE_SYSTEM, NONE); + areJobsRestricted(instance, + new int[] {UID_1, UID_10_1, UID_2, UID_10_2, Process.SYSTEM_UID}, + new String[] {PACKAGE_1, PACKAGE_1, PACKAGE_2, PACKAGE_2, PACKAGE_SYSTEM}, + new boolean[] {true, false, false, true, false}, + false); + areJobsRestricted(instance, + new int[] {UID_1, UID_10_1, UID_2, UID_10_2, Process.SYSTEM_UID}, + new String[] {PACKAGE_1, PACKAGE_1, PACKAGE_2, PACKAGE_2, PACKAGE_SYSTEM}, + new boolean[] {true, false, false, true, false}, + true); + + areAlarmsRestrictedByBatterySaver(instance, + new int[] {UID_1, UID_10_1, UID_2, UID_10_2, Process.SYSTEM_UID}, + new String[] {PACKAGE_1, PACKAGE_1, PACKAGE_2, PACKAGE_2, PACKAGE_SYSTEM}, + new boolean[] {false, false, false, false, false}); + areAlarmsRestrictedByFAS(instance, + new int[] {UID_1, UID_10_1, UID_2, UID_10_2, Process.SYSTEM_UID}, + new String[] {PACKAGE_1, PACKAGE_1, PACKAGE_2, PACKAGE_2, PACKAGE_SYSTEM}, + new boolean[] {true, false, false, true, false}); // Toggle power saver, should still be the same. mPowerSaveMode = true; mPowerSaveObserver.accept(getPowerSaveState()); + areJobsRestricted(instance, + new int[] {UID_1, UID_10_1, UID_2, UID_10_2, Process.SYSTEM_UID}, + new String[] {PACKAGE_1, PACKAGE_1, PACKAGE_2, PACKAGE_2, PACKAGE_SYSTEM}, + new boolean[] {true, true, true, true, false}, + false); + areJobsRestricted(instance, + new int[] {UID_1, UID_10_1, UID_2, UID_10_2, Process.SYSTEM_UID}, + new String[] {PACKAGE_1, PACKAGE_1, PACKAGE_2, PACKAGE_2, PACKAGE_SYSTEM}, + new boolean[] {true, false, false, true, false}, + true); + + areAlarmsRestrictedByBatterySaver(instance, + new int[] {UID_1, UID_10_1, UID_2, UID_10_2, Process.SYSTEM_UID}, + new String[] {PACKAGE_1, PACKAGE_1, PACKAGE_2, PACKAGE_2, PACKAGE_SYSTEM}, + new boolean[] {true, true, true, true, false}); + areAlarmsRestrictedByFAS(instance, + new int[] {UID_1, UID_10_1, UID_2, UID_10_2, Process.SYSTEM_UID}, + new String[] {PACKAGE_1, PACKAGE_1, PACKAGE_2, PACKAGE_2, PACKAGE_SYSTEM}, + new boolean[] {true, false, false, true, false}); + mPowerSaveMode = false; mPowerSaveObserver.accept(getPowerSaveState()); - areRestricted(instance, UID_1, PACKAGE_1, JOBS_AND_ALARMS); - areRestricted(instance, UID_10_1, PACKAGE_1, NONE); - areRestricted(instance, UID_2, PACKAGE_2, NONE); - areRestricted(instance, UID_10_2, PACKAGE_2, JOBS_AND_ALARMS); - areRestricted(instance, Process.SYSTEM_UID, PACKAGE_SYSTEM, NONE); + areJobsRestricted(instance, + new int[] {UID_1, UID_10_1, UID_2, UID_10_2, Process.SYSTEM_UID}, + new String[] {PACKAGE_1, PACKAGE_1, PACKAGE_2, PACKAGE_2, PACKAGE_SYSTEM}, + new boolean[] {true, false, false, true, false}, + false); + areJobsRestricted(instance, + new int[] {UID_1, UID_10_1, UID_2, UID_10_2, Process.SYSTEM_UID}, + new String[] {PACKAGE_1, PACKAGE_1, PACKAGE_2, PACKAGE_2, PACKAGE_SYSTEM}, + new boolean[] {true, false, false, true, false}, + true); + + areAlarmsRestrictedByBatterySaver(instance, + new int[] {UID_1, UID_10_1, UID_2, UID_10_2, Process.SYSTEM_UID}, + new String[] {PACKAGE_1, PACKAGE_1, PACKAGE_2, PACKAGE_2, PACKAGE_SYSTEM}, + new boolean[] {false, false, false, false, false}); + areAlarmsRestrictedByFAS(instance, + new int[] {UID_1, UID_10_1, UID_2, UID_10_2, Process.SYSTEM_UID}, + new String[] {PACKAGE_1, PACKAGE_1, PACKAGE_2, PACKAGE_2, PACKAGE_SYSTEM}, + new boolean[] {true, false, false, true, false}); // Clear the app ops and update the exemption list. setAppOps(UID_1, PACKAGE_1, false); @@ -455,24 +555,41 @@ public class AppStateTrackerTest { mPowerSaveMode = true; mPowerSaveObserver.accept(getPowerSaveState()); - areRestricted(instance, UID_1, PACKAGE_1, JOBS_AND_ALARMS); - areRestricted(instance, UID_10_1, PACKAGE_1, JOBS_AND_ALARMS); - areRestricted(instance, UID_2, PACKAGE_2, JOBS_AND_ALARMS); - areRestricted(instance, UID_10_2, PACKAGE_2, JOBS_AND_ALARMS); - areRestricted(instance, UID_3, PACKAGE_3, JOBS_AND_ALARMS); - areRestricted(instance, UID_10_3, PACKAGE_3, JOBS_AND_ALARMS); - areRestricted(instance, Process.SYSTEM_UID, PACKAGE_SYSTEM, NONE); + areJobsRestricted(instance, + new int[] {UID_1, UID_10_1, UID_2, UID_10_2, Process.SYSTEM_UID}, + new String[] {PACKAGE_1, PACKAGE_1, PACKAGE_2, PACKAGE_2, PACKAGE_SYSTEM}, + new boolean[] {true, true, true, true, false}, + false); + areJobsRestricted(instance, + new int[] {UID_1, UID_10_1, UID_2, UID_10_2, Process.SYSTEM_UID}, + new String[] {PACKAGE_1, PACKAGE_1, PACKAGE_2, PACKAGE_2, PACKAGE_SYSTEM}, + new boolean[] {false, false, false, false, false}, + true); + + areAlarmsRestrictedByBatterySaver(instance, + new int[] {UID_1, UID_10_1, UID_2, UID_10_2, Process.SYSTEM_UID}, + new String[] {PACKAGE_1, PACKAGE_1, PACKAGE_2, PACKAGE_2, PACKAGE_SYSTEM}, + new boolean[] {true, true, true, true, false}); + areAlarmsRestrictedByFAS(instance, + new int[] {UID_1, UID_10_1, UID_2, UID_10_2, Process.SYSTEM_UID}, + new String[] {PACKAGE_1, PACKAGE_1, PACKAGE_2, PACKAGE_2, PACKAGE_SYSTEM}, + new boolean[] {false, false, false, false, false}); instance.setPowerSaveExemptionListAppIds(new int[] {UID_1}, new int[] {}, new int[] {UID_2}); - areRestricted(instance, UID_1, PACKAGE_1, NONE); - areRestricted(instance, UID_10_1, PACKAGE_1, NONE); - areRestricted(instance, UID_2, PACKAGE_2, ALARMS_ONLY); - areRestricted(instance, UID_10_2, PACKAGE_2, ALARMS_ONLY); - areRestricted(instance, UID_3, PACKAGE_3, JOBS_AND_ALARMS); - areRestricted(instance, UID_10_3, PACKAGE_3, JOBS_AND_ALARMS); - areRestricted(instance, Process.SYSTEM_UID, PACKAGE_SYSTEM, NONE); + areJobsRestricted(instance, + new int[] {UID_1, UID_10_1, UID_2, UID_10_2, UID_3, UID_10_3, Process.SYSTEM_UID}, + new String[]{PACKAGE_1, PACKAGE_1, PACKAGE_2, PACKAGE_2, PACKAGE_3, PACKAGE_3, + PACKAGE_SYSTEM}, + new boolean[] {false, false, false, false, true, true, false}, + false); + + areAlarmsRestrictedByBatterySaver(instance, + new int[] {UID_1, UID_10_1, UID_2, UID_10_2, UID_3, UID_10_3, Process.SYSTEM_UID}, + new String[]{PACKAGE_1, PACKAGE_1, PACKAGE_2, PACKAGE_2, PACKAGE_3, PACKAGE_3, + PACKAGE_SYSTEM}, + new boolean[] {false, false, true, true, true, true, false}); // Again, make sure toggling the global state doesn't change it. mPowerSaveMode = false; @@ -481,13 +598,18 @@ public class AppStateTrackerTest { mPowerSaveMode = true; mPowerSaveObserver.accept(getPowerSaveState()); - areRestricted(instance, UID_1, PACKAGE_1, NONE); - areRestricted(instance, UID_10_1, PACKAGE_1, NONE); - areRestricted(instance, UID_2, PACKAGE_2, ALARMS_ONLY); - areRestricted(instance, UID_10_2, PACKAGE_2, ALARMS_ONLY); - areRestricted(instance, UID_3, PACKAGE_3, JOBS_AND_ALARMS); - areRestricted(instance, UID_10_3, PACKAGE_3, JOBS_AND_ALARMS); - areRestricted(instance, Process.SYSTEM_UID, PACKAGE_SYSTEM, NONE); + areJobsRestricted(instance, + new int[] {UID_1, UID_10_1, UID_2, UID_10_2, UID_3, UID_10_3, Process.SYSTEM_UID}, + new String[]{PACKAGE_1, PACKAGE_1, PACKAGE_2, PACKAGE_2, PACKAGE_3, PACKAGE_3, + PACKAGE_SYSTEM}, + new boolean[] {false, false, false, false, true, true, false}, + false); + + areAlarmsRestrictedByBatterySaver(instance, + new int[] {UID_1, UID_10_1, UID_2, UID_10_2, UID_3, UID_10_3, Process.SYSTEM_UID}, + new String[]{PACKAGE_1, PACKAGE_1, PACKAGE_2, PACKAGE_2, PACKAGE_3, PACKAGE_3, + PACKAGE_SYSTEM}, + new boolean[] {false, false, true, true, true, true, false}); assertTrue(instance.isUidPowerSaveExempt(UID_1)); assertTrue(instance.isUidPowerSaveExempt(UID_10_1)); @@ -646,52 +768,98 @@ public class AppStateTrackerTest { } @Test - public void testExempt() throws Exception { + public void testExemptedBucket() throws Exception { final AppStateTrackerTestable instance = newInstance(); callStart(instance); assertFalse(instance.isForceAllAppsStandbyEnabled()); - areRestricted(instance, UID_1, PACKAGE_1, NONE); - areRestricted(instance, UID_2, PACKAGE_2, NONE); - areRestricted(instance, Process.SYSTEM_UID, PACKAGE_SYSTEM, NONE); + + areJobsRestricted(instance, + new int[] {UID_1, UID_2, Process.SYSTEM_UID}, + new String[] {PACKAGE_1, PACKAGE_2, PACKAGE_SYSTEM}, + new boolean[] {false, false, false}, + false); + areAlarmsRestrictedByBatterySaver(instance, + new int[] {UID_1, UID_2, Process.SYSTEM_UID}, + new String[] {PACKAGE_1, PACKAGE_2, PACKAGE_SYSTEM}, + new boolean[] {false, false, false}); mPowerSaveMode = true; mPowerSaveObserver.accept(getPowerSaveState()); assertTrue(instance.isForceAllAppsStandbyEnabled()); - areRestricted(instance, UID_1, PACKAGE_1, JOBS_AND_ALARMS); - areRestricted(instance, UID_2, PACKAGE_2, JOBS_AND_ALARMS); - areRestricted(instance, UID_10_2, PACKAGE_2, JOBS_AND_ALARMS); - areRestricted(instance, Process.SYSTEM_UID, PACKAGE_SYSTEM, NONE); + areJobsRestricted(instance, + new int[] {UID_1, UID_2, UID_10_2, Process.SYSTEM_UID}, + new String[] {PACKAGE_1, PACKAGE_2, PACKAGE_2, PACKAGE_SYSTEM}, + new boolean[] {true, true, true, false}, + false); + areJobsRestricted(instance, + new int[] {UID_1, UID_2, UID_10_2, Process.SYSTEM_UID}, + new String[] {PACKAGE_1, PACKAGE_2, PACKAGE_2, PACKAGE_SYSTEM}, + new boolean[] {false, false, false, false}, + true); + areAlarmsRestrictedByBatterySaver(instance, + new int[] {UID_1, UID_2, UID_10_2, Process.SYSTEM_UID}, + new String[] {PACKAGE_1, PACKAGE_2, PACKAGE_2, PACKAGE_SYSTEM}, + new boolean[] {true, true, true, false}); // Exempt package 2 on user-10. mAppIdleStateChangeListener.onAppIdleStateChanged(PACKAGE_2, /*user=*/ 10, false, UsageStatsManager.STANDBY_BUCKET_EXEMPTED, REASON_MAIN_DEFAULT); - areRestricted(instance, UID_1, PACKAGE_1, JOBS_AND_ALARMS); - areRestricted(instance, UID_2, PACKAGE_2, JOBS_AND_ALARMS); - areRestricted(instance, UID_10_2, PACKAGE_2, NONE); - - areRestrictedWithExemption(instance, UID_1, PACKAGE_1, NONE); - areRestrictedWithExemption(instance, UID_2, PACKAGE_2, NONE); - areRestrictedWithExemption(instance, UID_10_2, PACKAGE_2, NONE); + areJobsRestricted(instance, + new int[] {UID_1, UID_2, UID_10_2}, + new String[] {PACKAGE_1, PACKAGE_2, PACKAGE_2}, + new boolean[] {true, true, false}, + false); + areJobsRestricted(instance, + new int[] {UID_1, UID_2, UID_10_2}, + new String[] {PACKAGE_1, PACKAGE_2, PACKAGE_2}, + new boolean[] {false, false, false}, + true); + areAlarmsRestrictedByBatterySaver(instance, + new int[] {UID_1, UID_2, UID_10_2}, + new String[] {PACKAGE_1, PACKAGE_2, PACKAGE_2}, + new boolean[] {true, true, false}); // Exempt package 1 on user-0. mAppIdleStateChangeListener.onAppIdleStateChanged(PACKAGE_1, /*user=*/ 0, false, UsageStatsManager.STANDBY_BUCKET_EXEMPTED, REASON_MAIN_DEFAULT); - areRestricted(instance, UID_1, PACKAGE_1, NONE); - areRestricted(instance, UID_2, PACKAGE_2, JOBS_AND_ALARMS); - areRestricted(instance, UID_10_2, PACKAGE_2, NONE); + areJobsRestricted(instance, + new int[] {UID_1, UID_2, UID_10_2}, + new String[] {PACKAGE_1, PACKAGE_2, PACKAGE_2}, + new boolean[] {false, true, false}, + false); + areJobsRestricted(instance, + new int[] {UID_1, UID_2, UID_10_2}, + new String[] {PACKAGE_1, PACKAGE_2, PACKAGE_2}, + new boolean[] {false, false, false}, + true); + areAlarmsRestrictedByBatterySaver(instance, + new int[] {UID_1, UID_2, UID_10_2}, + new String[] {PACKAGE_1, PACKAGE_2, PACKAGE_2}, + new boolean[] {false, true, false}); // Unexempt package 2 on user-10. mAppIdleStateChangeListener.onAppIdleStateChanged(PACKAGE_2, /*user=*/ 10, false, UsageStatsManager.STANDBY_BUCKET_ACTIVE, REASON_MAIN_USAGE); - areRestricted(instance, UID_1, PACKAGE_1, NONE); - areRestricted(instance, UID_2, PACKAGE_2, JOBS_AND_ALARMS); - areRestricted(instance, UID_10_2, PACKAGE_2, JOBS_AND_ALARMS); + areJobsRestricted(instance, + new int[] {UID_1, UID_2, UID_10_2}, + new String[] {PACKAGE_1, PACKAGE_2, PACKAGE_2}, + new boolean[] {false, true, true}, + false); + areJobsRestricted(instance, + new int[] {UID_1, UID_2, UID_10_2}, + new String[] {PACKAGE_1, PACKAGE_2, PACKAGE_2}, + new boolean[] {false, false, false}, + true); + areAlarmsRestrictedByBatterySaver(instance, + new int[] {UID_1, UID_2, UID_10_2}, + new String[] {PACKAGE_1, PACKAGE_2, PACKAGE_2}, + new boolean[] {false, true, true}); // Check force-app-standby. // EXEMPT doesn't exempt from force-app-standby. @@ -703,13 +871,28 @@ public class AppStateTrackerTest { mAppIdleStateChangeListener.onAppIdleStateChanged(PACKAGE_2, /*user=*/ 0, false, UsageStatsManager.STANDBY_BUCKET_EXEMPTED, REASON_MAIN_DEFAULT); + // All 3 packages (u0:p1, u0:p2, u10:p2) are now in the exempted bucket. setAppOps(UID_1, PACKAGE_1, true); - areRestricted(instance, UID_1, PACKAGE_1, JOBS_AND_ALARMS); - areRestricted(instance, UID_2, PACKAGE_2, NONE); - - areRestrictedWithExemption(instance, UID_1, PACKAGE_1, JOBS_AND_ALARMS); - areRestrictedWithExemption(instance, UID_2, PACKAGE_2, NONE); + areJobsRestricted(instance, + new int[] {UID_1, UID_2, UID_10_2}, + new String[] {PACKAGE_1, PACKAGE_2, PACKAGE_2}, + new boolean[] {true, false, false}, + false); + areJobsRestricted(instance, + new int[] {UID_1, UID_2, UID_10_2}, + new String[] {PACKAGE_1, PACKAGE_2, PACKAGE_2}, + new boolean[] {true, false, false}, + true); + + areAlarmsRestrictedByBatterySaver(instance, + new int[] {UID_1, UID_2, UID_10_2}, + new String[] {PACKAGE_1, PACKAGE_2, PACKAGE_2}, + new boolean[] {false, false, false}); + areAlarmsRestrictedByFAS(instance, + new int[] {UID_1, UID_2, UID_10_2}, + new String[] {PACKAGE_1, PACKAGE_2, PACKAGE_2}, + new boolean[] {true, false, false}); } @Test @@ -809,6 +992,8 @@ public class AppStateTrackerTest { verify(l, times(0)).updateJobsForUid(anyInt(), anyBoolean()); verify(l, times(0)).updateJobsForUidPackage(anyInt(), anyString(), anyBoolean()); + verify(l, times(1)).updateAllAlarms(); + verify(l, times(0)).updateAlarmsForUid(anyInt()); verify(l, times(0)).unblockAllUnrestrictedAlarms(); verify(l, times(0)).unblockAlarmsForUid(anyInt()); verify(l, times(0)).unblockAlarmsForUidPackage(anyInt(), anyString()); @@ -823,7 +1008,9 @@ public class AppStateTrackerTest { verify(l, times(0)).updateJobsForUid(anyInt(), anyBoolean()); verify(l, times(0)).updateJobsForUidPackage(anyInt(), anyString(), anyBoolean()); - verify(l, times(1)).unblockAllUnrestrictedAlarms(); + verify(l, times(1)).updateAllAlarms(); + verify(l, times(0)).updateAlarmsForUid(anyInt()); + verify(l, times(0)).unblockAllUnrestrictedAlarms(); verify(l, times(0)).unblockAlarmsForUid(anyInt()); verify(l, times(0)).unblockAlarmsForUidPackage(anyInt(), anyString()); reset(l); @@ -853,6 +1040,8 @@ public class AppStateTrackerTest { verify(l, times(0)).updateJobsForUid(anyInt(), anyBoolean()); verify(l, times(1)).updateJobsForUidPackage(eq(UID_10_2), eq(PACKAGE_2), anyBoolean()); + verify(l, times(0)).updateAllAlarms(); + verify(l, times(0)).updateAlarmsForUid(anyInt()); verify(l, times(0)).unblockAllUnrestrictedAlarms(); verify(l, times(0)).unblockAlarmsForUid(anyInt()); verify(l, times(0)).unblockAlarmsForUidPackage(anyInt(), anyString()); @@ -865,6 +1054,8 @@ public class AppStateTrackerTest { verify(l, times(0)).updateJobsForUid(anyInt(), anyBoolean()); verify(l, times(1)).updateJobsForUidPackage(eq(UID_10_2), eq(PACKAGE_2), anyBoolean()); + verify(l, times(0)).updateAllAlarms(); + verify(l, times(0)).updateAlarmsForUid(anyInt()); verify(l, times(0)).unblockAllUnrestrictedAlarms(); verify(l, times(0)).unblockAlarmsForUid(anyInt()); verify(l, times(1)).unblockAlarmsForUidPackage(eq(UID_10_2), eq(PACKAGE_2)); @@ -876,15 +1067,16 @@ public class AppStateTrackerTest { verify(l, times(0)).updateJobsForUid(anyInt(), anyBoolean()); verify(l, times(0)).updateJobsForUidPackage(anyInt(), anyString(), anyBoolean()); + verify(l, times(0)).updateAllAlarms(); + verify(l, times(0)).updateAlarmsForUid(anyInt()); verify(l, times(0)).unblockAllUnrestrictedAlarms(); verify(l, times(0)).unblockAlarmsForUid(anyInt()); verify(l, times(0)).unblockAlarmsForUidPackage(anyInt(), anyString()); - // Unrestrict while battery saver is on. Shouldn't fire. + // Test overlap with battery saver mPowerSaveMode = true; mPowerSaveObserver.accept(getPowerSaveState()); - // Note toggling appops while BS is on will suppress unblockAlarmsForUidPackage(). setAppOps(UID_10_2, PACKAGE_2, true); waitUntilMainHandlerDrain(); @@ -892,6 +1084,8 @@ public class AppStateTrackerTest { verify(l, times(0)).updateJobsForUid(anyInt(), anyBoolean()); verify(l, times(1)).updateJobsForUidPackage(eq(UID_10_2), eq(PACKAGE_2), anyBoolean()); + verify(l, times(1)).updateAllAlarms(); + verify(l, times(0)).updateAlarmsForUid(anyInt()); verify(l, times(0)).unblockAllUnrestrictedAlarms(); verify(l, times(0)).unblockAlarmsForUid(anyInt()); verify(l, times(0)).unblockAlarmsForUidPackage(anyInt(), anyString()); @@ -906,7 +1100,9 @@ public class AppStateTrackerTest { verify(l, times(0)).updateJobsForUid(anyInt(), anyBoolean()); verify(l, times(0)).updateJobsForUidPackage(anyInt(), anyString(), anyBoolean()); - verify(l, times(1)).unblockAllUnrestrictedAlarms(); + verify(l, times(1)).updateAllAlarms(); + verify(l, times(0)).updateAlarmsForUid(anyInt()); + verify(l, times(0)).unblockAllUnrestrictedAlarms(); verify(l, times(0)).unblockAlarmsForUid(anyInt()); verify(l, times(0)).unblockAlarmsForUidPackage(anyInt(), anyString()); reset(l); @@ -922,7 +1118,9 @@ public class AppStateTrackerTest { verify(l, times(0)).updateJobsForUid(anyInt(), anyBoolean()); verify(l, times(0)).updateJobsForUidPackage(anyInt(), anyString(), anyBoolean()); - verify(l, times(0)).unblockAllUnrestrictedAlarms(); + verify(l, times(1)).updateAllAlarms(); + verify(l, times(0)).updateAlarmsForUid(anyInt()); + verify(l, times(1)).unblockAllUnrestrictedAlarms(); verify(l, times(0)).unblockAlarmsForUid(anyInt()); verify(l, times(0)).unblockAlarmsForUidPackage(anyInt(), anyString()); reset(l); @@ -934,7 +1132,9 @@ public class AppStateTrackerTest { verify(l, times(0)).updateJobsForUid(anyInt(), anyBoolean()); verify(l, times(0)).updateJobsForUidPackage(anyInt(), anyString(), anyBoolean()); - verify(l, times(1)).unblockAllUnrestrictedAlarms(); + verify(l, times(1)).updateAllAlarms(); + verify(l, times(0)).updateAlarmsForUid(anyInt()); + verify(l, times(0)).unblockAllUnrestrictedAlarms(); verify(l, times(0)).unblockAlarmsForUid(anyInt()); verify(l, times(0)).unblockAlarmsForUidPackage(anyInt(), anyString()); reset(l); @@ -948,6 +1148,8 @@ public class AppStateTrackerTest { verify(l, times(0)).updateJobsForUid(anyInt(), anyBoolean()); verify(l, times(0)).updateJobsForUidPackage(anyInt(), anyString(), anyBoolean()); + verify(l, times(0)).updateAllAlarms(); + verify(l, times(0)).updateAlarmsForUid(anyInt()); verify(l, times(0)).unblockAllUnrestrictedAlarms(); verify(l, times(0)).unblockAlarmsForUid(anyInt()); verify(l, times(0)).unblockAlarmsForUidPackage(anyInt(), anyString()); @@ -961,12 +1163,14 @@ public class AppStateTrackerTest { verify(l, times(0)).updateJobsForUid(anyInt(), anyBoolean()); verify(l, times(0)).updateJobsForUidPackage(anyInt(), anyString(), anyBoolean()); + verify(l, times(0)).updateAllAlarms(); + verify(l, times(0)).updateAlarmsForUid(anyInt()); verify(l, times(0)).unblockAllUnrestrictedAlarms(); verify(l, times(0)).unblockAlarmsForUid(anyInt()); verify(l, times(0)).unblockAlarmsForUidPackage(anyInt(), anyString()); reset(l); - // Do the same thing with battery saver on. (Currently same callbacks are called.) + // Do the same thing with battery saver on. mPowerSaveMode = true; mPowerSaveObserver.accept(getPowerSaveState()); @@ -975,6 +1179,8 @@ public class AppStateTrackerTest { verify(l, times(0)).updateJobsForUid(anyInt(), anyBoolean()); verify(l, times(0)).updateJobsForUidPackage(anyInt(), anyString(), anyBoolean()); + verify(l, times(1)).updateAllAlarms(); + verify(l, times(0)).updateAlarmsForUid(anyInt()); verify(l, times(0)).unblockAllUnrestrictedAlarms(); verify(l, times(0)).unblockAlarmsForUid(anyInt()); verify(l, times(0)).unblockAlarmsForUidPackage(anyInt(), anyString()); @@ -989,7 +1195,9 @@ public class AppStateTrackerTest { verify(l, times(0)).updateJobsForUid(anyInt(), anyBoolean()); verify(l, times(0)).updateJobsForUidPackage(anyInt(), anyString(), anyBoolean()); - verify(l, times(0)).unblockAllUnrestrictedAlarms(); + verify(l, times(1)).updateAllAlarms(); + verify(l, times(0)).updateAlarmsForUid(anyInt()); + verify(l, times(1)).unblockAllUnrestrictedAlarms(); verify(l, times(0)).unblockAlarmsForUid(anyInt()); verify(l, times(0)).unblockAlarmsForUidPackage(anyInt(), anyString()); reset(l); @@ -1001,7 +1209,9 @@ public class AppStateTrackerTest { verify(l, times(0)).updateJobsForUid(anyInt(), anyBoolean()); verify(l, times(0)).updateJobsForUidPackage(anyInt(), anyString(), anyBoolean()); - verify(l, times(1)).unblockAllUnrestrictedAlarms(); + verify(l, times(1)).updateAllAlarms(); + verify(l, times(0)).updateAlarmsForUid(anyInt()); + verify(l, times(0)).unblockAllUnrestrictedAlarms(); verify(l, times(0)).unblockAlarmsForUid(anyInt()); verify(l, times(0)).unblockAlarmsForUidPackage(anyInt(), anyString()); reset(l); @@ -1015,6 +1225,8 @@ public class AppStateTrackerTest { verify(l, times(0)).updateJobsForUid(anyInt(), anyBoolean()); verify(l, times(0)).updateJobsForUidPackage(anyInt(), anyString(), anyBoolean()); + verify(l, times(0)).updateAllAlarms(); + verify(l, times(0)).updateAlarmsForUid(anyInt()); verify(l, times(0)).unblockAllUnrestrictedAlarms(); verify(l, times(0)).unblockAlarmsForUid(anyInt()); verify(l, times(0)).unblockAlarmsForUidPackage(anyInt(), anyString()); @@ -1028,6 +1240,8 @@ public class AppStateTrackerTest { verify(l, times(0)).updateJobsForUid(anyInt(), anyBoolean()); verify(l, times(0)).updateJobsForUidPackage(anyInt(), anyString(), anyBoolean()); + verify(l, times(0)).updateAllAlarms(); + verify(l, times(0)).updateAlarmsForUid(anyInt()); verify(l, times(0)).unblockAllUnrestrictedAlarms(); verify(l, times(0)).unblockAlarmsForUid(anyInt()); verify(l, times(0)).unblockAlarmsForUidPackage(anyInt(), anyString()); @@ -1037,9 +1251,8 @@ public class AppStateTrackerTest { // ------------------------------------------------------------------------- // Tests with proc state changes. - // With battery save. - mPowerSaveMode = true; - mPowerSaveObserver.accept(getPowerSaveState()); + // With battery saver. + // Battery saver is already on. mIUidObserver.onUidActive(UID_10_1); @@ -1049,6 +1262,8 @@ public class AppStateTrackerTest { verify(l, times(1)).updateJobsForUid(eq(UID_10_1), anyBoolean()); verify(l, times(0)).updateJobsForUidPackage(anyInt(), anyString(), anyBoolean()); + verify(l, times(0)).updateAllAlarms(); + verify(l, times(1)).updateAlarmsForUid(eq(UID_10_1)); verify(l, times(0)).unblockAllUnrestrictedAlarms(); verify(l, times(1)).unblockAlarmsForUid(eq(UID_10_1)); verify(l, times(0)).unblockAlarmsForUidPackage(anyInt(), anyString()); @@ -1062,6 +1277,8 @@ public class AppStateTrackerTest { verify(l, times(1)).updateJobsForUid(eq(UID_10_1), anyBoolean()); verify(l, times(0)).updateJobsForUidPackage(anyInt(), anyString(), anyBoolean()); + verify(l, times(0)).updateAllAlarms(); + verify(l, times(1)).updateAlarmsForUid(eq(UID_10_1)); verify(l, times(0)).unblockAllUnrestrictedAlarms(); verify(l, times(0)).unblockAlarmsForUid(anyInt()); verify(l, times(0)).unblockAlarmsForUidPackage(anyInt(), anyString()); @@ -1075,6 +1292,8 @@ public class AppStateTrackerTest { verify(l, times(1)).updateJobsForUid(eq(UID_10_1), anyBoolean()); verify(l, times(0)).updateJobsForUidPackage(anyInt(), anyString(), anyBoolean()); + verify(l, times(0)).updateAllAlarms(); + verify(l, times(1)).updateAlarmsForUid(eq(UID_10_1)); verify(l, times(0)).unblockAllUnrestrictedAlarms(); verify(l, times(1)).unblockAlarmsForUid(eq(UID_10_1)); verify(l, times(0)).unblockAlarmsForUidPackage(anyInt(), anyString()); @@ -1088,12 +1307,14 @@ public class AppStateTrackerTest { verify(l, times(1)).updateJobsForUid(eq(UID_10_1), anyBoolean()); verify(l, times(0)).updateJobsForUidPackage(anyInt(), anyString(), anyBoolean()); + verify(l, times(0)).updateAllAlarms(); + verify(l, times(1)).updateAlarmsForUid(eq(UID_10_1)); verify(l, times(0)).unblockAllUnrestrictedAlarms(); verify(l, times(0)).unblockAlarmsForUid(anyInt()); verify(l, times(0)).unblockAlarmsForUidPackage(anyInt(), anyString()); reset(l); - // Without battery save. + // Without battery saver. mPowerSaveMode = false; mPowerSaveObserver.accept(getPowerSaveState()); @@ -1102,7 +1323,9 @@ public class AppStateTrackerTest { verify(l, times(0)).updateJobsForUid(eq(UID_10_1), anyBoolean()); verify(l, times(0)).updateJobsForUidPackage(anyInt(), anyString(), anyBoolean()); - verify(l, times(1)).unblockAllUnrestrictedAlarms(); + verify(l, times(1)).updateAllAlarms(); + verify(l, times(0)).updateAlarmsForUid(eq(UID_10_1)); + verify(l, times(0)).unblockAllUnrestrictedAlarms(); verify(l, times(0)).unblockAlarmsForUid(anyInt()); verify(l, times(0)).unblockAlarmsForUidPackage(anyInt(), anyString()); reset(l); @@ -1115,6 +1338,8 @@ public class AppStateTrackerTest { verify(l, times(1)).updateJobsForUid(eq(UID_10_1), anyBoolean()); verify(l, times(0)).updateJobsForUidPackage(anyInt(), anyString(), anyBoolean()); + verify(l, times(0)).updateAllAlarms(); + verify(l, times(1)).updateAlarmsForUid(eq(UID_10_1)); verify(l, times(0)).unblockAllUnrestrictedAlarms(); verify(l, times(1)).unblockAlarmsForUid(eq(UID_10_1)); verify(l, times(0)).unblockAlarmsForUidPackage(anyInt(), anyString()); @@ -1128,6 +1353,8 @@ public class AppStateTrackerTest { verify(l, times(1)).updateJobsForUid(eq(UID_10_1), anyBoolean()); verify(l, times(0)).updateJobsForUidPackage(anyInt(), anyString(), anyBoolean()); + verify(l, times(0)).updateAllAlarms(); + verify(l, times(1)).updateAlarmsForUid(eq(UID_10_1)); verify(l, times(0)).unblockAllUnrestrictedAlarms(); verify(l, times(0)).unblockAlarmsForUid(anyInt()); verify(l, times(0)).unblockAlarmsForUidPackage(anyInt(), anyString()); @@ -1141,6 +1368,8 @@ public class AppStateTrackerTest { verify(l, times(1)).updateJobsForUid(eq(UID_10_1), anyBoolean()); verify(l, times(0)).updateJobsForUidPackage(anyInt(), anyString(), anyBoolean()); + verify(l, times(0)).updateAllAlarms(); + verify(l, times(1)).updateAlarmsForUid(eq(UID_10_1)); verify(l, times(0)).unblockAllUnrestrictedAlarms(); verify(l, times(1)).unblockAlarmsForUid(eq(UID_10_1)); verify(l, times(0)).unblockAlarmsForUidPackage(anyInt(), anyString()); @@ -1154,6 +1383,8 @@ public class AppStateTrackerTest { verify(l, times(1)).updateJobsForUid(eq(UID_10_1), anyBoolean()); verify(l, times(0)).updateJobsForUidPackage(anyInt(), anyString(), anyBoolean()); + verify(l, times(0)).updateAllAlarms(); + verify(l, times(1)).updateAlarmsForUid(eq(UID_10_1)); verify(l, times(0)).unblockAllUnrestrictedAlarms(); verify(l, times(0)).unblockAlarmsForUid(anyInt()); verify(l, times(0)).unblockAlarmsForUidPackage(anyInt(), anyString()); diff --git a/services/tests/mockingservicestests/src/com/android/server/alarm/AlarmManagerServiceTest.java b/services/tests/mockingservicestests/src/com/android/server/alarm/AlarmManagerServiceTest.java index 8edac4f8ce58..7a970a1c3d46 100644 --- a/services/tests/mockingservicestests/src/com/android/server/alarm/AlarmManagerServiceTest.java +++ b/services/tests/mockingservicestests/src/com/android/server/alarm/AlarmManagerServiceTest.java @@ -52,6 +52,7 @@ import static com.android.server.alarm.AlarmManagerService.Constants.KEY_LISTENE import static com.android.server.alarm.AlarmManagerService.Constants.KEY_MAX_INTERVAL; import static com.android.server.alarm.AlarmManagerService.Constants.KEY_MIN_FUTURITY; import static com.android.server.alarm.AlarmManagerService.Constants.KEY_MIN_INTERVAL; +import static com.android.server.alarm.AlarmManagerService.INDEFINITE_DELAY; import static com.android.server.alarm.AlarmManagerService.IS_WAKEUP_MASK; import static com.android.server.alarm.AlarmManagerService.TIME_CHANGED_MASK; import static com.android.server.alarm.AlarmManagerService.WORKING_INDEX; @@ -71,6 +72,7 @@ import static org.mockito.ArgumentMatchers.argThat; import static org.mockito.ArgumentMatchers.eq; import static org.mockito.ArgumentMatchers.isNull; import static org.mockito.Mockito.atLeastOnce; +import static org.mockito.Mockito.never; import android.app.ActivityManager; import android.app.ActivityManagerInternal; @@ -817,14 +819,14 @@ public class AlarmManagerServiceTest { } @Test - public void testAlarmRestrictedInBatterySaver() throws Exception { + public void testAlarmRestrictedByFAS() throws Exception { final ArgumentCaptor<AppStateTrackerImpl.Listener> listenerArgumentCaptor = ArgumentCaptor.forClass(AppStateTrackerImpl.Listener.class); verify(mAppStateTracker).addListener(listenerArgumentCaptor.capture()); final PendingIntent alarmPi = getNewMockPendingIntent(); - when(mAppStateTracker.areAlarmsRestricted(TEST_CALLING_UID, TEST_CALLING_PACKAGE, - false)).thenReturn(true); + when(mAppStateTracker.areAlarmsRestricted(TEST_CALLING_UID, + TEST_CALLING_PACKAGE)).thenReturn(true); setTestAlarm(ELAPSED_REALTIME_WAKEUP, mNowElapsedTest + 2, alarmPi); assertEquals(mNowElapsedTest + 2, mTestTimer.getElapsed()); @@ -1301,7 +1303,6 @@ public class AlarmManagerServiceTest { final long awiDelayForTest = 23; setDeviceConfigLong(KEY_ALLOW_WHILE_IDLE_LONG_TIME, awiDelayForTest); - setDeviceConfigLong(KEY_ALLOW_WHILE_IDLE_SHORT_TIME, 0); setIdleUntilAlarm(ELAPSED_REALTIME_WAKEUP, mNowElapsedTest + 1000, getNewMockPendingIntent()); @@ -1336,7 +1337,7 @@ public class AlarmManagerServiceTest { } @Test - public void allowWhileIdleUnrestricted() throws Exception { + public void allowWhileIdleUnrestrictedInIdle() throws Exception { doReturn(0).when(mService).fuzzForDuration(anyLong()); final long awiDelayForTest = 127; @@ -1361,7 +1362,7 @@ public class AlarmManagerServiceTest { } @Test - public void deviceIdleThrottling() throws Exception { + public void deviceIdleDeferralOnSet() throws Exception { doReturn(0).when(mService).fuzzForDuration(anyLong()); final long deviceIdleUntil = mNowElapsedTest + 1234; @@ -1386,6 +1387,123 @@ public class AlarmManagerServiceTest { } @Test + public void deviceIdleStateChanges() throws Exception { + doReturn(0).when(mService).fuzzForDuration(anyLong()); + + final int numAlarms = 10; + final PendingIntent[] pis = new PendingIntent[numAlarms]; + for (int i = 0; i < numAlarms; i++) { + setTestAlarm(ELAPSED_REALTIME_WAKEUP, mNowElapsedTest + i + 1, + pis[i] = getNewMockPendingIntent()); + assertEquals(mNowElapsedTest + 1, mTestTimer.getElapsed()); + } + + final PendingIntent idleUntil = getNewMockPendingIntent(); + setIdleUntilAlarm(ELAPSED_REALTIME_WAKEUP, mNowElapsedTest + 1234, idleUntil); + + assertEquals(mNowElapsedTest + 1234, mTestTimer.getElapsed()); + + mNowElapsedTest += 5; + mTestTimer.expire(); + // Nothing should happen. + verify(pis[0], never()).send(eq(mMockContext), eq(0), any(Intent.class), any(), + any(Handler.class), isNull(), any()); + + mService.removeLocked(idleUntil, null); + mTestTimer.expire(); + // Now, the first 5 alarms (upto i = 4) should expire. + for (int i = 0; i < 5; i++) { + verify(pis[i]).send(eq(mMockContext), eq(0), any(Intent.class), any(), + any(Handler.class), isNull(), any()); + } + // Rest should be restored, so the timer should reflect the next alarm. + assertEquals(mNowElapsedTest + 1, mTestTimer.getElapsed()); + } + + @Test + public void batterySaverThrottling() { + final ArgumentCaptor<AppStateTrackerImpl.Listener> listenerArgumentCaptor = + ArgumentCaptor.forClass(AppStateTrackerImpl.Listener.class); + verify(mAppStateTracker).addListener(listenerArgumentCaptor.capture()); + final AppStateTrackerImpl.Listener listener = listenerArgumentCaptor.getValue(); + + final PendingIntent alarmPi = getNewMockPendingIntent(); + when(mAppStateTracker.areAlarmsRestrictedByBatterySaver(TEST_CALLING_UID, + TEST_CALLING_PACKAGE)).thenReturn(true); + setTestAlarm(ELAPSED_REALTIME_WAKEUP, mNowElapsedTest + 7, alarmPi); + assertEquals(mNowElapsedTest + INDEFINITE_DELAY, mTestTimer.getElapsed()); + + when(mAppStateTracker.areAlarmsRestrictedByBatterySaver(TEST_CALLING_UID, + TEST_CALLING_PACKAGE)).thenReturn(false); + listener.updateAllAlarms(); + assertEquals(mNowElapsedTest + 7, mTestTimer.getElapsed()); + + when(mAppStateTracker.areAlarmsRestrictedByBatterySaver(TEST_CALLING_UID, + TEST_CALLING_PACKAGE)).thenReturn(true); + listener.updateAlarmsForUid(TEST_CALLING_UID); + assertEquals(mNowElapsedTest + INDEFINITE_DELAY, mTestTimer.getElapsed()); + } + + @Test + public void allowWhileIdleAlarmsInBatterySaver() throws Exception { + final ArgumentCaptor<AppStateTrackerImpl.Listener> listenerArgumentCaptor = + ArgumentCaptor.forClass(AppStateTrackerImpl.Listener.class); + verify(mAppStateTracker).addListener(listenerArgumentCaptor.capture()); + final AppStateTrackerImpl.Listener listener = listenerArgumentCaptor.getValue(); + + final long longDelay = 23; + final long shortDelay = 7; + setDeviceConfigLong(KEY_ALLOW_WHILE_IDLE_LONG_TIME, longDelay); + setDeviceConfigLong(KEY_ALLOW_WHILE_IDLE_SHORT_TIME, shortDelay); + + when(mAppStateTracker.areAlarmsRestrictedByBatterySaver(TEST_CALLING_UID, + TEST_CALLING_PACKAGE)).thenReturn(true); + setAllowWhileIdleAlarm(ELAPSED_REALTIME_WAKEUP, mNowElapsedTest + 1, + getNewMockPendingIntent(), false); + setAllowWhileIdleAlarm(ELAPSED_REALTIME_WAKEUP, mNowElapsedTest + 2, + getNewMockPendingIntent(), false); + + assertEquals(mNowElapsedTest + 1, mTestTimer.getElapsed()); + + mNowElapsedTest += 1; + mTestTimer.expire(); + + assertEquals(mNowElapsedTest + longDelay, mTestTimer.getElapsed()); + listener.onUidForeground(TEST_CALLING_UID, true); + // The next alarm should be deferred by shortDelay. + assertEquals(mNowElapsedTest + shortDelay, mTestTimer.getElapsed()); + + mNowElapsedTest = mTestTimer.getElapsed(); + setAllowWhileIdleAlarm(ELAPSED_REALTIME_WAKEUP, mNowElapsedTest + 1, + getNewMockPendingIntent(), false); + + when(mAppStateTracker.isUidInForeground(TEST_CALLING_UID)).thenReturn(true); + mTestTimer.expire(); + // The next alarm should be deferred by shortDelay again. + assertEquals(mNowElapsedTest + shortDelay, mTestTimer.getElapsed()); + + mNowElapsedTest = mTestTimer.getElapsed(); + setAllowWhileIdleAlarm(ELAPSED_REALTIME_WAKEUP, mNowElapsedTest + 1, + getNewMockPendingIntent(), true); + when(mAppStateTracker.isUidInForeground(TEST_CALLING_UID)).thenReturn(false); + mTestTimer.expire(); + final long lastAwiDispatch = mNowElapsedTest; + // Unrestricted, so should not be changed. + assertEquals(mNowElapsedTest + 1, mTestTimer.getElapsed()); + + mNowElapsedTest = mTestTimer.getElapsed(); + // AWI_unrestricted should not affect normal AWI bookkeeping. + // The next alarm is after the short delay but before the long delay. + setAllowWhileIdleAlarm(ELAPSED_REALTIME_WAKEUP, lastAwiDispatch + shortDelay + 1, + getNewMockPendingIntent(), false); + mTestTimer.expire(); + assertEquals(lastAwiDispatch + longDelay, mTestTimer.getElapsed()); + + listener.onUidForeground(TEST_CALLING_UID, true); + assertEquals(lastAwiDispatch + shortDelay + 1, mTestTimer.getElapsed()); + } + + @Test public void dispatchOrder() throws Exception { doReturn(0).when(mService).fuzzForDuration(anyLong()); diff --git a/services/tests/servicestests/src/com/android/server/DropBoxTest.java b/services/tests/servicestests/src/com/android/server/DropBoxTest.java index 56773e831902..a25f4920030c 100644 --- a/services/tests/servicestests/src/com/android/server/DropBoxTest.java +++ b/services/tests/servicestests/src/com/android/server/DropBoxTest.java @@ -31,15 +31,20 @@ import android.os.UserHandle; import android.provider.Settings; import android.test.AndroidTestCase; +import com.android.server.DropBoxManagerInternal.EntrySource; import com.android.server.DropBoxManagerService.EntryFile; +import libcore.io.Streams; + import java.io.BufferedReader; import java.io.File; +import java.io.FileDescriptor; import java.io.FileOutputStream; import java.io.FileWriter; import java.io.IOException; import java.io.InputStream; import java.io.InputStreamReader; +import java.nio.charset.StandardCharsets; import java.util.Random; import java.util.zip.GZIPOutputStream; @@ -56,6 +61,8 @@ public class DropBoxTest extends AndroidTestCase { protected void setUp() throws Exception { super.setUp(); + LocalServices.removeServiceForTest(DropBoxManagerInternal.class); + mContext = new ContextWrapper(super.getContext()) { @Override public void sendBroadcastAsUser(Intent intent, @@ -212,6 +219,67 @@ public class DropBoxTest extends AndroidTestCase { e3.close(); } + public void testAddEntry_Success() throws Exception { + File dir = getEmptyDir("testAddEntry"); + long before = System.currentTimeMillis(); + + DropBoxManagerService service = new DropBoxManagerService(getContext(), dir, + Looper.getMainLooper()); + DropBoxManager dropbox = new DropBoxManager(getContext(), service.getServiceStub()); + + LocalServices.getService(DropBoxManagerInternal.class).addEntry("DropBoxTest", + new EntrySource() { + @Override + public void writeTo(FileDescriptor fd) throws IOException { + try (FileOutputStream out = new FileOutputStream(fd)) { + out.write("test".getBytes(StandardCharsets.UTF_8)); + } + } + + @Override + public void close() throws IOException { + } + + @Override + public long length() { + return 0; + } + }, DropBoxManager.IS_TEXT); + + DropBoxManager.Entry entry = dropbox.getNextEntry("DropBoxTest", before); + assertEquals(DropBoxManager.IS_TEXT, entry.getFlags()); + assertEquals("test", new String(Streams.readFully(entry.getInputStream()))); + } + + public void testAddEntry_Failure() throws Exception { + File dir = getEmptyDir("testAddEntry"); + long before = System.currentTimeMillis(); + + DropBoxManagerService service = new DropBoxManagerService(getContext(), dir, + Looper.getMainLooper()); + DropBoxManager dropbox = new DropBoxManager(getContext(), service.getServiceStub()); + + LocalServices.getService(DropBoxManagerInternal.class).addEntry("DropBoxTest", + new EntrySource() { + @Override + public void writeTo(FileDescriptor fd) throws IOException { + throw new IOException(); + } + + @Override + public void close() throws IOException { + } + + @Override + public long length() { + return 0; + } + }, DropBoxManager.IS_TEXT); + + DropBoxManager.Entry entry = dropbox.getNextEntry("DropBoxTest", before); + assertNull(entry); + } + public void testAddEntriesInTheFuture() throws Exception { File dir = getEmptyDir("testAddEntriesInTheFuture"); long before = System.currentTimeMillis(); diff --git a/services/tests/servicestests/src/com/android/server/biometrics/InvalidationTrackerTest.java b/services/tests/servicestests/src/com/android/server/biometrics/InvalidationTrackerTest.java new file mode 100644 index 000000000000..340a1d9bdda0 --- /dev/null +++ b/services/tests/servicestests/src/com/android/server/biometrics/InvalidationTrackerTest.java @@ -0,0 +1,111 @@ +/* + * 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.biometrics; + +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.ArgumentMatchers.anyInt; +import static org.mockito.ArgumentMatchers.eq; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.never; +import static org.mockito.Mockito.verify; + +import android.hardware.biometrics.BiometricAuthenticator; +import android.hardware.biometrics.BiometricManager.Authenticators; +import android.hardware.biometrics.IBiometricAuthenticator; +import android.hardware.biometrics.IInvalidationCallback; +import android.platform.test.annotations.Presubmit; + +import androidx.test.filters.SmallTest; + +import com.android.server.biometrics.BiometricService.InvalidationTracker; + +import org.junit.Test; + +import java.util.ArrayList; + +@Presubmit +@SmallTest +public class InvalidationTrackerTest { + + @Test + public void testCallbackReceived_whenAllStrongSensorsInvalidated() throws Exception { + final IBiometricAuthenticator authenticator1 = mock(IBiometricAuthenticator.class); + final TestSensor sensor1 = new TestSensor(0 /* id */, + BiometricAuthenticator.TYPE_FINGERPRINT, Authenticators.BIOMETRIC_STRONG, + authenticator1); + + final IBiometricAuthenticator authenticator2 = mock(IBiometricAuthenticator.class); + final TestSensor sensor2 = new TestSensor(1 /* id */, + BiometricAuthenticator.TYPE_FINGERPRINT, Authenticators.BIOMETRIC_STRONG, + authenticator2); + + final IBiometricAuthenticator authenticator3 = mock(IBiometricAuthenticator.class); + final TestSensor sensor3 = new TestSensor(2 /* id */, + BiometricAuthenticator.TYPE_FACE, Authenticators.BIOMETRIC_STRONG, + authenticator3); + + final IBiometricAuthenticator authenticator4 = mock(IBiometricAuthenticator.class); + final TestSensor sensor4 = new TestSensor(3 /* id */, + BiometricAuthenticator.TYPE_FACE, Authenticators.BIOMETRIC_WEAK, + authenticator4); + + final ArrayList<BiometricSensor> sensors = new ArrayList<>(); + sensors.add(sensor1); + sensors.add(sensor2); + sensors.add(sensor3); + sensors.add(sensor4); + + final IInvalidationCallback callback = mock(IInvalidationCallback.class); + final InvalidationTracker tracker = + InvalidationTracker.start(sensors, 0 /* userId */, 0 /* fromSensorId */, callback); + + // The sensor which the request originated from should not be requested to invalidate + // its authenticatorId. + verify(authenticator1, never()).invalidateAuthenticatorId(anyInt(), any()); + + // All other strong sensors should be requested to invalidate authenticatorId + verify(authenticator2).invalidateAuthenticatorId(eq(0) /* userId */, any()); + verify(authenticator3).invalidateAuthenticatorId(eq(0) /* userId */, any()); + + // Weak sensors are not requested to invalidate authenticatorId + verify(authenticator4, never()).invalidateAuthenticatorId(anyInt(), any()); + + // Client is not notified until invalidation for all required sensors have completed + verify(callback, never()).onCompleted(); + tracker.onInvalidated(1); + verify(callback, never()).onCompleted(); + tracker.onInvalidated(2); + verify(callback).onCompleted(); + } + + private static class TestSensor extends BiometricSensor { + + TestSensor(int id, int modality, int strength, IBiometricAuthenticator impl) { + super(id, modality, strength, impl); + } + + @Override + boolean confirmationAlwaysRequired(int userId) { + return false; + } + + @Override + boolean confirmationSupported() { + return false; + } + } +} diff --git a/services/tests/servicestests/src/com/android/server/compat/CompatConfigBuilder.java b/services/tests/servicestests/src/com/android/server/compat/CompatConfigBuilder.java index 4f4aa3f16f09..f00edcc85404 100644 --- a/services/tests/servicestests/src/com/android/server/compat/CompatConfigBuilder.java +++ b/services/tests/servicestests/src/com/android/server/compat/CompatConfigBuilder.java @@ -40,78 +40,83 @@ class CompatConfigBuilder { } CompatConfigBuilder addEnableAfterSdkChangeWithId(int sdk, long id) { - mChanges.add(new CompatChange(id, "", sdk, -1, false, false, "")); + mChanges.add(new CompatChange(id, "", sdk, -1, false, false, "", false)); return this; } CompatConfigBuilder addEnableAfterSdkChangeWithIdAndName(int sdk, long id, String name) { - mChanges.add(new CompatChange(id, name, sdk, -1, false, false, "")); + mChanges.add(new CompatChange(id, name, sdk, -1, false, false, "", false)); return this; } CompatConfigBuilder addEnableAfterSdkChangeWithIdDefaultDisabled(int sdk, long id) { - mChanges.add(new CompatChange(id, "", sdk, -1, true, false, "")); + mChanges.add(new CompatChange(id, "", sdk, -1, true, false, "", false)); return this; } CompatConfigBuilder addEnableAfterSdkChangeWithIdAndDescription(int sdk, long id, String description) { - mChanges.add(new CompatChange(id, "", sdk, -1, false, false, description)); + mChanges.add(new CompatChange(id, "", sdk, -1, false, false, description, false)); return this; } CompatConfigBuilder addEnableSinceSdkChangeWithId(int sdk, long id) { - mChanges.add(new CompatChange(id, "", -1, sdk, false, false, "")); + mChanges.add(new CompatChange(id, "", -1, sdk, false, false, "", false)); return this; } CompatConfigBuilder addEnableSinceSdkChangeWithIdAndName(int sdk, long id, String name) { - mChanges.add(new CompatChange(id, name, -1, sdk, false, false, "")); + mChanges.add(new CompatChange(id, name, -1, sdk, false, false, "", false)); return this; } CompatConfigBuilder addEnableSinceSdkChangeWithIdDefaultDisabled(int sdk, long id) { - mChanges.add(new CompatChange(id, "", -1, sdk, true, false, "")); + mChanges.add(new CompatChange(id, "", -1, sdk, true, false, "", false)); return this; } CompatConfigBuilder addEnableSinceSdkChangeWithIdAndDescription(int sdk, long id, String description) { - mChanges.add(new CompatChange(id, "", -1, sdk, false, false, description)); + mChanges.add(new CompatChange(id, "", -1, sdk, false, false, description, false)); return this; } CompatConfigBuilder addEnabledChangeWithId(long id) { - mChanges.add(new CompatChange(id, "", -1, -1, false, false, "")); + mChanges.add(new CompatChange(id, "", -1, -1, false, false, "", false)); return this; } CompatConfigBuilder addEnabledChangeWithIdAndName(long id, String name) { - mChanges.add(new CompatChange(id, name, -1, -1, false, false, "")); + mChanges.add(new CompatChange(id, name, -1, -1, false, false, "", false)); return this; } CompatConfigBuilder addEnabledChangeWithIdAndDescription(long id, String description) { - mChanges.add(new CompatChange(id, "", -1, -1, false, false, description)); + mChanges.add(new CompatChange(id, "", -1, -1, false, false, description, false)); return this; } CompatConfigBuilder addDisabledChangeWithId(long id) { - mChanges.add(new CompatChange(id, "", -1, -1, true, false, "")); + mChanges.add(new CompatChange(id, "", -1, -1, true, false, "", false)); return this; } CompatConfigBuilder addDisabledChangeWithIdAndName(long id, String name) { - mChanges.add(new CompatChange(id, name, -1, -1, true, false, "")); + mChanges.add(new CompatChange(id, name, -1, -1, true, false, "", false)); return this; } CompatConfigBuilder addDisabledChangeWithIdAndDescription(long id, String description) { - mChanges.add(new CompatChange(id, "", -1, -1, true, false, description)); + mChanges.add(new CompatChange(id, "", -1, -1, true, false, description, false)); return this; } CompatConfigBuilder addLoggingOnlyChangeWithId(long id) { - mChanges.add(new CompatChange(id, "", -1, -1, false, true, "")); + mChanges.add(new CompatChange(id, "", -1, -1, false, true, "", false)); + return this; + } + + CompatConfigBuilder addOverridableChangeWithId(long id) { + mChanges.add(new CompatChange(id, "", -1, -1, false, true, "", true)); return this; } diff --git a/services/tests/servicestests/src/com/android/server/compat/PlatformCompatTest.java b/services/tests/servicestests/src/com/android/server/compat/PlatformCompatTest.java index a70c51045340..a1b2dc8bd82d 100644 --- a/services/tests/servicestests/src/com/android/server/compat/PlatformCompatTest.java +++ b/services/tests/servicestests/src/com/android/server/compat/PlatformCompatTest.java @@ -97,17 +97,22 @@ public class PlatformCompatTest { .addEnableAfterSdkChangeWithId(Build.VERSION_CODES.Q, 5L) .addEnableAfterSdkChangeWithId(Build.VERSION_CODES.R, 6L) .addLoggingOnlyChangeWithId(7L) + .addOverridableChangeWithId(8L) .build(); mPlatformCompat = new PlatformCompat(mContext, mCompatConfig); assertThat(mPlatformCompat.listAllChanges()).asList().containsExactly( - new CompatibilityChangeInfo(1L, "", -1, -1, false, false, ""), - new CompatibilityChangeInfo(2L, "change2", -1, -1, true, false, ""), + new CompatibilityChangeInfo(1L, "", -1, -1, false, false, "", false), + new CompatibilityChangeInfo(2L, "change2", -1, -1, true, false, "", false), new CompatibilityChangeInfo(3L, "", Build.VERSION_CODES.O, -1, false, false, - "desc"), - new CompatibilityChangeInfo(4L, "", Build.VERSION_CODES.P, -1, false, false, ""), - new CompatibilityChangeInfo(5L, "", Build.VERSION_CODES.Q, -1, false, false, ""), - new CompatibilityChangeInfo(6L, "", Build.VERSION_CODES.R, -1, false, false, ""), - new CompatibilityChangeInfo(7L, "", -1, -1, false, true, "")); + "desc", false), + new CompatibilityChangeInfo( + 4L, "", Build.VERSION_CODES.P, -1, false, false, "", false), + new CompatibilityChangeInfo( + 5L, "", Build.VERSION_CODES.Q, -1, false, false, "", false), + new CompatibilityChangeInfo( + 6L, "", Build.VERSION_CODES.R, -1, false, false, "", false), + new CompatibilityChangeInfo(7L, "", -1, -1, false, true, "", false), + new CompatibilityChangeInfo(8L, "", -1, -1, false, true, "", true)); } @Test @@ -123,12 +128,12 @@ public class PlatformCompatTest { .build(); mPlatformCompat = new PlatformCompat(mContext, mCompatConfig); assertThat(mPlatformCompat.listUIChanges()).asList().containsExactly( - new CompatibilityChangeInfo(1L, "", -1, -1, false, false, ""), - new CompatibilityChangeInfo(2L, "change2", -1, -1, true, false, ""), - new CompatibilityChangeInfo(5L, "", /*enableAfter*/ -1, - /*enableSince*/ Build.VERSION_CODES.Q, false, false, ""), - new CompatibilityChangeInfo(6L, "", /*enableAfter*/ -1, - /*enableSince*/ Build.VERSION_CODES.R, false, false, "")); + new CompatibilityChangeInfo(1L, "", -1, -1, false, false, "", false), + new CompatibilityChangeInfo(2L, "change2", -1, -1, true, false, "", false), + new CompatibilityChangeInfo( + 5L, "", Build.VERSION_CODES.P, -1, false, false, "", false), + new CompatibilityChangeInfo( + 6L, "", Build.VERSION_CODES.Q, -1, false, false, "", false)); } @Test diff --git a/services/tests/servicestests/src/com/android/server/devicepolicy/DevicePolicyManagerTest.java b/services/tests/servicestests/src/com/android/server/devicepolicy/DevicePolicyManagerTest.java index 39fa20e4153f..a455ba90ccfe 100644 --- a/services/tests/servicestests/src/com/android/server/devicepolicy/DevicePolicyManagerTest.java +++ b/services/tests/servicestests/src/com/android/server/devicepolicy/DevicePolicyManagerTest.java @@ -6918,6 +6918,35 @@ public class DevicePolicyManagerTest extends DpmTestBase { DevicePolicyManager.PASSWORD_QUALITY_UNSPECIFIED); } + @Test + public void testSetRequiredPasswordComplexityFailsWithQualityOnParent() throws Exception { + final int managedProfileUserId = CALLER_USER_HANDLE; + final int managedProfileAdminUid = + UserHandle.getUid(managedProfileUserId, DpmMockContext.SYSTEM_UID); + mContext.binder.callingUid = managedProfileAdminUid; + addManagedProfile(admin1, managedProfileAdminUid, admin1, VERSION_CODES.R); + + parentDpm.setPasswordQuality(admin1, DevicePolicyManager.PASSWORD_QUALITY_COMPLEX); + + assertThrows(IllegalStateException.class, + () -> dpm.setRequiredPasswordComplexity(PASSWORD_COMPLEXITY_HIGH)); + } + + @Test + public void testSetQualityOnParentFailsWithComplexityOnProfile() throws Exception { + final int managedProfileUserId = CALLER_USER_HANDLE; + final int managedProfileAdminUid = + UserHandle.getUid(managedProfileUserId, DpmMockContext.SYSTEM_UID); + mContext.binder.callingUid = managedProfileAdminUid; + addManagedProfile(admin1, managedProfileAdminUid, admin1, VERSION_CODES.R); + + dpm.setRequiredPasswordComplexity(PASSWORD_COMPLEXITY_HIGH); + + assertThrows(IllegalStateException.class, + () -> parentDpm.setPasswordQuality(admin1, + DevicePolicyManager.PASSWORD_QUALITY_COMPLEX)); + } + private void setUserUnlocked(int userHandle, boolean unlocked) { when(getServices().userManager.isUserUnlocked(eq(userHandle))).thenReturn(unlocked); } diff --git a/services/tests/servicestests/src/com/android/server/display/DisplayModeDirectorTest.java b/services/tests/servicestests/src/com/android/server/display/DisplayModeDirectorTest.java index 603608b23172..26c304f763d6 100644 --- a/services/tests/servicestests/src/com/android/server/display/DisplayModeDirectorTest.java +++ b/services/tests/servicestests/src/com/android/server/display/DisplayModeDirectorTest.java @@ -34,6 +34,7 @@ import static org.mockito.Mockito.any; import static org.mockito.Mockito.spy; import static org.mockito.Mockito.verify; import static org.mockito.Mockito.when; +import static org.mockito.internal.verification.VerificationModeFactory.times; import android.annotation.NonNull; import android.content.ContentResolver; @@ -505,7 +506,7 @@ public class DisplayModeDirectorTest { createDirectorFromRefreshRateArray(new float[] {60.f, 90.f}, 0); setPeakRefreshRate(90); director.getSettingsObserver().setDefaultRefreshRate(90); - director.getBrightnessObserver().setDefaultDisplayState(true); + director.getBrightnessObserver().setDefaultDisplayState(Display.STATE_ON); final FakeDeviceConfig config = mInjector.getDeviceConfig(); config.setRefreshRateInLowZone(90); @@ -548,7 +549,7 @@ public class DisplayModeDirectorTest { createDirectorFromRefreshRateArray(new float[] {60.f, 90.f}, 0); setPeakRefreshRate(90 /*fps*/); director.getSettingsObserver().setDefaultRefreshRate(90); - director.getBrightnessObserver().setDefaultDisplayState(true); + director.getBrightnessObserver().setDefaultDisplayState(Display.STATE_ON); final FakeDeviceConfig config = mInjector.getDeviceConfig(); config.setRefreshRateInHighZone(60); @@ -585,6 +586,43 @@ public class DisplayModeDirectorTest { assertVoteForRefreshRateLocked(vote, 60 /*fps*/); } + @Test + public void testSensorRegistration() { + DisplayModeDirector director = + createDirectorFromRefreshRateArray(new float[] {60.f, 90.f}, 0); + setPeakRefreshRate(90 /*fps*/); + director.getSettingsObserver().setDefaultRefreshRate(90); + director.getBrightnessObserver().setDefaultDisplayState(Display.STATE_ON); + + Sensor lightSensor = createLightSensor(); + SensorManager sensorManager = createMockSensorManager(lightSensor); + + director.start(sensorManager); + ArgumentCaptor<SensorEventListener> listenerCaptor = + ArgumentCaptor.forClass(SensorEventListener.class); + Mockito.verify(sensorManager, Mockito.timeout(TimeUnit.SECONDS.toMillis(1))) + .registerListener( + listenerCaptor.capture(), + eq(lightSensor), + anyInt(), + any(Handler.class)); + + // Dispaly state changed from On to Doze + director.getBrightnessObserver().setDefaultDisplayState(Display.STATE_DOZE); + Mockito.verify(sensorManager) + .unregisterListener(listenerCaptor.capture()); + + // Dispaly state changed from Doze to On + director.getBrightnessObserver().setDefaultDisplayState(Display.STATE_ON); + Mockito.verify(sensorManager, times(2)) + .registerListener( + listenerCaptor.capture(), + eq(lightSensor), + anyInt(), + any(Handler.class)); + + } + private void assertVoteForRefreshRateLocked(Vote vote, float refreshRate) { assertThat(vote).isNotNull(); final DisplayModeDirector.RefreshRateRange expectedRange = diff --git a/services/tests/servicestests/src/com/android/server/pm/PackageManagerServiceTest.java b/services/tests/servicestests/src/com/android/server/pm/PackageManagerServiceTest.java index d54a40e58af1..c010e1995446 100644 --- a/services/tests/servicestests/src/com/android/server/pm/PackageManagerServiceTest.java +++ b/services/tests/servicestests/src/com/android/server/pm/PackageManagerServiceTest.java @@ -29,6 +29,10 @@ import android.util.SparseArray; import androidx.test.runner.AndroidJUnit4; +import com.android.internal.util.HexDump; +import com.android.server.pm.PerPackageReadTimeouts.Timeouts; +import com.android.server.pm.PerPackageReadTimeouts.VersionCodes; + import com.google.android.collect.Lists; import org.junit.After; @@ -45,6 +49,7 @@ import java.util.Collections; import java.util.List; import java.util.regex.Pattern; +// atest PackageManagerServiceTest // runtest -c com.android.server.pm.PackageManagerServiceTest frameworks-services // bit FrameworksServicesTests:com.android.server.pm.PackageManagerServiceTest @RunWith(AndroidJUnit4.class) @@ -182,6 +187,219 @@ public class PackageManagerServiceTest { } } + @Test + public void testTimeouts() { + Timeouts defaults = Timeouts.parse("3600000001:3600000002:3600000003"); + Assert.assertEquals(3600000001L, defaults.minTimeUs); + Assert.assertEquals(3600000002L, defaults.minPendingTimeUs); + Assert.assertEquals(3600000003L, defaults.maxPendingTimeUs); + + Timeouts empty = Timeouts.parse(""); + Assert.assertEquals(3600000000L, empty.minTimeUs); + Assert.assertEquals(3600000000L, empty.minPendingTimeUs); + Assert.assertEquals(3600000000L, empty.maxPendingTimeUs); + + Timeouts partial0 = Timeouts.parse("10000::"); + Assert.assertEquals(10000L, partial0.minTimeUs); + Assert.assertEquals(3600000000L, partial0.minPendingTimeUs); + Assert.assertEquals(3600000000L, partial0.maxPendingTimeUs); + + Timeouts partial1 = Timeouts.parse("10000:10001:"); + Assert.assertEquals(10000L, partial1.minTimeUs); + Assert.assertEquals(10001L, partial1.minPendingTimeUs); + Assert.assertEquals(3600000000L, partial1.maxPendingTimeUs); + + Timeouts fullDefault = Timeouts.parse("3600000000:3600000000:3600000000"); + Assert.assertEquals(3600000000L, fullDefault.minTimeUs); + Assert.assertEquals(3600000000L, fullDefault.minPendingTimeUs); + Assert.assertEquals(3600000000L, fullDefault.maxPendingTimeUs); + + Timeouts full = Timeouts.parse("10000:10001:10002"); + Assert.assertEquals(10000L, full.minTimeUs); + Assert.assertEquals(10001L, full.minPendingTimeUs); + Assert.assertEquals(10002L, full.maxPendingTimeUs); + + Timeouts invalid0 = Timeouts.parse(":10000"); + Assert.assertEquals(3600000000L, invalid0.minTimeUs); + Assert.assertEquals(3600000000L, invalid0.minPendingTimeUs); + Assert.assertEquals(3600000000L, invalid0.maxPendingTimeUs); + + Timeouts invalid1 = Timeouts.parse(":10000::"); + Assert.assertEquals(3600000000L, invalid1.minTimeUs); + Assert.assertEquals(3600000000L, invalid1.minPendingTimeUs); + Assert.assertEquals(3600000000L, invalid1.maxPendingTimeUs); + + Timeouts invalid2 = Timeouts.parse("10000:10001:abcd"); + Assert.assertEquals(10000L, invalid2.minTimeUs); + Assert.assertEquals(10001L, invalid2.minPendingTimeUs); + Assert.assertEquals(3600000000L, invalid2.maxPendingTimeUs); + + Timeouts invalid3 = Timeouts.parse(":10000:"); + Assert.assertEquals(3600000000L, invalid3.minTimeUs); + Assert.assertEquals(3600000000L, invalid3.minPendingTimeUs); + Assert.assertEquals(3600000000L, invalid3.maxPendingTimeUs); + + Timeouts invalid4 = Timeouts.parse("abcd:10001:10002"); + Assert.assertEquals(3600000000L, invalid4.minTimeUs); + Assert.assertEquals(3600000000L, invalid4.minPendingTimeUs); + Assert.assertEquals(3600000000L, invalid4.maxPendingTimeUs); + + Timeouts invalid5 = Timeouts.parse("::1000000000000000000000000"); + Assert.assertEquals(3600000000L, invalid5.minTimeUs); + Assert.assertEquals(3600000000L, invalid5.minPendingTimeUs); + Assert.assertEquals(3600000000L, invalid5.maxPendingTimeUs); + + Timeouts invalid6 = Timeouts.parse("-10000:10001:10002"); + Assert.assertEquals(3600000000L, invalid6.minTimeUs); + Assert.assertEquals(3600000000L, invalid6.minPendingTimeUs); + Assert.assertEquals(3600000000L, invalid6.maxPendingTimeUs); + } + + @Test + public void testVersionCodes() { + final VersionCodes defaults = VersionCodes.parse(""); + Assert.assertEquals(Long.MIN_VALUE, defaults.minVersionCode); + Assert.assertEquals(Long.MAX_VALUE, defaults.maxVersionCode); + + VersionCodes single = VersionCodes.parse("191000070"); + Assert.assertEquals(191000070, single.minVersionCode); + Assert.assertEquals(191000070, single.maxVersionCode); + + VersionCodes single2 = VersionCodes.parse("191000070-191000070"); + Assert.assertEquals(191000070, single2.minVersionCode); + Assert.assertEquals(191000070, single2.maxVersionCode); + + VersionCodes upto = VersionCodes.parse("-191000070"); + Assert.assertEquals(Long.MIN_VALUE, upto.minVersionCode); + Assert.assertEquals(191000070, upto.maxVersionCode); + + VersionCodes andabove = VersionCodes.parse("191000070-"); + Assert.assertEquals(191000070, andabove.minVersionCode); + Assert.assertEquals(Long.MAX_VALUE, andabove.maxVersionCode); + + VersionCodes range = VersionCodes.parse("191000070-201000070"); + Assert.assertEquals(191000070, range.minVersionCode); + Assert.assertEquals(201000070, range.maxVersionCode); + + VersionCodes invalid0 = VersionCodes.parse("201000070-191000070"); + Assert.assertEquals(Long.MIN_VALUE, invalid0.minVersionCode); + Assert.assertEquals(Long.MAX_VALUE, invalid0.maxVersionCode); + + VersionCodes invalid1 = VersionCodes.parse("abcd-191000070"); + Assert.assertEquals(Long.MIN_VALUE, invalid1.minVersionCode); + Assert.assertEquals(191000070, invalid1.maxVersionCode); + + VersionCodes invalid2 = VersionCodes.parse("abcd"); + Assert.assertEquals(Long.MIN_VALUE, invalid2.minVersionCode); + Assert.assertEquals(Long.MAX_VALUE, invalid2.maxVersionCode); + + VersionCodes invalid3 = VersionCodes.parse("191000070-abcd"); + Assert.assertEquals(191000070, invalid3.minVersionCode); + Assert.assertEquals(Long.MAX_VALUE, invalid3.maxVersionCode); + } + + @Test + public void testPerPackageReadTimeouts() { + final String sha256 = "336faefc91bb2dddf9b21829106fbc607b862132fecd273e1b6b3ea55f09d4e1"; + final VersionCodes defVCs = VersionCodes.parse(""); + final Timeouts defTs = Timeouts.parse("3600000001:3600000002:3600000003"); + + PerPackageReadTimeouts empty = PerPackageReadTimeouts.parse("", defVCs, defTs); + Assert.assertNull(empty); + + PerPackageReadTimeouts packageOnly = PerPackageReadTimeouts.parse("package.com", defVCs, + defTs); + Assert.assertEquals("package.com", packageOnly.packageName); + Assert.assertEquals(null, packageOnly.sha256certificate); + Assert.assertEquals(Long.MIN_VALUE, packageOnly.versionCodes.minVersionCode); + Assert.assertEquals(Long.MAX_VALUE, packageOnly.versionCodes.maxVersionCode); + Assert.assertEquals(3600000001L, packageOnly.timeouts.minTimeUs); + Assert.assertEquals(3600000002L, packageOnly.timeouts.minPendingTimeUs); + Assert.assertEquals(3600000003L, packageOnly.timeouts.maxPendingTimeUs); + + PerPackageReadTimeouts packageHash = PerPackageReadTimeouts.parse( + "package.com:" + sha256, defVCs, defTs); + Assert.assertEquals("package.com", packageHash.packageName); + Assert.assertEquals(sha256, bytesToHexString(packageHash.sha256certificate)); + Assert.assertEquals(Long.MIN_VALUE, packageHash.versionCodes.minVersionCode); + Assert.assertEquals(Long.MAX_VALUE, packageHash.versionCodes.maxVersionCode); + Assert.assertEquals(3600000001L, packageHash.timeouts.minTimeUs); + Assert.assertEquals(3600000002L, packageHash.timeouts.minPendingTimeUs); + Assert.assertEquals(3600000003L, packageHash.timeouts.maxPendingTimeUs); + + PerPackageReadTimeouts packageVersionCode = PerPackageReadTimeouts.parse( + "package.com::191000070", defVCs, defTs); + Assert.assertEquals("package.com", packageVersionCode.packageName); + Assert.assertEquals(null, packageVersionCode.sha256certificate); + Assert.assertEquals(191000070, packageVersionCode.versionCodes.minVersionCode); + Assert.assertEquals(191000070, packageVersionCode.versionCodes.maxVersionCode); + Assert.assertEquals(3600000001L, packageVersionCode.timeouts.minTimeUs); + Assert.assertEquals(3600000002L, packageVersionCode.timeouts.minPendingTimeUs); + Assert.assertEquals(3600000003L, packageVersionCode.timeouts.maxPendingTimeUs); + + PerPackageReadTimeouts full = PerPackageReadTimeouts.parse( + "package.com:" + sha256 + ":191000070-201000070:10001:10002:10003", defVCs, defTs); + Assert.assertEquals("package.com", full.packageName); + Assert.assertEquals(sha256, bytesToHexString(full.sha256certificate)); + Assert.assertEquals(191000070, full.versionCodes.minVersionCode); + Assert.assertEquals(201000070, full.versionCodes.maxVersionCode); + Assert.assertEquals(10001L, full.timeouts.minTimeUs); + Assert.assertEquals(10002L, full.timeouts.minPendingTimeUs); + Assert.assertEquals(10003L, full.timeouts.maxPendingTimeUs); + } + + @Test + public void testGetPerPackageReadTimeouts() { + Assert.assertEquals(0, getPerPackageReadTimeouts(null).length); + Assert.assertEquals(0, getPerPackageReadTimeouts("").length); + Assert.assertEquals(0, getPerPackageReadTimeouts(",,,,").length); + + final String sha256 = "0fae93f1a7925b4c68bbea80ad3eaa41acfc9bc6f10bf1054f5d93a2bd556093"; + + PerPackageReadTimeouts[] singlePackage = getPerPackageReadTimeouts( + "package.com:" + sha256 + ":191000070-201000070:10001:10002:10003"); + Assert.assertEquals(1, singlePackage.length); + Assert.assertEquals("package.com", singlePackage[0].packageName); + Assert.assertEquals(sha256, bytesToHexString(singlePackage[0].sha256certificate)); + Assert.assertEquals(191000070, singlePackage[0].versionCodes.minVersionCode); + Assert.assertEquals(201000070, singlePackage[0].versionCodes.maxVersionCode); + Assert.assertEquals(10001L, singlePackage[0].timeouts.minTimeUs); + Assert.assertEquals(10002L, singlePackage[0].timeouts.minPendingTimeUs); + Assert.assertEquals(10003L, singlePackage[0].timeouts.maxPendingTimeUs); + + PerPackageReadTimeouts[] multiPackage = getPerPackageReadTimeouts("package.com:" + sha256 + + ":191000070-201000070:10001:10002:10003,package1.com::123456"); + Assert.assertEquals(2, multiPackage.length); + Assert.assertEquals("package.com", multiPackage[0].packageName); + Assert.assertEquals(sha256, bytesToHexString(multiPackage[0].sha256certificate)); + Assert.assertEquals(191000070, multiPackage[0].versionCodes.minVersionCode); + Assert.assertEquals(201000070, multiPackage[0].versionCodes.maxVersionCode); + Assert.assertEquals(10001L, multiPackage[0].timeouts.minTimeUs); + Assert.assertEquals(10002L, multiPackage[0].timeouts.minPendingTimeUs); + Assert.assertEquals(10003L, multiPackage[0].timeouts.maxPendingTimeUs); + Assert.assertEquals("package1.com", multiPackage[1].packageName); + Assert.assertEquals(null, multiPackage[1].sha256certificate); + Assert.assertEquals(123456, multiPackage[1].versionCodes.minVersionCode); + Assert.assertEquals(123456, multiPackage[1].versionCodes.maxVersionCode); + Assert.assertEquals(3600000001L, multiPackage[1].timeouts.minTimeUs); + Assert.assertEquals(3600000002L, multiPackage[1].timeouts.minPendingTimeUs); + Assert.assertEquals(3600000003L, multiPackage[1].timeouts.maxPendingTimeUs); + } + + private static PerPackageReadTimeouts[] getPerPackageReadTimeouts(String knownDigestersList) { + final String defaultTimeouts = "3600000001:3600000002:3600000003"; + List<PerPackageReadTimeouts> result = PerPackageReadTimeouts.parseDigestersList( + defaultTimeouts, knownDigestersList); + if (result == null) { + return null; + } + return result.toArray(new PerPackageReadTimeouts[result.size()]); + } + + private static String bytesToHexString(byte[] bytes) { + return HexDump.toHexString(bytes, 0, bytes.length, /*upperCase=*/ false); + } + private List<Integer> getKnownPackageIdsList() throws IllegalAccessException { final ArrayList<Integer> knownPackageIds = new ArrayList<>(); final Field[] allFields = PackageManagerInternal.class.getDeclaredFields(); diff --git a/services/tests/servicestests/src/com/android/server/tv/tunerresourcemanager/TunerResourceManagerServiceTest.java b/services/tests/servicestests/src/com/android/server/tv/tunerresourcemanager/TunerResourceManagerServiceTest.java index 62be98c15a2e..385837009b2f 100644 --- a/services/tests/servicestests/src/com/android/server/tv/tunerresourcemanager/TunerResourceManagerServiceTest.java +++ b/services/tests/servicestests/src/com/android/server/tv/tunerresourcemanager/TunerResourceManagerServiceTest.java @@ -29,6 +29,7 @@ import android.media.tv.tuner.frontend.FrontendSettings; import android.media.tv.tunerresourcemanager.CasSessionRequest; import android.media.tv.tunerresourcemanager.IResourcesReclaimListener; import android.media.tv.tunerresourcemanager.ResourceClientProfile; +import android.media.tv.tunerresourcemanager.TunerCiCamRequest; import android.media.tv.tunerresourcemanager.TunerDemuxRequest; import android.media.tv.tunerresourcemanager.TunerDescramblerRequest; import android.media.tv.tunerresourcemanager.TunerFrontendInfo; @@ -86,9 +87,9 @@ public class TunerResourceManagerServiceTest { return (actual == null) && (expected == null); } - return actual.getHandle() == expected.getHandle() - && actual.getType() == expected.getFrontendType() - && actual.getExclusiveGroupId() == expected.getExclusiveGroupId(); + return actual.getHandle() == expected.handle + && actual.getType() == expected.frontendType + && actual.getExclusiveGroupId() == expected.exclusiveGroupId; }, "is correctly configured from "); @Before @@ -99,7 +100,7 @@ public class TunerResourceManagerServiceTest { when(mContextSpy.getSystemService(Context.TV_INPUT_SERVICE)).thenReturn(tvInputManager); mTunerResourceManagerService = new TunerResourceManagerService(mContextSpy) { @Override - protected boolean isForeground(int pid) { + protected boolean checkIsForeground(int pid) { return mIsForeground; } }; @@ -111,19 +112,19 @@ public class TunerResourceManagerServiceTest { // Init frontend resources. TunerFrontendInfo[] infos = new TunerFrontendInfo[2]; infos[0] = - new TunerFrontendInfo(0 /*id*/, FrontendSettings.TYPE_DVBT, 0 /*exclusiveGroupId*/); + tunerFrontendInfo(0 /*handle*/, FrontendSettings.TYPE_DVBT, 0 /*exclusiveGroupId*/); infos[1] = - new TunerFrontendInfo(1 /*id*/, FrontendSettings.TYPE_DVBT, 1 /*exclusiveGroupId*/); + tunerFrontendInfo(1 /*handle*/, FrontendSettings.TYPE_DVBT, 1 /*exclusiveGroupId*/); mTunerResourceManagerService.setFrontendInfoListInternal(infos); Map<Integer, FrontendResource> resources = mTunerResourceManagerService.getFrontendResources(); for (int id = 0; id < infos.length; id++) { - assertThat(resources.get(infos[id].getHandle()) + assertThat(resources.get(infos[id].handle) .getExclusiveGroupMemberFeHandles().size()).isEqualTo(0); } for (int id = 0; id < infos.length; id++) { - assertThat(resources.get(infos[id].getHandle()) + assertThat(resources.get(infos[id].handle) .getExclusiveGroupMemberFeHandles().size()).isEqualTo(0); } assertThat(resources.values()).comparingElementsUsing(FR_TFI_COMPARE) @@ -135,13 +136,13 @@ public class TunerResourceManagerServiceTest { // Init frontend resources. TunerFrontendInfo[] infos = new TunerFrontendInfo[4]; infos[0] = - new TunerFrontendInfo(0 /*id*/, FrontendSettings.TYPE_DVBT, 0 /*exclusiveGroupId*/); + tunerFrontendInfo(0 /*handle*/, FrontendSettings.TYPE_DVBT, 0 /*exclusiveGroupId*/); infos[1] = - new TunerFrontendInfo(1 /*id*/, FrontendSettings.TYPE_DVBT, 1 /*exclusiveGroupId*/); + tunerFrontendInfo(1 /*handle*/, FrontendSettings.TYPE_DVBT, 1 /*exclusiveGroupId*/); infos[2] = - new TunerFrontendInfo(2 /*id*/, FrontendSettings.TYPE_DVBS, 1 /*exclusiveGroupId*/); + tunerFrontendInfo(2 /*handle*/, FrontendSettings.TYPE_DVBS, 1 /*exclusiveGroupId*/); infos[3] = - new TunerFrontendInfo(3 /*id*/, FrontendSettings.TYPE_ATSC, 1 /*exclusiveGroupId*/); + tunerFrontendInfo(3 /*handle*/, FrontendSettings.TYPE_ATSC, 1 /*exclusiveGroupId*/); mTunerResourceManagerService.setFrontendInfoListInternal(infos); Map<Integer, FrontendResource> resources = @@ -160,9 +161,9 @@ public class TunerResourceManagerServiceTest { // Init frontend resources. TunerFrontendInfo[] infos = new TunerFrontendInfo[2]; infos[0] = - new TunerFrontendInfo(0 /*id*/, FrontendSettings.TYPE_DVBT, 1 /*exclusiveGroupId*/); + tunerFrontendInfo(0 /*handle*/, FrontendSettings.TYPE_DVBT, 1 /*exclusiveGroupId*/); infos[1] = - new TunerFrontendInfo(1 /*id*/, FrontendSettings.TYPE_DVBS, 1 /*exclusiveGroupId*/); + tunerFrontendInfo(1 /*handle*/, FrontendSettings.TYPE_DVBS, 1 /*exclusiveGroupId*/); mTunerResourceManagerService.setFrontendInfoListInternal(infos); Map<Integer, FrontendResource> resources0 = @@ -180,22 +181,22 @@ public class TunerResourceManagerServiceTest { // Init frontend resources. TunerFrontendInfo[] infos0 = new TunerFrontendInfo[3]; infos0[0] = - new TunerFrontendInfo(0 /*id*/, FrontendSettings.TYPE_DVBT, 0 /*exclusiveGroupId*/); + tunerFrontendInfo(0 /*handle*/, FrontendSettings.TYPE_DVBT, 0 /*exclusiveGroupId*/); infos0[1] = - new TunerFrontendInfo(1 /*id*/, FrontendSettings.TYPE_DVBT, 1 /*exclusiveGroupId*/); + tunerFrontendInfo(1 /*handle*/, FrontendSettings.TYPE_DVBT, 1 /*exclusiveGroupId*/); infos0[2] = - new TunerFrontendInfo(2 /*id*/, FrontendSettings.TYPE_DVBS, 2 /*exclusiveGroupId*/); + tunerFrontendInfo(2 /*handle*/, FrontendSettings.TYPE_DVBS, 2 /*exclusiveGroupId*/); mTunerResourceManagerService.setFrontendInfoListInternal(infos0); TunerFrontendInfo[] infos1 = new TunerFrontendInfo[1]; infos1[0] = - new TunerFrontendInfo(1 /*id*/, FrontendSettings.TYPE_DVBT, 1 /*exclusiveGroupId*/); + tunerFrontendInfo(1 /*handle*/, FrontendSettings.TYPE_DVBT, 1 /*exclusiveGroupId*/); mTunerResourceManagerService.setFrontendInfoListInternal(infos1); Map<Integer, FrontendResource> resources = mTunerResourceManagerService.getFrontendResources(); for (int id = 0; id < infos1.length; id++) { - assertThat(resources.get(infos1[id].getHandle()) + assertThat(resources.get(infos1[id].handle) .getExclusiveGroupMemberFeHandles().size()).isEqualTo(0); } assertThat(resources.values()).comparingElementsUsing(FR_TFI_COMPARE) @@ -207,22 +208,22 @@ public class TunerResourceManagerServiceTest { // Init frontend resources. TunerFrontendInfo[] infos0 = new TunerFrontendInfo[3]; infos0[0] = - new TunerFrontendInfo(0 /*id*/, FrontendSettings.TYPE_DVBT, 0 /*exclusiveGroupId*/); + tunerFrontendInfo(0 /*handle*/, FrontendSettings.TYPE_DVBT, 0 /*exclusiveGroupId*/); infos0[1] = - new TunerFrontendInfo(1 /*id*/, FrontendSettings.TYPE_DVBT, 1 /*exclusiveGroupId*/); + tunerFrontendInfo(1 /*handle*/, FrontendSettings.TYPE_DVBT, 1 /*exclusiveGroupId*/); infos0[2] = - new TunerFrontendInfo(2 /*id*/, FrontendSettings.TYPE_DVBS, 1 /*exclusiveGroupId*/); + tunerFrontendInfo(2 /*handle*/, FrontendSettings.TYPE_DVBS, 1 /*exclusiveGroupId*/); mTunerResourceManagerService.setFrontendInfoListInternal(infos0); TunerFrontendInfo[] infos1 = new TunerFrontendInfo[1]; infos1[0] = - new TunerFrontendInfo(1 /*id*/, FrontendSettings.TYPE_DVBT, 1 /*exclusiveGroupId*/); + tunerFrontendInfo(1 /*handle*/, FrontendSettings.TYPE_DVBT, 1 /*exclusiveGroupId*/); mTunerResourceManagerService.setFrontendInfoListInternal(infos1); Map<Integer, FrontendResource> resources = mTunerResourceManagerService.getFrontendResources(); for (int id = 0; id < infos1.length; id++) { - assertThat(resources.get(infos1[id].getHandle()) + assertThat(resources.get(infos1[id].handle) .getExclusiveGroupMemberFeHandles().size()).isEqualTo(0); } assertThat(resources.values()).comparingElementsUsing(FR_TFI_COMPARE) @@ -231,8 +232,12 @@ public class TunerResourceManagerServiceTest { @Test public void requestFrontendTest_ClientNotRegistered() { + TunerFrontendInfo[] infos0 = new TunerFrontendInfo[1]; + infos0[0] = + tunerFrontendInfo(0 /*id*/, FrontendSettings.TYPE_DVBT, 0 /*exclusiveGroupId*/); + mTunerResourceManagerService.setFrontendInfoListInternal(infos0); TunerFrontendRequest request = - new TunerFrontendRequest(0 /*clientId*/, FrontendSettings.TYPE_DVBT); + tunerFrontendRequest(0 /*clientId*/, FrontendSettings.TYPE_DVBT); int[] frontendHandle = new int[1]; assertThat(mTunerResourceManagerService .requestFrontendInternal(request, frontendHandle)).isFalse(); @@ -241,7 +246,7 @@ public class TunerResourceManagerServiceTest { @Test public void requestFrontendTest_NoFrontendWithGiveTypeAvailable() { - ResourceClientProfile profile = new ResourceClientProfile("0" /*sessionId*/, + ResourceClientProfile profile = resourceClientProfile("0" /*sessionId*/, TvInputService.PRIORITY_HINT_USE_CASE_TYPE_PLAYBACK); int[] clientId = new int[1]; mTunerResourceManagerService.registerClientProfileInternal( @@ -251,11 +256,11 @@ public class TunerResourceManagerServiceTest { // Init frontend resources. TunerFrontendInfo[] infos = new TunerFrontendInfo[1]; infos[0] = - new TunerFrontendInfo(0 /*id*/, FrontendSettings.TYPE_DVBS, 0 /*exclusiveGroupId*/); + tunerFrontendInfo(0 /*handle*/, FrontendSettings.TYPE_DVBS, 0 /*exclusiveGroupId*/); mTunerResourceManagerService.setFrontendInfoListInternal(infos); TunerFrontendRequest request = - new TunerFrontendRequest(clientId[0] /*clientId*/, FrontendSettings.TYPE_DVBT); + tunerFrontendRequest(clientId[0] /*clientId*/, FrontendSettings.TYPE_DVBT); int[] frontendHandle = new int[1]; assertThat(mTunerResourceManagerService .requestFrontendInternal(request, frontendHandle)).isFalse(); @@ -264,7 +269,7 @@ public class TunerResourceManagerServiceTest { @Test public void requestFrontendTest_FrontendWithNoExclusiveGroupAvailable() { - ResourceClientProfile profile = new ResourceClientProfile("0" /*sessionId*/, + ResourceClientProfile profile = resourceClientProfile("0" /*sessionId*/, TvInputService.PRIORITY_HINT_USE_CASE_TYPE_PLAYBACK); int[] clientId = new int[1]; mTunerResourceManagerService.registerClientProfileInternal( @@ -273,22 +278,22 @@ public class TunerResourceManagerServiceTest { // Init frontend resources. TunerFrontendInfo[] infos = new TunerFrontendInfo[3]; - infos[0] = new TunerFrontendInfo( + infos[0] = tunerFrontendInfo( 0 /*handle*/, FrontendSettings.TYPE_DVBT, 0 /*exclusiveGroupId*/); - infos[1] = new TunerFrontendInfo( + infos[1] = tunerFrontendInfo( 1 /*handle*/, FrontendSettings.TYPE_DVBT, 1 /*exclusiveGroupId*/); - infos[2] = new TunerFrontendInfo( + infos[2] = tunerFrontendInfo( 2 /*handle*/, FrontendSettings.TYPE_DVBS, 1 /*exclusiveGroupId*/); mTunerResourceManagerService.setFrontendInfoListInternal(infos); TunerFrontendRequest request = - new TunerFrontendRequest(clientId[0] /*clientId*/, FrontendSettings.TYPE_DVBT); + tunerFrontendRequest(clientId[0] /*clientId*/, FrontendSettings.TYPE_DVBT); int[] frontendHandle = new int[1]; assertThat(mTunerResourceManagerService .requestFrontendInternal(request, frontendHandle)).isTrue(); @@ -297,9 +302,9 @@ public class TunerResourceManagerServiceTest { @Test public void requestFrontendTest_FrontendWithExclusiveGroupAvailable() { - ResourceClientProfile profile0 = new ResourceClientProfile("0" /*sessionId*/, + ResourceClientProfile profile0 = resourceClientProfile("0" /*sessionId*/, TvInputService.PRIORITY_HINT_USE_CASE_TYPE_PLAYBACK); - ResourceClientProfile profile1 = new ResourceClientProfile("1" /*sessionId*/, + ResourceClientProfile profile1 = resourceClientProfile("1" /*sessionId*/, TvInputService.PRIORITY_HINT_USE_CASE_TYPE_PLAYBACK); int[] clientId0 = new int[1]; int[] clientId1 = new int[1]; @@ -312,15 +317,15 @@ public class TunerResourceManagerServiceTest { // Init frontend resources. TunerFrontendInfo[] infos = new TunerFrontendInfo[3]; - infos[0] = new TunerFrontendInfo( + infos[0] = tunerFrontendInfo( 0 /*handle*/, FrontendSettings.TYPE_DVBT, 0 /*exclusiveGroupId*/); - infos[1] = new TunerFrontendInfo( + infos[1] = tunerFrontendInfo( 1 /*handle*/, FrontendSettings.TYPE_DVBT, 1 /*exclusiveGroupId*/); - infos[2] = new TunerFrontendInfo( + infos[2] = tunerFrontendInfo( 2 /*handle*/, FrontendSettings.TYPE_DVBS, 1 /*exclusiveGroupId*/); @@ -328,19 +333,19 @@ public class TunerResourceManagerServiceTest { int[] frontendHandle = new int[1]; TunerFrontendRequest request = - new TunerFrontendRequest(clientId1[0] /*clientId*/, FrontendSettings.TYPE_DVBT); + tunerFrontendRequest(clientId1[0] /*clientId*/, FrontendSettings.TYPE_DVBT); assertThat(mTunerResourceManagerService .requestFrontendInternal(request, frontendHandle)).isTrue(); - assertThat(frontendHandle[0]).isEqualTo(infos[0].getHandle()); + assertThat(frontendHandle[0]).isEqualTo(infos[0].handle); request = - new TunerFrontendRequest(clientId0[0] /*clientId*/, FrontendSettings.TYPE_DVBT); + tunerFrontendRequest(clientId0[0] /*clientId*/, FrontendSettings.TYPE_DVBT); assertThat(mTunerResourceManagerService .requestFrontendInternal(request, frontendHandle)).isTrue(); - assertThat(frontendHandle[0]).isEqualTo(infos[1].getHandle()); - assertThat(mTunerResourceManagerService.getFrontendResource(infos[1].getHandle()).isInUse()) + assertThat(frontendHandle[0]).isEqualTo(infos[1].handle); + assertThat(mTunerResourceManagerService.getFrontendResource(infos[1].handle).isInUse()) .isTrue(); - assertThat(mTunerResourceManagerService.getFrontendResource(infos[2].getHandle()).isInUse()) + assertThat(mTunerResourceManagerService.getFrontendResource(infos[2].handle).isInUse()) .isTrue(); } @@ -348,9 +353,9 @@ public class TunerResourceManagerServiceTest { public void requestFrontendTest_NoFrontendAvailable_RequestWithLowerPriority() { // Register clients ResourceClientProfile[] profiles = new ResourceClientProfile[2]; - profiles[0] = new ResourceClientProfile("0" /*sessionId*/, + profiles[0] = resourceClientProfile("0" /*sessionId*/, TvInputService.PRIORITY_HINT_USE_CASE_TYPE_PLAYBACK); - profiles[1] = new ResourceClientProfile("1" /*sessionId*/, + profiles[1] = resourceClientProfile("1" /*sessionId*/, TvInputService.PRIORITY_HINT_USE_CASE_TYPE_PLAYBACK); int[] clientPriorities = {100, 50}; int[] clientId0 = new int[1]; @@ -371,25 +376,25 @@ public class TunerResourceManagerServiceTest { // Init frontend resources. TunerFrontendInfo[] infos = new TunerFrontendInfo[2]; infos[0] = - new TunerFrontendInfo(0 /*id*/, FrontendSettings.TYPE_DVBT, 1 /*exclusiveGroupId*/); + tunerFrontendInfo(0 /*handle*/, FrontendSettings.TYPE_DVBT, 1 /*exclusiveGroupId*/); infos[1] = - new TunerFrontendInfo(1 /*id*/, FrontendSettings.TYPE_DVBS, 1 /*exclusiveGroupId*/); + tunerFrontendInfo(1 /*handle*/, FrontendSettings.TYPE_DVBS, 1 /*exclusiveGroupId*/); mTunerResourceManagerService.setFrontendInfoListInternal(infos); TunerFrontendRequest request = - new TunerFrontendRequest(clientId0[0] /*clientId*/, FrontendSettings.TYPE_DVBT); + tunerFrontendRequest(clientId0[0] /*clientId*/, FrontendSettings.TYPE_DVBT); int[] frontendHandle = new int[1]; assertThat(mTunerResourceManagerService .requestFrontendInternal(request, frontendHandle)).isTrue(); request = - new TunerFrontendRequest(clientId1[0] /*clientId*/, FrontendSettings.TYPE_DVBT); + tunerFrontendRequest(clientId1[0] /*clientId*/, FrontendSettings.TYPE_DVBT); assertThat(mTunerResourceManagerService .requestFrontendInternal(request, frontendHandle)).isFalse(); assertThat(listener.isReclaimed()).isFalse(); request = - new TunerFrontendRequest(clientId1[0] /*clientId*/, FrontendSettings.TYPE_DVBS); + tunerFrontendRequest(clientId1[0] /*clientId*/, FrontendSettings.TYPE_DVBS); assertThat(mTunerResourceManagerService .requestFrontendInternal(request, frontendHandle)).isFalse(); assertThat(listener.isReclaimed()).isFalse(); @@ -399,9 +404,9 @@ public class TunerResourceManagerServiceTest { public void requestFrontendTest_NoFrontendAvailable_RequestWithHigherPriority() { // Register clients ResourceClientProfile[] profiles = new ResourceClientProfile[2]; - profiles[0] = new ResourceClientProfile("0" /*sessionId*/, + profiles[0] = resourceClientProfile("0" /*sessionId*/, TvInputService.PRIORITY_HINT_USE_CASE_TYPE_PLAYBACK); - profiles[1] = new ResourceClientProfile("1" /*sessionId*/, + profiles[1] = resourceClientProfile("1" /*sessionId*/, TvInputService.PRIORITY_HINT_USE_CASE_TYPE_PLAYBACK); int[] clientPriorities = {100, 500}; int[] clientId0 = new int[1]; @@ -421,33 +426,33 @@ public class TunerResourceManagerServiceTest { // Init frontend resources. TunerFrontendInfo[] infos = new TunerFrontendInfo[2]; infos[0] = - new TunerFrontendInfo(0 /*id*/, FrontendSettings.TYPE_DVBT, 1 /*exclusiveGroupId*/); + tunerFrontendInfo(0 /*handle*/, FrontendSettings.TYPE_DVBT, 1 /*exclusiveGroupId*/); infos[1] = - new TunerFrontendInfo(1 /*id*/, FrontendSettings.TYPE_DVBS, 1 /*exclusiveGroupId*/); + tunerFrontendInfo(1 /*handle*/, FrontendSettings.TYPE_DVBS, 1 /*exclusiveGroupId*/); mTunerResourceManagerService.setFrontendInfoListInternal(infos); TunerFrontendRequest request = - new TunerFrontendRequest(clientId0[0] /*clientId*/, FrontendSettings.TYPE_DVBT); + tunerFrontendRequest(clientId0[0] /*clientId*/, FrontendSettings.TYPE_DVBT); int[] frontendHandle = new int[1]; assertThat(mTunerResourceManagerService .requestFrontendInternal(request, frontendHandle)).isTrue(); - assertThat(frontendHandle[0]).isEqualTo(infos[0].getHandle()); + assertThat(frontendHandle[0]).isEqualTo(infos[0].handle); assertThat(mTunerResourceManagerService.getClientProfile(clientId0[0]) .getInUseFrontendHandles()).isEqualTo(new HashSet<Integer>(Arrays.asList( - infos[0].getHandle(), infos[1].getHandle()))); + infos[0].handle, infos[1].handle))); request = - new TunerFrontendRequest(clientId1[0] /*clientId*/, FrontendSettings.TYPE_DVBS); + tunerFrontendRequest(clientId1[0] /*clientId*/, FrontendSettings.TYPE_DVBS); assertThat(mTunerResourceManagerService .requestFrontendInternal(request, frontendHandle)).isTrue(); - assertThat(frontendHandle[0]).isEqualTo(infos[1].getHandle()); - assertThat(mTunerResourceManagerService.getFrontendResource(infos[0].getHandle()) + assertThat(frontendHandle[0]).isEqualTo(infos[1].handle); + assertThat(mTunerResourceManagerService.getFrontendResource(infos[0].handle) .isInUse()).isTrue(); - assertThat(mTunerResourceManagerService.getFrontendResource(infos[1].getHandle()) + assertThat(mTunerResourceManagerService.getFrontendResource(infos[1].handle) .isInUse()).isTrue(); - assertThat(mTunerResourceManagerService.getFrontendResource(infos[0].getHandle()) + assertThat(mTunerResourceManagerService.getFrontendResource(infos[0].handle) .getOwnerClientId()).isEqualTo(clientId1[0]); - assertThat(mTunerResourceManagerService.getFrontendResource(infos[1].getHandle()) + assertThat(mTunerResourceManagerService.getFrontendResource(infos[1].handle) .getOwnerClientId()).isEqualTo(clientId1[0]); assertThat(listener.isReclaimed()).isTrue(); } @@ -456,7 +461,7 @@ public class TunerResourceManagerServiceTest { public void releaseFrontendTest_UnderTheSameExclusiveGroup() { // Register clients ResourceClientProfile[] profiles = new ResourceClientProfile[1]; - profiles[0] = new ResourceClientProfile("0" /*sessionId*/, + profiles[0] = resourceClientProfile("0" /*sessionId*/, TvInputService.PRIORITY_HINT_USE_CASE_TYPE_PLAYBACK); int[] clientId = new int[1]; TestResourcesReclaimListener listener = new TestResourcesReclaimListener(); @@ -466,19 +471,19 @@ public class TunerResourceManagerServiceTest { // Init frontend resources. TunerFrontendInfo[] infos = new TunerFrontendInfo[2]; infos[0] = - new TunerFrontendInfo(0 /*id*/, FrontendSettings.TYPE_DVBT, 1 /*exclusiveGroupId*/); + tunerFrontendInfo(0 /*handle*/, FrontendSettings.TYPE_DVBT, 1 /*exclusiveGroupId*/); infos[1] = - new TunerFrontendInfo(1 /*id*/, FrontendSettings.TYPE_DVBS, 1 /*exclusiveGroupId*/); + tunerFrontendInfo(1 /*handle*/, FrontendSettings.TYPE_DVBS, 1 /*exclusiveGroupId*/); mTunerResourceManagerService.setFrontendInfoListInternal(infos); TunerFrontendRequest request = - new TunerFrontendRequest(clientId[0] /*clientId*/, FrontendSettings.TYPE_DVBT); + tunerFrontendRequest(clientId[0] /*clientId*/, FrontendSettings.TYPE_DVBT); int[] frontendHandle = new int[1]; assertThat(mTunerResourceManagerService .requestFrontendInternal(request, frontendHandle)).isTrue(); - assertThat(frontendHandle[0]).isEqualTo(infos[0].getHandle()); + assertThat(frontendHandle[0]).isEqualTo(infos[0].handle); assertThat(mTunerResourceManagerService - .getFrontendResource(infos[1].getHandle()).isInUse()).isTrue(); + .getFrontendResource(infos[1].handle).isInUse()).isTrue(); // Release frontend mTunerResourceManagerService.releaseFrontendInternal(mTunerResourceManagerService @@ -486,7 +491,7 @@ public class TunerResourceManagerServiceTest { assertThat(mTunerResourceManagerService .getFrontendResource(frontendHandle[0]).isInUse()).isFalse(); assertThat(mTunerResourceManagerService - .getFrontendResource(infos[1].getHandle()).isInUse()).isFalse(); + .getFrontendResource(infos[1].handle).isInUse()).isFalse(); assertThat(mTunerResourceManagerService .getClientProfile(clientId[0]).getInUseFrontendHandles().size()).isEqualTo(0); } @@ -495,9 +500,9 @@ public class TunerResourceManagerServiceTest { public void requestCasTest_NoCasAvailable_RequestWithHigherPriority() { // Register clients ResourceClientProfile[] profiles = new ResourceClientProfile[2]; - profiles[0] = new ResourceClientProfile("0" /*sessionId*/, + profiles[0] = resourceClientProfile("0" /*sessionId*/, TvInputService.PRIORITY_HINT_USE_CASE_TYPE_PLAYBACK); - profiles[1] = new ResourceClientProfile("1" /*sessionId*/, + profiles[1] = resourceClientProfile("1" /*sessionId*/, TvInputService.PRIORITY_HINT_USE_CASE_TYPE_PLAYBACK); int[] clientPriorities = {100, 500}; int[] clientId0 = new int[1]; @@ -517,7 +522,7 @@ public class TunerResourceManagerServiceTest { // Init cas resources. mTunerResourceManagerService.updateCasInfoInternal(1 /*casSystemId*/, 2 /*maxSessionNum*/); - CasSessionRequest request = new CasSessionRequest(clientId0[0], 1 /*casSystemId*/); + CasSessionRequest request = casSessionRequest(clientId0[0], 1 /*casSystemId*/); int[] casSessionHandle = new int[1]; // Request for 2 cas sessions. assertThat(mTunerResourceManagerService @@ -532,7 +537,7 @@ public class TunerResourceManagerServiceTest { .getOwnerClientIds()).isEqualTo(new HashSet<Integer>(Arrays.asList(clientId0[0]))); assertThat(mTunerResourceManagerService.getCasResource(1).isFullyUsed()).isTrue(); - request = new CasSessionRequest(clientId1[0], 1); + request = casSessionRequest(clientId1[0], 1); assertThat(mTunerResourceManagerService .requestCasSessionInternal(request, casSessionHandle)).isTrue(); assertThat(mTunerResourceManagerService.getResourceIdFromHandle(casSessionHandle[0])) @@ -548,10 +553,66 @@ public class TunerResourceManagerServiceTest { } @Test + public void requestCiCamTest_NoCiCamAvailable_RequestWithHigherPriority() { + // Register clients + ResourceClientProfile[] profiles = new ResourceClientProfile[2]; + profiles[0] = resourceClientProfile("0" /*sessionId*/, + TvInputService.PRIORITY_HINT_USE_CASE_TYPE_PLAYBACK); + profiles[1] = resourceClientProfile("1" /*sessionId*/, + TvInputService.PRIORITY_HINT_USE_CASE_TYPE_PLAYBACK); + int[] clientPriorities = {100, 500}; + int[] clientId0 = new int[1]; + int[] clientId1 = new int[1]; + TestResourcesReclaimListener listener = new TestResourcesReclaimListener(); + mTunerResourceManagerService.registerClientProfileInternal( + profiles[0], listener, clientId0); + assertThat(clientId0[0]).isNotEqualTo(TunerResourceManagerService.INVALID_CLIENT_ID); + mTunerResourceManagerService.getClientProfile(clientId0[0]) + .setPriority(clientPriorities[0]); + mTunerResourceManagerService.registerClientProfileInternal( + profiles[1], new TestResourcesReclaimListener(), clientId1); + assertThat(clientId1[0]).isNotEqualTo(TunerResourceManagerService.INVALID_CLIENT_ID); + mTunerResourceManagerService.getClientProfile(clientId1[0]) + .setPriority(clientPriorities[1]); + + // Init cicam/cas resources. + mTunerResourceManagerService.updateCasInfoInternal(1 /*casSystemId*/, 2 /*maxSessionNum*/); + + TunerCiCamRequest request = tunerCiCamRequest(clientId0[0], 1 /*ciCamId*/); + int[] ciCamHandle = new int[1]; + // Request for 2 ciCam sessions. + assertThat(mTunerResourceManagerService + .requestCiCamInternal(request, ciCamHandle)).isTrue(); + assertThat(mTunerResourceManagerService + .requestCiCamInternal(request, ciCamHandle)).isTrue(); + assertThat(mTunerResourceManagerService.getResourceIdFromHandle(ciCamHandle[0])) + .isEqualTo(1); + assertThat(mTunerResourceManagerService.getClientProfile(clientId0[0]) + .getInUseCiCamId()).isEqualTo(1); + assertThat(mTunerResourceManagerService.getCiCamResource(1) + .getOwnerClientIds()).isEqualTo(new HashSet<Integer>(Arrays.asList(clientId0[0]))); + assertThat(mTunerResourceManagerService.getCiCamResource(1).isFullyUsed()).isTrue(); + + request = tunerCiCamRequest(clientId1[0], 1); + assertThat(mTunerResourceManagerService + .requestCiCamInternal(request, ciCamHandle)).isTrue(); + assertThat(mTunerResourceManagerService.getResourceIdFromHandle(ciCamHandle[0])) + .isEqualTo(1); + assertThat(mTunerResourceManagerService.getClientProfile(clientId1[0]) + .getInUseCiCamId()).isEqualTo(1); + assertThat(mTunerResourceManagerService.getClientProfile(clientId0[0]) + .getInUseCiCamId()).isEqualTo(ClientProfile.INVALID_RESOURCE_ID); + assertThat(mTunerResourceManagerService.getCiCamResource(1) + .getOwnerClientIds()).isEqualTo(new HashSet<Integer>(Arrays.asList(clientId1[0]))); + assertThat(mTunerResourceManagerService.getCiCamResource(1).isFullyUsed()).isFalse(); + assertThat(listener.isReclaimed()).isTrue(); + } + + @Test public void releaseCasTest() { // Register clients ResourceClientProfile[] profiles = new ResourceClientProfile[1]; - profiles[0] = new ResourceClientProfile("0" /*sessionId*/, + profiles[0] = resourceClientProfile("0" /*sessionId*/, TvInputService.PRIORITY_HINT_USE_CASE_TYPE_PLAYBACK); int[] clientId = new int[1]; TestResourcesReclaimListener listener = new TestResourcesReclaimListener(); @@ -561,7 +622,7 @@ public class TunerResourceManagerServiceTest { // Init cas resources. mTunerResourceManagerService.updateCasInfoInternal(1 /*casSystemId*/, 2 /*maxSessionNum*/); - CasSessionRequest request = new CasSessionRequest(clientId[0], 1 /*casSystemId*/); + CasSessionRequest request = casSessionRequest(clientId[0], 1 /*casSystemId*/); int[] casSessionHandle = new int[1]; // Request for 1 cas sessions. assertThat(mTunerResourceManagerService @@ -585,12 +646,49 @@ public class TunerResourceManagerServiceTest { } @Test + public void releaseCiCamTest() { + // Register clients + ResourceClientProfile[] profiles = new ResourceClientProfile[1]; + profiles[0] = resourceClientProfile("0" /*sessionId*/, + TvInputService.PRIORITY_HINT_USE_CASE_TYPE_PLAYBACK); + int[] clientId = new int[1]; + TestResourcesReclaimListener listener = new TestResourcesReclaimListener(); + mTunerResourceManagerService.registerClientProfileInternal(profiles[0], listener, clientId); + assertThat(clientId[0]).isNotEqualTo(TunerResourceManagerService.INVALID_CLIENT_ID); + + // Init cas resources. + mTunerResourceManagerService.updateCasInfoInternal(1 /*casSystemId*/, 2 /*maxSessionNum*/); + + TunerCiCamRequest request = tunerCiCamRequest(clientId[0], 1 /*ciCamId*/); + int[] ciCamHandle = new int[1]; + // Request for 1 ciCam sessions. + assertThat(mTunerResourceManagerService + .requestCiCamInternal(request, ciCamHandle)).isTrue(); + assertThat(mTunerResourceManagerService.getResourceIdFromHandle(ciCamHandle[0])) + .isEqualTo(1); + assertThat(mTunerResourceManagerService.getClientProfile(clientId[0]) + .getInUseCiCamId()).isEqualTo(1); + assertThat(mTunerResourceManagerService.getCiCamResource(1) + .getOwnerClientIds()).isEqualTo(new HashSet<Integer>(Arrays.asList(clientId[0]))); + assertThat(mTunerResourceManagerService.getCiCamResource(1).isFullyUsed()).isFalse(); + + // Release ciCam + mTunerResourceManagerService.releaseCiCamInternal(mTunerResourceManagerService + .getCiCamResource(1), clientId[0]); + assertThat(mTunerResourceManagerService.getClientProfile(clientId[0]) + .getInUseCiCamId()).isEqualTo(ClientProfile.INVALID_RESOURCE_ID); + assertThat(mTunerResourceManagerService.getCiCamResource(1).isFullyUsed()).isFalse(); + assertThat(mTunerResourceManagerService.getCiCamResource(1) + .getOwnerClientIds()).isEmpty(); + } + + @Test public void requestLnbTest_NoLnbAvailable_RequestWithHigherPriority() { // Register clients ResourceClientProfile[] profiles = new ResourceClientProfile[2]; - profiles[0] = new ResourceClientProfile("0" /*sessionId*/, + profiles[0] = resourceClientProfile("0" /*sessionId*/, TvInputService.PRIORITY_HINT_USE_CASE_TYPE_PLAYBACK); - profiles[1] = new ResourceClientProfile("1" /*sessionId*/, + profiles[1] = resourceClientProfile("1" /*sessionId*/, TvInputService.PRIORITY_HINT_USE_CASE_TYPE_PLAYBACK); int[] clientPriorities = {100, 500}; int[] clientId0 = new int[1]; @@ -611,7 +709,8 @@ public class TunerResourceManagerServiceTest { int[] lnbHandles = {1}; mTunerResourceManagerService.setLnbInfoListInternal(lnbHandles); - TunerLnbRequest request = new TunerLnbRequest(clientId0[0]); + TunerLnbRequest request = new TunerLnbRequest(); + request.clientId = clientId0[0]; int[] lnbHandle = new int[1]; assertThat(mTunerResourceManagerService .requestLnbInternal(request, lnbHandle)).isTrue(); @@ -619,7 +718,9 @@ public class TunerResourceManagerServiceTest { assertThat(mTunerResourceManagerService.getClientProfile(clientId0[0]).getInUseLnbHandles()) .isEqualTo(new HashSet<Integer>(Arrays.asList(lnbHandles[0]))); - request = new TunerLnbRequest(clientId1[0]); + request = new TunerLnbRequest(); + request.clientId = clientId1[0]; + assertThat(mTunerResourceManagerService .requestLnbInternal(request, lnbHandle)).isTrue(); assertThat(lnbHandle[0]).isEqualTo(lnbHandles[0]); @@ -636,7 +737,7 @@ public class TunerResourceManagerServiceTest { public void releaseLnbTest() { // Register clients ResourceClientProfile[] profiles = new ResourceClientProfile[1]; - profiles[0] = new ResourceClientProfile("0" /*sessionId*/, + profiles[0] = resourceClientProfile("0" /*sessionId*/, TvInputService.PRIORITY_HINT_USE_CASE_TYPE_PLAYBACK); int[] clientId = new int[1]; TestResourcesReclaimListener listener = new TestResourcesReclaimListener(); @@ -647,7 +748,8 @@ public class TunerResourceManagerServiceTest { int[] lnbHandles = {0}; mTunerResourceManagerService.setLnbInfoListInternal(lnbHandles); - TunerLnbRequest request = new TunerLnbRequest(clientId[0]); + TunerLnbRequest request = new TunerLnbRequest(); + request.clientId = clientId[0]; int[] lnbHandle = new int[1]; assertThat(mTunerResourceManagerService .requestLnbInternal(request, lnbHandle)).isTrue(); @@ -665,7 +767,7 @@ public class TunerResourceManagerServiceTest { @Test public void unregisterClientTest_usingFrontend() { // Register client - ResourceClientProfile profile = new ResourceClientProfile("0" /*sessionId*/, + ResourceClientProfile profile = resourceClientProfile("0" /*sessionId*/, TvInputService.PRIORITY_HINT_USE_CASE_TYPE_PLAYBACK); int[] clientId = new int[1]; mTunerResourceManagerService.registerClientProfileInternal( @@ -675,27 +777,27 @@ public class TunerResourceManagerServiceTest { // Init frontend resources. TunerFrontendInfo[] infos = new TunerFrontendInfo[2]; infos[0] = - new TunerFrontendInfo(0 /*id*/, FrontendSettings.TYPE_DVBT, 1 /*exclusiveGroupId*/); + tunerFrontendInfo(0 /*handle*/, FrontendSettings.TYPE_DVBT, 1 /*exclusiveGroupId*/); infos[1] = - new TunerFrontendInfo(1 /*id*/, FrontendSettings.TYPE_DVBS, 1 /*exclusiveGroupId*/); + tunerFrontendInfo(1 /*handle*/, FrontendSettings.TYPE_DVBS, 1 /*exclusiveGroupId*/); mTunerResourceManagerService.setFrontendInfoListInternal(infos); TunerFrontendRequest request = - new TunerFrontendRequest(clientId[0] /*clientId*/, FrontendSettings.TYPE_DVBT); + tunerFrontendRequest(clientId[0] /*clientId*/, FrontendSettings.TYPE_DVBT); int[] frontendHandle = new int[1]; assertThat(mTunerResourceManagerService .requestFrontendInternal(request, frontendHandle)).isTrue(); - assertThat(frontendHandle[0]).isEqualTo(infos[0].getHandle()); - assertThat(mTunerResourceManagerService.getFrontendResource(infos[0].getHandle()) + assertThat(frontendHandle[0]).isEqualTo(infos[0].handle); + assertThat(mTunerResourceManagerService.getFrontendResource(infos[0].handle) .isInUse()).isTrue(); - assertThat(mTunerResourceManagerService.getFrontendResource(infos[1].getHandle()) + assertThat(mTunerResourceManagerService.getFrontendResource(infos[1].handle) .isInUse()).isTrue(); // Unregister client when using frontend mTunerResourceManagerService.unregisterClientProfileInternal(clientId[0]); - assertThat(mTunerResourceManagerService.getFrontendResource(infos[0].getHandle()) + assertThat(mTunerResourceManagerService.getFrontendResource(infos[0].handle) .isInUse()).isFalse(); - assertThat(mTunerResourceManagerService.getFrontendResource(infos[1].getHandle()) + assertThat(mTunerResourceManagerService.getFrontendResource(infos[1].handle) .isInUse()).isFalse(); assertThat(mTunerResourceManagerService.checkClientExists(clientId[0])).isFalse(); @@ -704,7 +806,7 @@ public class TunerResourceManagerServiceTest { @Test public void requestDemuxTest() { // Register client - ResourceClientProfile profile = new ResourceClientProfile("0" /*sessionId*/, + ResourceClientProfile profile = resourceClientProfile("0" /*sessionId*/, TvInputService.PRIORITY_HINT_USE_CASE_TYPE_PLAYBACK); int[] clientId = new int[1]; mTunerResourceManagerService.registerClientProfileInternal( @@ -712,7 +814,8 @@ public class TunerResourceManagerServiceTest { assertThat(clientId[0]).isNotEqualTo(TunerResourceManagerService.INVALID_CLIENT_ID); int[] demuxHandle = new int[1]; - TunerDemuxRequest request = new TunerDemuxRequest(clientId[0]); + TunerDemuxRequest request = new TunerDemuxRequest(); + request.clientId = clientId[0]; assertThat(mTunerResourceManagerService.requestDemuxInternal(request, demuxHandle)) .isTrue(); assertThat(mTunerResourceManagerService.getResourceIdFromHandle(demuxHandle[0])) @@ -722,7 +825,7 @@ public class TunerResourceManagerServiceTest { @Test public void requestDescramblerTest() { // Register client - ResourceClientProfile profile = new ResourceClientProfile("0" /*sessionId*/, + ResourceClientProfile profile = resourceClientProfile("0" /*sessionId*/, TvInputService.PRIORITY_HINT_USE_CASE_TYPE_PLAYBACK); int[] clientId = new int[1]; mTunerResourceManagerService.registerClientProfileInternal( @@ -730,7 +833,8 @@ public class TunerResourceManagerServiceTest { assertThat(clientId[0]).isNotEqualTo(TunerResourceManagerService.INVALID_CLIENT_ID); int[] desHandle = new int[1]; - TunerDescramblerRequest request = new TunerDescramblerRequest(clientId[0]); + TunerDescramblerRequest request = new TunerDescramblerRequest(); + request.clientId = clientId[0]; assertThat(mTunerResourceManagerService.requestDescramblerInternal(request, desHandle)) .isTrue(); assertThat(mTunerResourceManagerService.getResourceIdFromHandle(desHandle[0])).isEqualTo(0); @@ -740,15 +844,15 @@ public class TunerResourceManagerServiceTest { public void isHigherPriorityTest() { mIsForeground = false; ResourceClientProfile backgroundPlaybackProfile = - new ResourceClientProfile(null /*sessionId*/, + resourceClientProfile(null /*sessionId*/, TvInputService.PRIORITY_HINT_USE_CASE_TYPE_PLAYBACK); ResourceClientProfile backgroundRecordProfile = - new ResourceClientProfile(null /*sessionId*/, + resourceClientProfile(null /*sessionId*/, TvInputService.PRIORITY_HINT_USE_CASE_TYPE_RECORD); int backgroundPlaybackPriority = mTunerResourceManagerService.getClientPriority( - TvInputService.PRIORITY_HINT_USE_CASE_TYPE_PLAYBACK, 0); + TvInputService.PRIORITY_HINT_USE_CASE_TYPE_PLAYBACK, mIsForeground); int backgroundRecordPriority = mTunerResourceManagerService.getClientPriority( - TvInputService.PRIORITY_HINT_USE_CASE_TYPE_RECORD, 0); + TvInputService.PRIORITY_HINT_USE_CASE_TYPE_RECORD, mIsForeground); assertThat(mTunerResourceManagerService.isHigherPriorityInternal(backgroundPlaybackProfile, backgroundRecordProfile)).isEqualTo( (backgroundPlaybackPriority > backgroundRecordPriority)); @@ -767,16 +871,16 @@ public class TunerResourceManagerServiceTest { // Predefined client profiles ResourceClientProfile[] ownerProfiles = new ResourceClientProfile[2]; ResourceClientProfile[] shareProfiles = new ResourceClientProfile[2]; - ownerProfiles[0] = new ResourceClientProfile( + ownerProfiles[0] = resourceClientProfile( "0" /*sessionId*/, TvInputService.PRIORITY_HINT_USE_CASE_TYPE_LIVE); - ownerProfiles[1] = new ResourceClientProfile( + ownerProfiles[1] = resourceClientProfile( "1" /*sessionId*/, TvInputService.PRIORITY_HINT_USE_CASE_TYPE_LIVE); - shareProfiles[0] = new ResourceClientProfile( + shareProfiles[0] = resourceClientProfile( "2" /*sessionId*/, TvInputService.PRIORITY_HINT_USE_CASE_TYPE_RECORD); - shareProfiles[1] = new ResourceClientProfile( + shareProfiles[1] = resourceClientProfile( "3" /*sessionId*/, TvInputService.PRIORITY_HINT_USE_CASE_TYPE_RECORD); @@ -828,12 +932,12 @@ public class TunerResourceManagerServiceTest { // Predefined frontend info TunerFrontendInfo[] infos = new TunerFrontendInfo[2]; - infos[0] = new TunerFrontendInfo( - 0 /*id*/, + infos[0] = tunerFrontendInfo( + 0 /*handle*/, FrontendSettings.TYPE_DVBT, 1 /*exclusiveGroupId*/); - infos[1] = new TunerFrontendInfo( - 1 /*id*/, + infos[1] = tunerFrontendInfo( + 1 /*handle*/, FrontendSettings.TYPE_DVBS, 1 /*exclusiveGroupId*/); @@ -848,7 +952,7 @@ public class TunerResourceManagerServiceTest { // Predefined frontend request and array to save returned frontend handle int[] frontendHandle = new int[1]; - TunerFrontendRequest request = new TunerFrontendRequest( + TunerFrontendRequest request = tunerFrontendRequest( ownerClientId0[0] /*clientId*/, FrontendSettings.TYPE_DVBT); @@ -856,13 +960,13 @@ public class TunerResourceManagerServiceTest { assertThat(mTunerResourceManagerService .requestFrontendInternal(request, frontendHandle)) .isTrue(); - assertThat(frontendHandle[0]).isEqualTo(infos[0].getHandle()); + assertThat(frontendHandle[0]).isEqualTo(infos[0].handle); assertThat(mTunerResourceManagerService .getClientProfile(ownerClientId0[0]) .getInUseFrontendHandles()) .isEqualTo(new HashSet<Integer>(Arrays.asList( - infos[0].getHandle(), - infos[1].getHandle()))); + infos[0].handle, + infos[1].handle))); /**** Share Frontend ****/ @@ -874,14 +978,14 @@ public class TunerResourceManagerServiceTest { shareClientId1[0]/*selfClientId*/, ownerClientId0[0]/*targetClientId*/); // Verify fe in use status - assertThat(mTunerResourceManagerService.getFrontendResource(infos[0].getHandle()) + assertThat(mTunerResourceManagerService.getFrontendResource(infos[0].handle) .isInUse()).isTrue(); - assertThat(mTunerResourceManagerService.getFrontendResource(infos[1].getHandle()) + assertThat(mTunerResourceManagerService.getFrontendResource(infos[1].handle) .isInUse()).isTrue(); // Verify fe owner status - assertThat(mTunerResourceManagerService.getFrontendResource(infos[0].getHandle()) + assertThat(mTunerResourceManagerService.getFrontendResource(infos[0].handle) .getOwnerClientId()).isEqualTo(ownerClientId0[0]); - assertThat(mTunerResourceManagerService.getFrontendResource(infos[1].getHandle()) + assertThat(mTunerResourceManagerService.getFrontendResource(infos[1].handle) .getOwnerClientId()).isEqualTo(ownerClientId0[0]); // Verify share fe client status in the primary owner client assertThat(mTunerResourceManagerService.getClientProfile(ownerClientId0[0]) @@ -894,20 +998,20 @@ public class TunerResourceManagerServiceTest { .getClientProfile(ownerClientId0[0]) .getInUseFrontendHandles()) .isEqualTo(new HashSet<Integer>(Arrays.asList( - infos[0].getHandle(), - infos[1].getHandle()))); + infos[0].handle, + infos[1].handle))); assertThat(mTunerResourceManagerService .getClientProfile(shareClientId0[0]) .getInUseFrontendHandles()) .isEqualTo(new HashSet<Integer>(Arrays.asList( - infos[0].getHandle(), - infos[1].getHandle()))); + infos[0].handle, + infos[1].handle))); assertThat(mTunerResourceManagerService .getClientProfile(shareClientId1[0]) .getInUseFrontendHandles()) .isEqualTo(new HashSet<Integer>(Arrays.asList( - infos[0].getHandle(), - infos[1].getHandle()))); + infos[0].handle, + infos[1].handle))); /**** Remove Frontend Share Owner ****/ @@ -923,19 +1027,19 @@ public class TunerResourceManagerServiceTest { .getClientProfile(ownerClientId0[0]) .getInUseFrontendHandles()) .isEqualTo(new HashSet<Integer>(Arrays.asList( - infos[0].getHandle(), - infos[1].getHandle()))); + infos[0].handle, + infos[1].handle))); assertThat(mTunerResourceManagerService .getClientProfile(shareClientId0[0]) .getInUseFrontendHandles()) .isEqualTo(new HashSet<Integer>(Arrays.asList( - infos[0].getHandle(), - infos[1].getHandle()))); + infos[0].handle, + infos[1].handle))); /**** Request Shared Frontend with Higher Priority Client ****/ // Predefined second frontend request - request = new TunerFrontendRequest( + request = tunerFrontendRequest( ownerClientId1[0] /*clientId*/, FrontendSettings.TYPE_DVBT); @@ -945,17 +1049,17 @@ public class TunerResourceManagerServiceTest { .isTrue(); // Validate granted resource and internal mapping - assertThat(frontendHandle[0]).isEqualTo(infos[0].getHandle()); - assertThat(mTunerResourceManagerService.getFrontendResource(infos[0].getHandle()) + assertThat(frontendHandle[0]).isEqualTo(infos[0].handle); + assertThat(mTunerResourceManagerService.getFrontendResource(infos[0].handle) .getOwnerClientId()).isEqualTo(ownerClientId1[0]); - assertThat(mTunerResourceManagerService.getFrontendResource(infos[1].getHandle()) + assertThat(mTunerResourceManagerService.getFrontendResource(infos[1].handle) .getOwnerClientId()).isEqualTo(ownerClientId1[0]); assertThat(mTunerResourceManagerService .getClientProfile(ownerClientId1[0]) .getInUseFrontendHandles()) .isEqualTo(new HashSet<Integer>(Arrays.asList( - infos[0].getHandle(), - infos[1].getHandle()))); + infos[0].handle, + infos[1].handle))); assertThat(mTunerResourceManagerService .getClientProfile(ownerClientId0[0]) .getInUseFrontendHandles() @@ -983,12 +1087,12 @@ public class TunerResourceManagerServiceTest { // Release the frontend resource from the primary owner mTunerResourceManagerService.releaseFrontendInternal(mTunerResourceManagerService - .getFrontendResource(infos[0].getHandle()), ownerClientId1[0]); + .getFrontendResource(infos[0].handle), ownerClientId1[0]); // Validate the internal mapping - assertThat(mTunerResourceManagerService.getFrontendResource(infos[0].getHandle()) + assertThat(mTunerResourceManagerService.getFrontendResource(infos[0].handle) .isInUse()).isFalse(); - assertThat(mTunerResourceManagerService.getFrontendResource(infos[1].getHandle()) + assertThat(mTunerResourceManagerService.getFrontendResource(infos[1].handle) .isInUse()).isFalse(); // Verify client status assertThat(mTunerResourceManagerService @@ -1010,7 +1114,8 @@ public class TunerResourceManagerServiceTest { /**** Unregister Primary Owner when the Share owner owns an Lnb ****/ // Predefined Lnb request and handle array - TunerLnbRequest requestLnb = new TunerLnbRequest(shareClientId0[0]); + TunerLnbRequest requestLnb = new TunerLnbRequest(); + requestLnb.clientId = shareClientId0[0]; int[] lnbHandle = new int[1]; // Request for an Lnb @@ -1030,9 +1135,9 @@ public class TunerResourceManagerServiceTest { mTunerResourceManagerService.unregisterClientProfileInternal(ownerClientId1[0]); // Validate the internal mapping - assertThat(mTunerResourceManagerService.getFrontendResource(infos[0].getHandle()) + assertThat(mTunerResourceManagerService.getFrontendResource(infos[0].handle) .isInUse()).isFalse(); - assertThat(mTunerResourceManagerService.getFrontendResource(infos[1].getHandle()) + assertThat(mTunerResourceManagerService.getFrontendResource(infos[1].handle) .isInUse()).isFalse(); // Verify client status assertThat(mTunerResourceManagerService @@ -1046,4 +1151,41 @@ public class TunerResourceManagerServiceTest { .isEqualTo(new HashSet<Integer>(Arrays.asList( lnbHandles[0]))); } + + private TunerFrontendInfo tunerFrontendInfo( + int handle, int frontendType, int exclusiveGroupId) { + TunerFrontendInfo info = new TunerFrontendInfo(); + info.handle = handle; + info.frontendType = frontendType; + info.exclusiveGroupId = exclusiveGroupId; + return info; + } + + private TunerFrontendRequest tunerFrontendRequest(int clientId, int frontendType) { + TunerFrontendRequest request = new TunerFrontendRequest(); + request.clientId = clientId; + request.frontendType = frontendType; + return request; + } + + private ResourceClientProfile resourceClientProfile(String sessionId, int useCase) { + ResourceClientProfile profile = new ResourceClientProfile(); + profile.tvInputSessionId = sessionId; + profile.useCase = useCase; + return profile; + } + + private CasSessionRequest casSessionRequest(int clientId, int casSystemId) { + CasSessionRequest request = new CasSessionRequest(); + request.clientId = clientId; + request.casSystemId = casSystemId; + return request; + } + + private TunerCiCamRequest tunerCiCamRequest(int clientId, int ciCamId) { + TunerCiCamRequest request = new TunerCiCamRequest(); + request.clientId = clientId; + request.ciCamId = ciCamId; + return request; + } } diff --git a/services/tests/wmtests/src/com/android/server/wm/ActivityStackTests.java b/services/tests/wmtests/src/com/android/server/wm/ActivityStackTests.java index 21aa6bf8750a..fc96b69a568b 100644 --- a/services/tests/wmtests/src/com/android/server/wm/ActivityStackTests.java +++ b/services/tests/wmtests/src/com/android/server/wm/ActivityStackTests.java @@ -156,8 +156,10 @@ public class ActivityStackTests extends WindowTestsBase { organizer.setMoveToSecondaryOnEnter(false); // Create primary splitscreen stack. - final Task primarySplitScreen = mDefaultTaskDisplayArea.createRootTask( - WINDOWING_MODE_SPLIT_SCREEN_PRIMARY, ACTIVITY_TYPE_STANDARD, true /* onTop */); + final Task primarySplitScreen = new TaskBuilder(mAtm.mTaskSupervisor) + .setParentTask(organizer.mPrimary) + .setOnTop(true) + .build(); // Assert windowing mode. assertEquals(WINDOWING_MODE_SPLIT_SCREEN_PRIMARY, primarySplitScreen.getWindowingMode()); @@ -205,14 +207,15 @@ public class ActivityStackTests extends WindowTestsBase { @Test public void testSplitScreenMoveToBack() { TestSplitOrganizer organizer = new TestSplitOrganizer(mAtm); - // Set up split-screen with primary on top and secondary containing the home task below - // another stack. + // Explicitly reparent task to primary split root to enter split mode, in which implies + // primary on top and secondary containing the home task below another stack. final Task primaryTask = mDefaultTaskDisplayArea.createRootTask( - WINDOWING_MODE_SPLIT_SCREEN_PRIMARY, ACTIVITY_TYPE_STANDARD, true /* onTop */); + WINDOWING_MODE_UNDEFINED, ACTIVITY_TYPE_STANDARD, true /* onTop */); + final Task secondaryTask = mDefaultTaskDisplayArea.createRootTask( + WINDOWING_MODE_UNDEFINED, ACTIVITY_TYPE_STANDARD, true /* onTop */); final Task homeRoot = mDefaultTaskDisplayArea.getRootTask( WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_HOME); - final Task secondaryTask = mDefaultTaskDisplayArea.createRootTask( - WINDOWING_MODE_SPLIT_SCREEN_SECONDARY, ACTIVITY_TYPE_STANDARD, true /* onTop */); + primaryTask.reparent(organizer.mPrimary, POSITION_TOP); mDefaultTaskDisplayArea.positionChildAt(POSITION_TOP, organizer.mPrimary, false /* includingParents */); diff --git a/services/tests/wmtests/src/com/android/server/wm/ActivityStarterTests.java b/services/tests/wmtests/src/com/android/server/wm/ActivityStarterTests.java index e8045e06d9ff..4bfc83742c04 100644 --- a/services/tests/wmtests/src/com/android/server/wm/ActivityStarterTests.java +++ b/services/tests/wmtests/src/com/android/server/wm/ActivityStarterTests.java @@ -485,8 +485,10 @@ public class ActivityStarterTests extends WindowTestsBase { final ActivityRecord splitSecondActivity = new ActivityBuilder(mAtm).setCreateTask(true).build(); final ActivityRecord splitPrimaryActivity = new TaskBuilder(mSupervisor) - .setWindowingMode(WINDOWING_MODE_SPLIT_SCREEN_PRIMARY).setCreateActivity(true) - .build().getTopMostActivity(); + .setParentTask(splitOrg.mPrimary) + .setCreateActivity(true) + .build() + .getTopMostActivity(); splitPrimaryActivity.mVisibleRequested = splitSecondActivity.mVisibleRequested = true; assertEquals(splitOrg.mPrimary, splitPrimaryActivity.getRootTask()); @@ -720,6 +722,7 @@ public class ActivityStarterTests extends WindowTestsBase { } // caller is instrumenting with background activity starts privileges callerApp.setInstrumenting(callerIsInstrumentingWithBackgroundActivityStartPrivileges, + callerIsInstrumentingWithBackgroundActivityStartPrivileges ? Process.SHELL_UID : -1, callerIsInstrumentingWithBackgroundActivityStartPrivileges); // callingUid is the device owner doReturn(isCallingUidDeviceOwner).when(mAtm).isDeviceOwner(callingUid); diff --git a/services/tests/wmtests/src/com/android/server/wm/WindowOrganizerTests.java b/services/tests/wmtests/src/com/android/server/wm/WindowOrganizerTests.java index fe7bdd8f620a..d8be2c1a21a7 100644 --- a/services/tests/wmtests/src/com/android/server/wm/WindowOrganizerTests.java +++ b/services/tests/wmtests/src/com/android/server/wm/WindowOrganizerTests.java @@ -80,6 +80,8 @@ import android.window.WindowContainerTransaction; import androidx.test.filters.SmallTest; +import com.android.server.wm.TaskOrganizerController.PendingTaskEvent; + import org.junit.Before; import org.junit.Test; import org.junit.runner.RunWith; @@ -150,10 +152,14 @@ public class WindowOrganizerTests extends WindowTestsBase { final ITaskOrganizer organizer = registerMockOrganizer(); final Task stack = createStack(); final Task task = createTask(stack); + // Ensure events dispatch to organizer. + mWm.mAtmService.mTaskOrganizerController.dispatchPendingEvents(); verify(organizer).onTaskAppeared(any(RunningTaskInfo.class), any(SurfaceControl.class)); stack.removeImmediately(); + // Ensure events dispatch to organizer. + mWm.mAtmService.mTaskOrganizerController.dispatchPendingEvents(); verify(organizer).onTaskVanished(any()); } @@ -162,15 +168,21 @@ public class WindowOrganizerTests extends WindowTestsBase { final ITaskOrganizer organizer = registerMockOrganizer(); final Task stack = createStack(); final Task task = createTask(stack, false); + // Ensure events dispatch to organizer. + mWm.mAtmService.mTaskOrganizerController.dispatchPendingEvents(); verify(organizer, never()) .onTaskAppeared(any(RunningTaskInfo.class), any(SurfaceControl.class)); stack.setHasBeenVisible(true); + // Ensure events dispatch to organizer. + mWm.mAtmService.mTaskOrganizerController.dispatchPendingEvents(); assertTrue(stack.getHasBeenVisible()); verify(organizer).onTaskAppeared(any(RunningTaskInfo.class), any(SurfaceControl.class)); stack.removeImmediately(); + // Ensure events dispatch to organizer. + mWm.mAtmService.mTaskOrganizerController.dispatchPendingEvents(); verify(organizer).onTaskVanished(any()); } @@ -195,12 +207,16 @@ public class WindowOrganizerTests extends WindowTestsBase { final ITaskOrganizer organizer = registerMockOrganizer(); final Task stack = createStack(); final Task task = createTask(stack, false /* fakeDraw */); + // Ensure events dispatch to organizer. + mWm.mAtmService.mTaskOrganizerController.dispatchPendingEvents(); verify(organizer, never()) .onTaskAppeared(any(RunningTaskInfo.class), any(SurfaceControl.class)); assertTrue(stack.isOrganized()); mWm.mAtmService.mTaskOrganizerController.unregisterTaskOrganizer(organizer); + // Ensure events dispatch to organizer. + mWm.mAtmService.mTaskOrganizerController.dispatchPendingEvents(); assertTaskVanished(organizer, false /* expectVanished */, stack); assertFalse(stack.isOrganized()); } @@ -210,11 +226,16 @@ public class WindowOrganizerTests extends WindowTestsBase { final ITaskOrganizer organizer = registerMockOrganizer(); final Task stack = createStack(); final Task task = createTask(stack); + // Ensure events dispatch to organizer. + mWm.mAtmService.mTaskOrganizerController.dispatchPendingEvents(); verify(organizer).onTaskAppeared(any(RunningTaskInfo.class), any(SurfaceControl.class)); assertTrue(stack.isOrganized()); stack.setTaskOrganizer(null); + // Ensure events dispatch to organizer. + mWm.mAtmService.mTaskOrganizerController.dispatchPendingEvents(); + verify(organizer).onTaskVanished(any()); assertFalse(stack.isOrganized()); } @@ -224,11 +245,16 @@ public class WindowOrganizerTests extends WindowTestsBase { final ITaskOrganizer organizer = registerMockOrganizer(); final Task stack = createStack(); final Task task = createTask(stack); + // Ensure events dispatch to organizer. + mWm.mAtmService.mTaskOrganizerController.dispatchPendingEvents(); verify(organizer).onTaskAppeared(any(RunningTaskInfo.class), any(SurfaceControl.class)); assertTrue(stack.isOrganized()); mWm.mAtmService.mTaskOrganizerController.unregisterTaskOrganizer(organizer); + // Ensure events dispatch to organizer. + mWm.mAtmService.mTaskOrganizerController.dispatchPendingEvents(); + assertTaskVanished(organizer, true /* expectVanished */, stack); assertFalse(stack.isOrganized()); } @@ -243,6 +269,8 @@ public class WindowOrganizerTests extends WindowTestsBase { final Task task3 = createTask(stack3); final ArrayList<TaskAppearedInfo> existingTasks = new ArrayList<>(); final ITaskOrganizer organizer = registerMockOrganizer(existingTasks); + // Ensure events dispatch to organizer. + mWm.mAtmService.mTaskOrganizerController.dispatchPendingEvents(); // verify that tasks are returned and taskAppeared is not called assertContainsTasks(existingTasks, stack, stack2, stack3); @@ -254,6 +282,8 @@ public class WindowOrganizerTests extends WindowTestsBase { // Now we replace the registration and verify the new organizer receives existing tasks final ArrayList<TaskAppearedInfo> existingTasks2 = new ArrayList<>(); final ITaskOrganizer organizer2 = registerMockOrganizer(existingTasks2); + // Ensure events dispatch to organizer. + mWm.mAtmService.mTaskOrganizerController.dispatchPendingEvents(); assertContainsTasks(existingTasks2, stack, stack2, stack3); verify(organizer2, times(0)).onTaskAppeared(any(RunningTaskInfo.class), any(SurfaceControl.class)); @@ -265,6 +295,8 @@ public class WindowOrganizerTests extends WindowTestsBase { // Now we unregister the second one, the first one should automatically be reregistered // so we verify that it's now seeing changes. mWm.mAtmService.mTaskOrganizerController.unregisterTaskOrganizer(organizer2); + // Ensure events dispatch to organizer. + mWm.mAtmService.mTaskOrganizerController.dispatchPendingEvents(); verify(organizer, times(3)) .onTaskAppeared(any(RunningTaskInfo.class), any(SurfaceControl.class)); assertTaskVanished(organizer2, true /* expectVanished */, stack, stack2, stack3); @@ -599,6 +631,8 @@ public class WindowOrganizerTests extends WindowTestsBase { Task task = mWm.mAtmService.mTaskOrganizerController.createRootTask( mDisplayContent, WINDOWING_MODE_SPLIT_SCREEN_SECONDARY, null); RunningTaskInfo info1 = task.getTaskInfo(); + // Ensure events dispatch to organizer. + mWm.mAtmService.mTaskOrganizerController.dispatchPendingEvents(); lastReportedTiles.clear(); called[0] = false; @@ -673,6 +707,8 @@ public class WindowOrganizerTests extends WindowTestsBase { Task task2 = mWm.mAtmService.mTaskOrganizerController.createRootTask( mDisplayContent, WINDOWING_MODE_SPLIT_SCREEN_SECONDARY, null); RunningTaskInfo info2 = task2.getTaskInfo(); + // Ensure events dispatch to organizer. + mWm.mAtmService.mTaskOrganizerController.dispatchPendingEvents(); final int initialRootTaskCount = mWm.mAtmService.mTaskOrganizerController.getRootTasks( mDisplayContent.mDisplayId, null /* activityTypes */).size(); @@ -856,6 +892,8 @@ public class WindowOrganizerTests extends WindowTestsBase { .setAspectRatio(new Rational(3, 4)).build(); mWm.mAtmService.mActivityClientController.setPictureInPictureParams(record.token, p2); waitUntilHandlersIdle(); + // Ensure events dispatch to organizer. + mWm.mAtmService.mTaskOrganizerController.dispatchPendingEvents(); assertNotNull(o.mChangedInfo); assertNotNull(o.mChangedInfo.pictureInPictureParams); final Rational ratio = o.mChangedInfo.pictureInPictureParams.getAspectRatioRational(); @@ -895,16 +933,24 @@ public class WindowOrganizerTests extends WindowTestsBase { stack.setTaskOrganizer(organizer); // setHasBeenVisible was already called once by the set-up code. stack.setHasBeenVisible(true); + // Ensure events dispatch to organizer. + mWm.mAtmService.mTaskOrganizerController.dispatchPendingEvents(); verify(organizer, times(1)) .onTaskAppeared(any(RunningTaskInfo.class), any(SurfaceControl.class)); stack.setTaskOrganizer(null); + // Ensure events dispatch to organizer. + mWm.mAtmService.mTaskOrganizerController.dispatchPendingEvents(); verify(organizer, times(1)).onTaskVanished(any()); stack.setTaskOrganizer(organizer); + // Ensure events dispatch to organizer. + mWm.mAtmService.mTaskOrganizerController.dispatchPendingEvents(); verify(organizer, times(2)) .onTaskAppeared(any(RunningTaskInfo.class), any(SurfaceControl.class)); stack.removeImmediately(); + // Ensure events dispatch to organizer. + mWm.mAtmService.mTaskOrganizerController.dispatchPendingEvents(); verify(organizer, times(2)).onTaskVanished(any()); } @@ -923,6 +969,8 @@ public class WindowOrganizerTests extends WindowTestsBase { // Verify a back pressed does not call the organizer mWm.mAtmService.mActivityClientController.onBackPressedOnTaskRoot(activity.token); + // Ensure events dispatch to organizer. + mWm.mAtmService.mTaskOrganizerController.dispatchPendingEvents(); verify(organizer, never()).onBackPressedOnTaskRoot(any()); // Enable intercepting back @@ -931,6 +979,8 @@ public class WindowOrganizerTests extends WindowTestsBase { // Verify now that the back press does call the organizer mWm.mAtmService.mActivityClientController.onBackPressedOnTaskRoot(activity.token); + // Ensure events dispatch to organizer. + mWm.mAtmService.mTaskOrganizerController.dispatchPendingEvents(); verify(organizer, times(1)).onBackPressedOnTaskRoot(any()); // Disable intercepting back @@ -939,6 +989,8 @@ public class WindowOrganizerTests extends WindowTestsBase { // Verify now that the back press no longer calls the organizer mWm.mAtmService.mActivityClientController.onBackPressedOnTaskRoot(activity.token); + // Ensure events dispatch to organizer. + mWm.mAtmService.mTaskOrganizerController.dispatchPendingEvents(); verify(organizer, times(1)).onBackPressedOnTaskRoot(any()); } @@ -1019,6 +1071,151 @@ public class WindowOrganizerTests extends WindowTestsBase { assertTrue(task2.isOrganized()); } + @Test + public void testAppearDeferThenInfoChange() { + final ITaskOrganizer organizer = registerMockOrganizer(); + final Task stack = createStack(); + + // Assume layout defer + mWm.mWindowPlacerLocked.deferLayout(); + + final Task task = createTask(stack); + final ActivityRecord record = createActivityRecord(stack.mDisplayContent, task); + + stack.setWindowingMode(WINDOWING_MODE_MULTI_WINDOW); + record.setTaskDescription(new ActivityManager.TaskDescription("TestDescription")); + waitUntilHandlersIdle(); + + ArrayList<PendingTaskEvent> pendingEvents = getTaskPendingEvent(stack); + assertEquals(1, pendingEvents.size()); + assertEquals(PendingTaskEvent.EVENT_APPEARED, pendingEvents.get(0).mEventType); + assertEquals("TestDescription", + pendingEvents.get(0).mTask.getTaskInfo().taskDescription.getLabel()); + } + + @Test + public void testAppearDeferThenVanish() { + final ITaskOrganizer organizer = registerMockOrganizer(); + final Task stack = createStack(); + + // Assume layout defer + mWm.mWindowPlacerLocked.deferLayout(); + + final Task task = createTask(stack); + + stack.removeImmediately(); + waitUntilHandlersIdle(); + + ArrayList<PendingTaskEvent> pendingEvents = getTaskPendingEvent(stack); + assertEquals(0, pendingEvents.size()); + } + + @Test + public void testInfoChangeDeferMultiple() { + final ITaskOrganizer organizer = registerMockOrganizer(); + final Task stack = createStack(); + final Task task = createTask(stack); + final ActivityRecord record = createActivityRecord(stack.mDisplayContent, task); + + // Assume layout defer + mWm.mWindowPlacerLocked.deferLayout(); + + stack.setWindowingMode(WINDOWING_MODE_MULTI_WINDOW); + record.setTaskDescription(new ActivityManager.TaskDescription("TestDescription")); + waitUntilHandlersIdle(); + + ArrayList<PendingTaskEvent> pendingEvents = getTaskPendingEvent(stack); + assertEquals(1, pendingEvents.size()); + assertEquals(PendingTaskEvent.EVENT_INFO_CHANGED, pendingEvents.get(0).mEventType); + assertEquals("TestDescription", + pendingEvents.get(0).mTask.getTaskInfo().taskDescription.getLabel()); + + record.setTaskDescription(new ActivityManager.TaskDescription("TestDescription2")); + waitUntilHandlersIdle(); + + pendingEvents = getTaskPendingEvent(stack); + assertEquals(1, pendingEvents.size()); + assertEquals(PendingTaskEvent.EVENT_INFO_CHANGED, pendingEvents.get(0).mEventType); + assertEquals("TestDescription2", + pendingEvents.get(0).mTask.getTaskInfo().taskDescription.getLabel()); + } + + @Test + public void testInfoChangDeferThenVanish() { + final ITaskOrganizer organizer = registerMockOrganizer(); + final Task stack = createStack(); + final Task task = createTask(stack); + final ActivityRecord record = createActivityRecord(stack.mDisplayContent, task); + + // Assume layout defer + mWm.mWindowPlacerLocked.deferLayout(); + + stack.setWindowingMode(WINDOWING_MODE_MULTI_WINDOW); + record.setTaskDescription(new ActivityManager.TaskDescription("TestDescription")); + + stack.removeImmediately(); + waitUntilHandlersIdle(); + + ArrayList<PendingTaskEvent> pendingEvents = getTaskPendingEvent(stack); + assertEquals(1, pendingEvents.size()); + assertEquals(PendingTaskEvent.EVENT_VANISHED, pendingEvents.get(0).mEventType); + assertEquals("TestDescription", + pendingEvents.get(0).mTask.getTaskInfo().taskDescription.getLabel()); + } + + @Test + public void testVanishDeferThenInfoChange() { + final ITaskOrganizer organizer = registerMockOrganizer(); + final Task stack = createStack(); + final Task task = createTask(stack); + final ActivityRecord record = createActivityRecord(stack.mDisplayContent, task); + + // Assume layout defer + mWm.mWindowPlacerLocked.deferLayout(); + + stack.removeImmediately(); + stack.setWindowingMode(WINDOWING_MODE_MULTI_WINDOW); + waitUntilHandlersIdle(); + + ArrayList<PendingTaskEvent> pendingEvents = getTaskPendingEvent(stack); + assertEquals(1, pendingEvents.size()); + assertEquals(PendingTaskEvent.EVENT_VANISHED, pendingEvents.get(0).mEventType); + } + + @Test + public void testVanishDeferThenBackOnRoot() { + final ITaskOrganizer organizer = registerMockOrganizer(); + final Task stack = createStack(); + final Task task = createTask(stack); + final ActivityRecord record = createActivityRecord(stack.mDisplayContent, task); + + // Assume layout defer + mWm.mWindowPlacerLocked.deferLayout(); + + stack.removeImmediately(); + mWm.mAtmService.mActivityClientController.onBackPressedOnTaskRoot(record.token); + waitUntilHandlersIdle(); + + ArrayList<PendingTaskEvent> pendingEvents = getTaskPendingEvent(stack); + assertEquals(1, pendingEvents.size()); + assertEquals(PendingTaskEvent.EVENT_VANISHED, pendingEvents.get(0).mEventType); + } + + private ArrayList<PendingTaskEvent> getTaskPendingEvent(Task task) { + ArrayList<PendingTaskEvent> total = + mWm.mAtmService.mTaskOrganizerController.getPendingEventList(); + ArrayList<PendingTaskEvent> result = new ArrayList(); + + for (int i = 0; i < total.size(); i++) { + PendingTaskEvent entry = total.get(i); + if (entry.mTask.mTaskId == task.mTaskId) { + result.add(entry); + } + } + + return result; + } + /** * Verifies that task vanished is called for a specific task. */ 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 31e2dce008de..8b93372e5a76 100644 --- a/services/tests/wmtests/src/com/android/server/wm/WindowTestsBase.java +++ b/services/tests/wmtests/src/com/android/server/wm/WindowTestsBase.java @@ -17,6 +17,8 @@ package com.android.server.wm; import static android.app.AppOpsManager.OP_NONE; +import static android.app.WindowConfiguration.ACTIVITY_TYPE_HOME; +import static android.app.WindowConfiguration.ACTIVITY_TYPE_RECENTS; import static android.app.WindowConfiguration.ACTIVITY_TYPE_STANDARD; import static android.app.WindowConfiguration.ACTIVITY_TYPE_UNDEFINED; import static android.app.WindowConfiguration.WINDOWING_MODE_FULLSCREEN; @@ -1107,6 +1109,17 @@ class WindowTestsBase extends SystemServiceTestsBase { // moves everything to secondary. Most tests expect this since sysui usually does it. boolean mMoveToSecondaryOnEnter = true; int mDisplayId; + private static final int[] CONTROLLED_ACTIVITY_TYPES = { + ACTIVITY_TYPE_STANDARD, + ACTIVITY_TYPE_HOME, + ACTIVITY_TYPE_RECENTS, + ACTIVITY_TYPE_UNDEFINED + }; + private static final int[] CONTROLLED_WINDOWING_MODES = { + WINDOWING_MODE_FULLSCREEN, + WINDOWING_MODE_SPLIT_SCREEN_SECONDARY, + WINDOWING_MODE_UNDEFINED + }; TestSplitOrganizer(ActivityTaskManagerService service, DisplayContent display) { mService = service; mDisplayId = display.mDisplayId; @@ -1151,9 +1164,9 @@ class WindowTestsBase extends SystemServiceTestsBase { if (!mMoveToSecondaryOnEnter) { return; } - mService.mTaskOrganizerController.setLaunchRoot(mDisplayId, - mSecondary.mRemoteToken.toWindowContainerToken()); DisplayContent dc = mService.mRootWindowContainer.getDisplayContent(mDisplayId); + dc.getDefaultTaskDisplayArea().setLaunchRootTask( + mSecondary, CONTROLLED_WINDOWING_MODES, CONTROLLED_ACTIVITY_TYPES); dc.forAllRootTasks(rootTask -> { if (!WindowConfiguration.isSplitScreenWindowingMode(rootTask.getWindowingMode())) { rootTask.reparent(mSecondary, POSITION_BOTTOM); diff --git a/services/translation/Android.bp b/services/translation/Android.bp new file mode 100644 index 000000000000..804a6177e94e --- /dev/null +++ b/services/translation/Android.bp @@ -0,0 +1,13 @@ +filegroup { + name: "services.translation-sources", + srcs: ["java/**/*.java"], + path: "java", + visibility: ["//frameworks/base/services"], +} + +java_library_static { + name: "services.translation", + defaults: ["platform_service_defaults"], + srcs: [":services.translation-sources"], + libs: ["services.core"], +}
\ No newline at end of file diff --git a/services/translation/OWNERS b/services/translation/OWNERS new file mode 100644 index 000000000000..a1e663aa8ff7 --- /dev/null +++ b/services/translation/OWNERS @@ -0,0 +1,8 @@ +# Bug component: 994311 + +adamhe@google.com +augale@google.com +joannechung@google.com +lpeter@google.com +svetoslavganov@google.com +tymtsai@google.com diff --git a/services/translation/java/com/android/server/translation/RemoteTranslationService.java b/services/translation/java/com/android/server/translation/RemoteTranslationService.java new file mode 100644 index 000000000000..0c7e61765501 --- /dev/null +++ b/services/translation/java/com/android/server/translation/RemoteTranslationService.java @@ -0,0 +1,87 @@ +/* + * Copyright (C) 2020 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.translation; + +import android.annotation.NonNull; +import android.content.ComponentName; +import android.content.Context; +import android.content.Intent; +import android.service.translation.ITranslationService; +import android.service.translation.TranslationService; +import android.util.Slog; +import android.view.translation.TranslationSpec; + +import com.android.internal.infra.AbstractRemoteService; +import com.android.internal.infra.ServiceConnector; +import com.android.internal.os.IResultReceiver; + +final class RemoteTranslationService extends ServiceConnector.Impl<ITranslationService> { + + private static final String TAG = RemoteTranslationService.class.getSimpleName(); + + // TODO(b/176590870): Make PERMANENT now. + private static final long TIMEOUT_IDLE_UNBIND_MS = + AbstractRemoteService.PERMANENT_BOUND_TIMEOUT_MS; + private static final int TIMEOUT_REQUEST_MS = 5_000; + + private final long mIdleUnbindTimeoutMs; + private final int mRequestTimeoutMs; + private final ComponentName mComponentName; + + RemoteTranslationService(Context context, ComponentName serviceName, + int userId, boolean bindInstantServiceAllowed) { + super(context, + new Intent(TranslationService.SERVICE_INTERFACE).setComponent(serviceName), + bindInstantServiceAllowed ? Context.BIND_ALLOW_INSTANT : 0, + userId, ITranslationService.Stub::asInterface); + mIdleUnbindTimeoutMs = TIMEOUT_IDLE_UNBIND_MS; + mRequestTimeoutMs = TIMEOUT_REQUEST_MS; + mComponentName = serviceName; + + // Bind right away. + connect(); + } + + public ComponentName getComponentName() { + return mComponentName; + } + + @Override // from ServiceConnector.Impl + protected void onServiceConnectionStatusChanged(ITranslationService service, + boolean connected) { + try { + if (connected) { + service.onConnected(); + } else { + service.onDisconnected(); + } + } catch (Exception e) { + Slog.w(TAG, + "Exception calling onServiceConnectionStatusChanged(" + connected + "): ", e); + } + } + + @Override // from AbstractRemoteService + protected long getAutoDisconnectTimeoutMs() { + return mIdleUnbindTimeoutMs; + } + + public void onSessionCreated(@NonNull TranslationSpec sourceSpec, + @NonNull TranslationSpec destSpec, int sessionId, IResultReceiver resultReceiver) { + run((s) -> s.onCreateTranslationSession(sourceSpec, destSpec, sessionId, resultReceiver)); + } +} diff --git a/services/translation/java/com/android/server/translation/TranslationManagerService.java b/services/translation/java/com/android/server/translation/TranslationManagerService.java new file mode 100644 index 000000000000..e2aabe6a89ea --- /dev/null +++ b/services/translation/java/com/android/server/translation/TranslationManagerService.java @@ -0,0 +1,92 @@ +/* + * Copyright (C) 2020 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.translation; + +import static android.content.Context.TRANSLATION_MANAGER_SERVICE; +import static android.view.translation.TranslationManager.STATUS_SYNC_CALL_FAIL; + +import android.content.Context; +import android.os.RemoteException; +import android.util.Slog; +import android.view.translation.ITranslationManager; +import android.view.translation.TranslationSpec; + +import com.android.internal.os.IResultReceiver; +import com.android.server.infra.AbstractMasterSystemService; +import com.android.server.infra.FrameworkResourcesServiceNameResolver; + +/** + * Entry point service for translation management. + * + * <p>This service provides the {@link ITranslationManager} implementation and keeps a list of + * {@link TranslationManagerServiceImpl} per user; the real work is done by + * {@link TranslationManagerServiceImpl} itself. + */ +public final class TranslationManagerService + extends AbstractMasterSystemService<TranslationManagerService, + TranslationManagerServiceImpl> { + + private static final String TAG = "TranslationManagerService"; + + public TranslationManagerService(Context context) { + // TODO: Discuss the disallow policy + super(context, new FrameworkResourcesServiceNameResolver(context, + com.android.internal.R.string.config_defaultTranslationService), + /* disallowProperty */ null, PACKAGE_UPDATE_POLICY_REFRESH_EAGER); + } + + @Override + protected TranslationManagerServiceImpl newServiceLocked(int resolvedUserId, boolean disabled) { + return new TranslationManagerServiceImpl(this, mLock, resolvedUserId, disabled); + } + + final class TranslationManagerServiceStub extends ITranslationManager.Stub { + @Override + public void getSupportedLocales(IResultReceiver receiver, int userId) + throws RemoteException { + synchronized (mLock) { + final TranslationManagerServiceImpl service = getServiceForUserLocked(userId); + if (service != null) { + service.getSupportedLocalesLocked(receiver); + } else { + Slog.v(TAG, "getSupportedLocales(): no service for " + userId); + receiver.send(STATUS_SYNC_CALL_FAIL, null); + } + } + } + + @Override + public void onSessionCreated(TranslationSpec sourceSpec, TranslationSpec destSpec, + int sessionId, IResultReceiver receiver, int userId) throws RemoteException { + synchronized (mLock) { + final TranslationManagerServiceImpl service = getServiceForUserLocked(userId); + if (service != null) { + service.onSessionCreatedLocked(sourceSpec, destSpec, sessionId, receiver); + } else { + Slog.v(TAG, "onSessionCreated(): no service for " + userId); + receiver.send(STATUS_SYNC_CALL_FAIL, null); + } + } + } + } + + @Override // from SystemService + public void onStart() { + publishBinderService(TRANSLATION_MANAGER_SERVICE, + new TranslationManagerService.TranslationManagerServiceStub()); + } +} diff --git a/services/translation/java/com/android/server/translation/TranslationManagerServiceImpl.java b/services/translation/java/com/android/server/translation/TranslationManagerServiceImpl.java new file mode 100644 index 000000000000..b1f6f80d4158 --- /dev/null +++ b/services/translation/java/com/android/server/translation/TranslationManagerServiceImpl.java @@ -0,0 +1,125 @@ +/* + * Copyright (C) 2020 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.translation; + +import static android.view.translation.TranslationManager.STATUS_SYNC_CALL_SUCCESS; + +import android.annotation.NonNull; +import android.annotation.Nullable; +import android.content.ComponentName; +import android.content.pm.PackageManager; +import android.content.pm.ServiceInfo; +import android.os.RemoteException; +import android.service.translation.TranslationServiceInfo; +import android.util.Slog; +import android.view.translation.TranslationSpec; + +import com.android.internal.annotations.GuardedBy; +import com.android.internal.os.IResultReceiver; +import com.android.internal.util.SyncResultReceiver; +import com.android.server.infra.AbstractPerUserSystemService; + +import java.util.ArrayList; + +final class TranslationManagerServiceImpl extends + AbstractPerUserSystemService<TranslationManagerServiceImpl, TranslationManagerService> { + + private static final String TAG = "TranslationManagerServiceImpl"; + + @GuardedBy("mLock") + @Nullable + private RemoteTranslationService mRemoteTranslationService; + + @GuardedBy("mLock") + @Nullable + private ServiceInfo mRemoteTranslationServiceInfo; + + protected TranslationManagerServiceImpl( + @NonNull TranslationManagerService master, + @NonNull Object lock, int userId, boolean disabled) { + super(master, lock, userId); + updateRemoteServiceLocked(); + } + + @GuardedBy("mLock") + @Override // from PerUserSystemService + protected ServiceInfo newServiceInfoLocked(@NonNull ComponentName serviceComponent) + throws PackageManager.NameNotFoundException { + final TranslationServiceInfo info = new TranslationServiceInfo(getContext(), + serviceComponent, isTemporaryServiceSetLocked(), mUserId); + mRemoteTranslationServiceInfo = info.getServiceInfo(); + return info.getServiceInfo(); + } + + @GuardedBy("mLock") + @Override // from PerUserSystemService + protected boolean updateLocked(boolean disabled) { + final boolean enabledChanged = super.updateLocked(disabled); + updateRemoteServiceLocked(); + return enabledChanged; + } + + /** + * Updates the reference to the remote service. + */ + @GuardedBy("mLock") + private void updateRemoteServiceLocked() { + if (mRemoteTranslationService != null) { + if (mMaster.debug) Slog.d(TAG, "updateRemoteService(): destroying old remote service"); + mRemoteTranslationService.unbind(); + mRemoteTranslationService = null; + } + } + + @GuardedBy("mLock") + @Nullable + private RemoteTranslationService ensureRemoteServiceLocked() { + if (mRemoteTranslationService == null) { + final String serviceName = getComponentNameLocked(); + if (serviceName == null) { + if (mMaster.verbose) { + Slog.v(TAG, "ensureRemoteServiceLocked(): no service component name."); + } + return null; + } + final ComponentName serviceComponent = ComponentName.unflattenFromString(serviceName); + mRemoteTranslationService = new RemoteTranslationService(getContext(), + serviceComponent, mUserId, /* isInstantAllowed= */ false); + } + return mRemoteTranslationService; + } + + @GuardedBy("mLock") + void getSupportedLocalesLocked(@NonNull IResultReceiver resultReceiver) { + // TODO: implement this + try { + resultReceiver.send(STATUS_SYNC_CALL_SUCCESS, + SyncResultReceiver.bundleFor(new ArrayList<>())); + } catch (RemoteException e) { + Slog.w(TAG, "RemoteException returning supported locales: " + e); + } + } + + @GuardedBy("mLock") + void onSessionCreatedLocked(@NonNull TranslationSpec sourceSpec, + @NonNull TranslationSpec destSpec, int sessionId, IResultReceiver resultReceiver) { + final RemoteTranslationService remoteService = ensureRemoteServiceLocked(); + if (remoteService != null) { + remoteService.onSessionCreated(sourceSpec, destSpec, sessionId, resultReceiver); + } + } +} diff --git a/telecomm/java/android/telecom/Call.java b/telecomm/java/android/telecom/Call.java index 1238e7b69a87..044ea80cba4b 100755 --- a/telecomm/java/android/telecom/Call.java +++ b/telecomm/java/android/telecom/Call.java @@ -607,6 +607,11 @@ public final class Call { */ public static final int PROPERTY_IS_ADHOC_CONFERENCE = 0x00002000; + /** + * Connection is using Cross SIM Calling. + */ + public static final int PROPERTY_CROSS_SIM = 0x00004000; + //****************************************************************************************** // Next PROPERTY value: 0x00004000 //****************************************************************************************** @@ -798,6 +803,9 @@ public final class Call { if (hasProperty(properties, PROPERTY_IS_ADHOC_CONFERENCE)) { builder.append(" PROPERTY_IS_ADHOC_CONFERENCE"); } + if (hasProperty(properties, PROPERTY_CROSS_SIM)) { + builder.append(" PROPERTY_CROSS_SIM"); + } builder.append("]"); return builder.toString(); } diff --git a/telecomm/java/android/telecom/Connection.java b/telecomm/java/android/telecom/Connection.java index 724a9e477b95..7a21b3adf654 100644 --- a/telecomm/java/android/telecom/Connection.java +++ b/telecomm/java/android/telecom/Connection.java @@ -527,9 +527,17 @@ public abstract class Connection extends Conferenceable { */ public static final int PROPERTY_IS_ADHOC_CONFERENCE = 1 << 12; + /** + * Connection is using cross sim technology. + * <p> + * Indicates that the {@link Connection} is using a cross sim technology which would + * register IMS over internet APN of default data subscription. + * <p> + */ + public static final int PROPERTY_CROSS_SIM = 1 << 13; //********************************************************************************************** - // Next PROPERTY value: 1<<13 + // Next PROPERTY value: 1<<14 //********************************************************************************************** /** diff --git a/telephony/java/android/telephony/CarrierBandwidth.java b/telephony/java/android/telephony/CarrierBandwidth.java index 17747a3919ee..b153fefce6e3 100644 --- a/telephony/java/android/telephony/CarrierBandwidth.java +++ b/telephony/java/android/telephony/CarrierBandwidth.java @@ -18,6 +18,7 @@ package android.telephony; import android.annotation.NonNull; import android.annotation.Nullable; +import android.annotation.RequiresFeature; import android.annotation.SystemApi; import android.os.Parcel; import android.os.Parcelable; @@ -102,7 +103,7 @@ public final class CarrierBandwidth implements Parcelable { /** * Retrieves the upstream bandwidth for the primary network in Kbps. This always only refers to * the estimated first hop transport bandwidth. - * This will be INVALID if the network is not connected + * This will be {@link #INVALID} if the network is not connected * * @return The estimated first hop upstream (device to network) bandwidth. */ @@ -113,7 +114,7 @@ public final class CarrierBandwidth implements Parcelable { /** * Retrieves the downstream bandwidth for the primary network in Kbps. This always only refers * to the estimated first hop transport bandwidth. - * This will be INVALID if the network is not connected + * This will be {@link #INVALID} if the network is not connected * * @return The estimated first hop downstream (network to device) bandwidth. */ @@ -124,10 +125,19 @@ public final class CarrierBandwidth implements Parcelable { /** * Retrieves the upstream bandwidth for the secondary network in Kbps. This always only refers * to the estimated first hop transport bandwidth. - * This will be INVALID if the network is not connected + * <p/> + * This will be {@link #INVALID} if either are the case: + * <ol> + * <li>The network is not connected</li> + * <li>The device does not support + * {@link android.telephony.TelephonyManager#CAPABILITY_SECONDARY_LINK_BANDWIDTH_VISIBLE}.</li> + * </ol> * * @return The estimated first hop upstream (device to network) bandwidth. */ + @RequiresFeature( + enforcement = "android.telephony.TelephonyManager#isRadioInterfaceCapabilitySupported", + value = TelephonyManager.CAPABILITY_SECONDARY_LINK_BANDWIDTH_VISIBLE) public int getSecondaryDownlinkCapacityKbps() { return mSecondaryDownlinkCapacityKbps; } @@ -135,10 +145,18 @@ public final class CarrierBandwidth implements Parcelable { /** * Retrieves the downstream bandwidth for the secondary network in Kbps. This always only * refers to the estimated first hop transport bandwidth. - * This will be INVALID if the network is not connected - * + * <p/> + * This will be {@link #INVALID} if either are the case: + * <ol> + * <li>The network is not connected</li> + * <li>The device does not support + * {@link android.telephony.TelephonyManager#CAPABILITY_SECONDARY_LINK_BANDWIDTH_VISIBLE}.</li> + * </ol> * @return The estimated first hop downstream (network to device) bandwidth. */ + @RequiresFeature( + enforcement = "android.telephony.TelephonyManager#isRadioInterfaceCapabilitySupported", + value = TelephonyManager.CAPABILITY_SECONDARY_LINK_BANDWIDTH_VISIBLE) public int getSecondaryUplinkCapacityKbps() { return mSecondaryUplinkCapacityKbps; } diff --git a/telephony/java/android/telephony/SubscriptionManager.java b/telephony/java/android/telephony/SubscriptionManager.java index 904232b54b8f..d4c2bc9d9b8b 100644 --- a/telephony/java/android/telephony/SubscriptionManager.java +++ b/telephony/java/android/telephony/SubscriptionManager.java @@ -373,6 +373,26 @@ public class SubscriptionManager { CONTENT_URI, "wfc_roaming_enabled"); /** + * A content {@link Uri} used to receive updates on cross sim enabled user setting. + * <p> + * Use this {@link Uri} with a {@link ContentObserver} to be notified of changes to the + * subscription cross sim calling enabled + * {@link ImsMmTelManager#isCrossSimCallingEnabledByUser()} + * while your app is running. You can also use a {@link android.app.job.JobService} + * to ensure your app + * is notified of changes to the {@link Uri} even when it is not running. + * Note, however, that using a {@link android.app.job.JobService} does not guarantee timely + * delivery of updates to the {@link Uri}. + * To be notified of changes to a specific subId, append subId to the URI + * {@link Uri#withAppendedPath(Uri, String)}. + * @hide + */ + @NonNull + @SystemApi + public static final Uri CROSS_SIM_ENABLED_CONTENT_URI = Uri.withAppendedPath(CONTENT_URI, + SimInfo.COLUMN_CROSS_SIM_CALLING_ENABLED); + + /** * TelephonyProvider unique key column name is the subscription id. * <P>Type: TEXT (String)</P> */ diff --git a/telephony/java/android/telephony/TelephonyManager.java b/telephony/java/android/telephony/TelephonyManager.java index a6e870fe7a16..bfd37cd60806 100644 --- a/telephony/java/android/telephony/TelephonyManager.java +++ b/telephony/java/android/telephony/TelephonyManager.java @@ -32,6 +32,7 @@ import android.annotation.Nullable; import android.annotation.RequiresPermission; import android.annotation.SdkConstant; import android.annotation.SdkConstant.SdkConstantType; +import android.annotation.StringDef; import android.annotation.SuppressAutoDoc; import android.annotation.SuppressLint; import android.annotation.SystemApi; @@ -56,8 +57,10 @@ import android.os.Build; import android.os.Bundle; import android.os.Handler; import android.os.IBinder; +import android.os.Looper; import android.os.OutcomeReceiver; import android.os.ParcelFileDescriptor; +import android.os.ParcelUuid; import android.os.Parcelable; import android.os.PersistableBundle; import android.os.Process; @@ -118,8 +121,13 @@ import com.android.internal.telephony.RILConstants; import com.android.internal.telephony.SmsApplication; import com.android.telephony.Rlog; +import java.io.IOException; +import java.io.InputStream; +import java.io.OutputStream; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; +import java.nio.file.Files; +import java.nio.file.Path; import java.util.ArrayList; import java.util.Collections; import java.util.HashMap; @@ -553,12 +561,12 @@ public class TelephonyManager { private static final int MAXIMUM_CALL_COMPOSER_PICTURE_SIZE = 80000; - // TODO(hallliu): link to upload method in docs /** * Indicates the maximum size of the call composure picture. * - * Pictures sent via uploadCallComposerPicture must not exceed this size, or an - * {@link IllegalArgumentException} will be thrown. + * Pictures sent via {@link #uploadCallComposerPicture(InputStream, Executor, OutcomeReceiver)} + * or {@link #uploadCallComposerPicture(Path, Executor, OutcomeReceiver)} must not exceed this + * size, or an error will be returned via the callback in those methods. * * @return Maximum file size in bytes. */ @@ -4242,6 +4250,342 @@ public class TelephonyManager { } /** + * Exception that may be supplied to the callback in {@link #uploadCallComposerPicture} if + * something goes awry. + */ + public static class CallComposerException extends Exception { + /** + * Used internally only, signals success of the upload to the carrier. + * @hide + */ + public static final int SUCCESS = -1; + /** + * Indicates that an unknown error was encountered when uploading the call composer picture. + * + * Clients that encounter this error should retry the upload. + */ + public static final int ERROR_UNKNOWN = 0; + + /** + * Indicates that the phone process died or otherwise became unavailable while uploading the + * call composer picture. + * + * Clients that encounter this error should retry the upload. + */ + public static final int ERROR_REMOTE_END_CLOSED = 1; + + /** + * Indicates that the file or stream supplied exceeds the size limit defined in + * {@link #getMaximumCallComposerPictureSize()}. + * + * Clients that encounter this error should retry the upload after reducing the size of the + * picture. + */ + public static final int ERROR_FILE_TOO_LARGE = 2; + + /** + * Indicates that the device failed to authenticate with the carrier when uploading the + * picture. + * + * Clients that encounter this error should not retry the upload unless a reboot or radio + * reset has been performed in the interim. + */ + public static final int ERROR_AUTHENTICATION_FAILED = 3; + + /** + * Indicates that the {@link InputStream} passed to {@link #uploadCallComposerPicture} + * was closed. + * + * The caller should retry if this error is encountered, and be sure to not close the stream + * before the callback is called this time. + */ + public static final int ERROR_INPUT_CLOSED = 4; + + /** + * Indicates that an {@link IOException} was encountered while reading the picture. + * + * The offending {@link IOException} will be available via {@link #getIOException()}. + * Clients should use the contents of the exception to determine whether a retry is + * warranted. + */ + public static final int ERROR_IO_EXCEPTION = 5; + + /** @hide */ + @IntDef(prefix = {"ERROR_"}, value = { + ERROR_UNKNOWN, + ERROR_REMOTE_END_CLOSED, + ERROR_FILE_TOO_LARGE, + ERROR_AUTHENTICATION_FAILED, + ERROR_INPUT_CLOSED, + ERROR_IO_EXCEPTION, + }) + @Retention(RetentionPolicy.SOURCE) + public @interface CallComposerError {} + + private final int mErrorCode; + private final IOException mIOException; + + public CallComposerException(@CallComposerError int errorCode, + @Nullable IOException ioException) { + mErrorCode = errorCode; + mIOException = ioException; + } + + /** + * Fetches the error code associated with this exception. + * @return An error code. + */ + public @CallComposerError int getErrorCode() { + return mErrorCode; + } + + /** + * Fetches the {@link IOException} that caused the error. + */ + // Follows the naming of IOException + @SuppressLint("AcronymName") + public @Nullable IOException getIOException() { + return mIOException; + } + } + + /** @hide */ + public static final String KEY_CALL_COMPOSER_PICTURE_HANDLE = "call_composer_picture_handle"; + + /** + * Uploads a picture to the carrier network for use with call composer. + * + * @see #uploadCallComposerPicture(InputStream, Executor, OutcomeReceiver) + * @param pictureToUpload Path to a local file containing the picture to upload. + * @param executor The {@link Executor} on which the {@code pictureToUpload} file will be read + * from disk, as well as on which {@code callback} will be called. + * @param callback A callback called when the upload operation terminates, either in success + * or in error. + */ + public void uploadCallComposerPicture(@NonNull Path pictureToUpload, + @CallbackExecutor @NonNull Executor executor, + @NonNull OutcomeReceiver<ParcelUuid, CallComposerException> callback) { + Objects.requireNonNull(pictureToUpload); + Objects.requireNonNull(executor); + Objects.requireNonNull(callback); + + // Do the role check now so that we can quit early if needed -- there's an additional + // permission check on the other side of the binder call as well. + RoleManager rm = mContext.getSystemService(RoleManager.class); + if (!rm.isRoleHeld(RoleManager.ROLE_DIALER)) { + throw new SecurityException("You must hold RoleManager.ROLE_DIALER to do this"); + } + + executor.execute(() -> { + try { + if (Looper.getMainLooper().isCurrentThread()) { + Log.w(TAG, "Uploading call composer picture on main thread!" + + " hic sunt dracones!"); + } + long size = Files.size(pictureToUpload); + if (size > getMaximumCallComposerPictureSize()) { + callback.onError(new CallComposerException( + CallComposerException.ERROR_FILE_TOO_LARGE, null)); + return; + } + InputStream fileStream = Files.newInputStream(pictureToUpload); + try { + uploadCallComposerPicture(fileStream, executor, + new OutcomeReceiver<ParcelUuid, CallComposerException>() { + @Override + public void onResult(ParcelUuid result) { + try { + fileStream.close(); + } catch (IOException e) { + // ignore + Log.e(TAG, "Error closing file input stream when" + + " uploading call composer pic"); + } + callback.onResult(result); + } + + @Override + public void onError(CallComposerException error) { + try { + fileStream.close(); + } catch (IOException e) { + // ignore + Log.e(TAG, "Error closing file input stream when" + + " uploading call composer pic"); + } + callback.onError(error); + } + }); + } catch (Exception e) { + Log.e(TAG, "Got exception calling into stream-version of" + + " uploadCallComposerPicture: " + e); + try { + fileStream.close(); + } catch (IOException e1) { + // ignore + Log.e(TAG, "Error closing file input stream when uploading" + + " call composer pic"); + } + } + } catch (IOException e) { + Log.e(TAG, "IOException when uploading call composer pic:" + e); + callback.onError( + new CallComposerException(CallComposerException.ERROR_IO_EXCEPTION, e)); + } + }); + + } + + /** + * Uploads a picture to the carrier network for use with call composer. + * + * This method allows a dialer app to upload a picture to the carrier network that can then + * later be attached to an outgoing call. In order to attach the picture to a call, use the + * {@link ParcelUuid} returned from {@code callback} upon successful upload as the value to + * {@link TelecomManager#EXTRA_OUTGOING_PICTURE}. + * + * This functionality is only available to the app filling the {@link RoleManager#ROLE_DIALER} + * role on the device. + * + * @param pictureToUpload An {@link InputStream} that supplies the bytes representing the + * picture to upload. The client bears responsibility for closing this + * stream after {@code callback} is called with success or failure. + * + * Additionally, if the stream supplies more bytes than the return value + * of {@link #getMaximumCallComposerPictureSize()}, the upload will be + * aborted and the callback will be called with an exception containing + * {@link CallComposerException#ERROR_FILE_TOO_LARGE}. + * @param executor The {@link Executor} on which the {@code pictureToUpload} stream will be + * read, as well as on which the callback will be called. + * @param callback A callback called when the upload operation terminates, either in success + * or in error. + */ + public void uploadCallComposerPicture(@NonNull InputStream pictureToUpload, + @CallbackExecutor @NonNull Executor executor, + @NonNull OutcomeReceiver<ParcelUuid, CallComposerException> callback) { + Objects.requireNonNull(pictureToUpload); + Objects.requireNonNull(executor); + Objects.requireNonNull(callback); + + ITelephony telephony = getITelephony(); + if (telephony == null) { + throw new IllegalStateException("Telephony service not available."); + } + + ParcelFileDescriptor writeFd; + ParcelFileDescriptor readFd; + try { + ParcelFileDescriptor[] pipe = ParcelFileDescriptor.createReliablePipe(); + writeFd = pipe[1]; + readFd = pipe[0]; + } catch (IOException e) { + executor.execute(() -> callback.onError( + new CallComposerException(CallComposerException.ERROR_IO_EXCEPTION, e))); + return; + } + + OutputStream output = new ParcelFileDescriptor.AutoCloseOutputStream(writeFd); + + try { + telephony.uploadCallComposerPicture(getSubId(), mContext.getOpPackageName(), + readFd, new ResultReceiver(null) { + @Override + protected void onReceiveResult(int resultCode, Bundle result) { + if (resultCode != CallComposerException.SUCCESS) { + executor.execute(() -> callback.onError( + new CallComposerException(resultCode, null))); + return; + } + ParcelUuid resultUuid = + result.getParcelable(KEY_CALL_COMPOSER_PICTURE_HANDLE); + if (resultUuid == null) { + Log.e(TAG, "Got null uuid without an error" + + " while uploading call composer pic"); + executor.execute(() -> callback.onError( + new CallComposerException( + CallComposerException.ERROR_UNKNOWN, null))); + return; + } + executor.execute(() -> callback.onResult(resultUuid)); + } + }); + } catch (RemoteException e) { + Log.e(TAG, "Remote exception uploading call composer pic:" + e); + e.rethrowAsRuntimeException(); + } + + executor.execute(() -> { + if (Looper.getMainLooper().isCurrentThread()) { + Log.w(TAG, "Uploading call composer picture on main thread!" + + " hic sunt dracones!"); + } + + int totalBytesRead = 0; + byte[] buffer = new byte[16 * 1024]; + try { + while (true) { + int numRead; + try { + numRead = pictureToUpload.read(buffer); + } catch (IOException e) { + Log.e(TAG, "IOException reading from input while uploading pic: " + e); + // Most likely, this was because the stream was closed. We have no way to + // tell though. + callback.onError(new CallComposerException( + CallComposerException.ERROR_INPUT_CLOSED, e)); + try { + writeFd.closeWithError("input closed"); + } catch (IOException e1) { + // log and ignore + Log.e(TAG, "Error closing fd pipe: " + e1); + } + break; + } + + if (numRead < 0) { + break; + } + + totalBytesRead += numRead; + if (totalBytesRead > getMaximumCallComposerPictureSize()) { + Log.e(TAG, "Read too many bytes from call composer pic stream: " + + totalBytesRead); + try { + callback.onError(new CallComposerException( + CallComposerException.ERROR_FILE_TOO_LARGE, null)); + writeFd.closeWithError("too large"); + } catch (IOException e1) { + // log and ignore + Log.e(TAG, "Error closing fd pipe: " + e1); + } + break; + } + + try { + output.write(buffer, 0, numRead); + } catch (IOException e) { + callback.onError(new CallComposerException( + CallComposerException.ERROR_REMOTE_END_CLOSED, e)); + try { + writeFd.closeWithError("remote end closed"); + } catch (IOException e1) { + // log and ignore + Log.e(TAG, "Error closing fd pipe: " + e1); + } + break; + } + } + } finally { + try { + output.close(); + } catch (IOException e) { + // Ignore -- we might've already closed it. + } + } + }); + } + + /** * Returns the Group Identifier Level1 for a GSM phone. * Return null if it is unavailable. * @@ -9953,6 +10297,8 @@ public class TelephonyManager { * Valid return results are: * - {@link ImsRegistrationImplBase#REGISTRATION_TECH_LTE} for LTE registration, * - {@link ImsRegistrationImplBase#REGISTRATION_TECH_IWLAN} for IWLAN registration, or + * - {@link ImsRegistrationImplBase#REGISTRATION_TECH_CROSS_SIM} for registration over + * other sim's internet, or * - {@link ImsRegistrationImplBase#REGISTRATION_TECH_NONE} if we are not registered or the * result is unavailable. * Use {@link ImsMmTelManager.RegistrationCallback} instead. @@ -14430,10 +14776,22 @@ public class TelephonyManager { return Collections.emptyList(); } + /** + * Indicates whether {@link CarrierBandwidth#getSecondaryDownlinkCapacityKbps()} and + * {@link CarrierBandwidth#getSecondaryUplinkCapacityKbps()} are visible. See comments + * on respective methods for more information. + * + * @hide + */ + @SystemApi + public static final String CAPABILITY_SECONDARY_LINK_BANDWIDTH_VISIBLE = + "CAPABILITY_SECONDARY_LINK_BANDWIDTH_VISIBLE"; + /** @hide */ @Retention(RetentionPolicy.SOURCE) - @IntDef(prefix = {"RADIO_INTERFACE_CAPABILITY_"}, - value = {}) + @StringDef(prefix = "CAPABILITY_", value = { + CAPABILITY_SECONDARY_LINK_BANDWIDTH_VISIBLE, + }) public @interface RadioInterfaceCapability {} /** @@ -14446,6 +14804,7 @@ public class TelephonyManager { * * @hide */ + @SystemApi public boolean isRadioInterfaceCapabilitySupported( @NonNull @RadioInterfaceCapability String capability) { try { diff --git a/telephony/java/android/telephony/ims/ImsCallProfile.java b/telephony/java/android/telephony/ims/ImsCallProfile.java index b15f4edeb79f..1faae42f054b 100644 --- a/telephony/java/android/telephony/ims/ImsCallProfile.java +++ b/telephony/java/android/telephony/ims/ImsCallProfile.java @@ -445,6 +445,15 @@ public final class ImsCallProfile implements Parcelable { public static final String EXTRA_FORWARDED_NUMBER = "android.telephony.ims.extra.FORWARDED_NUMBER"; + /** + * Extra key with an {@code boolean} value which can be set in + * {@link #setCallExtraBoolean(String, boolean)} to indicate whether call is a cross sim call. + * <p> + * Valid values are true if call is cross sim call else false. + */ + public static final String EXTRA_IS_CROSS_SIM_CALL = + "android.telephony.ims.extra.IS_CROSS_SIM_CALL"; + /** @hide */ public int mServiceType; /** @hide */ diff --git a/telephony/java/android/telephony/ims/ImsMmTelManager.java b/telephony/java/android/telephony/ims/ImsMmTelManager.java index 218875e347c7..fcb4782c7f62 100644 --- a/telephony/java/android/telephony/ims/ImsMmTelManager.java +++ b/telephony/java/android/telephony/ims/ImsMmTelManager.java @@ -716,6 +716,7 @@ public class ImsMmTelManager implements RegistrationManager { * * @param imsRegTech The IMS registration technology, can be one of the following: * {@link ImsRegistrationImplBase#REGISTRATION_TECH_LTE}, + * {@link ImsRegistrationImplBase#REGISTRATION_TECH_CROSS_SIM}, * {@link ImsRegistrationImplBase#REGISTRATION_TECH_IWLAN} * @param capability The IMS MmTel capability to query, can be one of the following: * {@link MmTelFeature.MmTelCapabilities#CAPABILITY_TYPE_VOICE}, @@ -750,6 +751,7 @@ public class ImsMmTelManager implements RegistrationManager { * * @param imsRegTech The IMS registration technology, can be one of the following: * {@link ImsRegistrationImplBase#REGISTRATION_TECH_LTE}, + * {@link ImsRegistrationImplBase#REGISTRATION_TECH_CROSS_SIM}, * {@link ImsRegistrationImplBase#REGISTRATION_TECH_IWLAN} * @param capability The IMS MmTel capability to query, can be one of the following: * {@link MmTelFeature.MmTelCapabilities#CAPABILITY_TYPE_VOICE}, @@ -979,6 +981,105 @@ public class ImsMmTelManager implements RegistrationManager { } /** + * This configuration is meaningful only on dual sim device. + * If enabled, this will result in the device setting up IMS of all other + * active subscriptions over the INTERNET APN of the primary default data subscription + * when any of those subscriptions are roaming or out of service and if wifi is not available + * for VoWifi. This feature will be disabled if + * {@link CarrierConfigManager#KEY_CARRIER_CROSS_SIM_IMS_AVAILABLE_BOOL} is set to false. + * <p>Following are the conditions in which system will try to register IMS over + * cross sim + * <ul> + * <li>Wifi is not available, one SIM is roaming and the default data + * SIM is in home network. Then roaming SIM IMS will be registered over INTERNET APN of the + * default data subscription </li> + * <li>Wifi is not available, one SIM is out of service and the default data + * SIM is in home network. Then out of service SIM IMS will be registered over INTERNET + * APN of the default data subscription </li> + * </ul> + * <p>This API requires one of the following: + * <ul> + * <li>The caller holds the READ_PRECISE_PHONE_STATE permission.</li> + * <li>If the caller is the device or profile owner, the caller holds the + * {@link Manifest.permission#READ_PRECISE_PHONE_STATE} permission.</li> + * <li>The caller has carrier privileges (see + * {@link android.telephony.TelephonyManager#hasCarrierPrivileges}) on any + * active subscription.</li> + * </ul> + * <p>The profile owner is an app that owns a managed profile on the device; for more details + * see <a href="https://developer.android.com/work/managed-profiles">Work profiles</a>. + * Access by profile owners is deprecated and will be removed in a future release. + * + * @throws ImsException if the IMS service associated with this subscription is not available or + * the IMS service is not available. + * @return true if the user's setting for Voice over Cross SIM is enabled and false if it is not + */ + @SuppressAutoDoc // No support for device / profile owner or carrier privileges (b/72967236). + @RequiresPermission(anyOf = { + android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE, + android.Manifest.permission.READ_PRECISE_PHONE_STATE}) + public boolean isCrossSimCallingEnabledByUser() throws ImsException { + ITelephony iTelephony = getITelephony(); + if (iTelephony == null) { + throw new ImsException("Could not find Telephony Service.", + ImsException.CODE_ERROR_SERVICE_UNAVAILABLE); + } + + try { + return iTelephony.isCrossSimCallingEnabledByUser(mSubId); + } catch (ServiceSpecificException sse) { + throw new ImsException(sse.getMessage(), sse.errorCode); + } catch (RemoteException e) { + e.rethrowAsRuntimeException(); + } + // Not reachable. Adding return to make compiler happy. + return false; + } + + /** + * Sets the user's setting for whether or not Voice over Cross SIM is enabled. + * If enabled, this will result in the device setting up IMS of all other + * active subscriptions over the INTERNET APN of the primary default data subscription + * when any of those subscriptions are roaming or out of service and if wifi is not available + * for VoWifi. This feature will be disabled if + * {@link CarrierConfigManager#KEY_CARRIER_CROSS_SIM_IMS_AVAILABLE_BOOL} is set to false. + * + * <p>Following are the conditions in which system will try to register IMS over + * cross sim + * <ul> + * <li>Wifi is not available, one SIM is roaming and the default data + * SIM is in home network. Then roaming SIM IMS will be registered over INTERNET APN of the + * default data subscription </li> + * <li>Wifi is not available, one SIM is out of service and the default data + * SIM is in home network. Then out of service SIM IMS will be registered over INTERNET + * APN of the default data subscription </li> + * </ul> + * @throws ImsException if the IMS service associated with this subscription is not available or + * the IMS service is not available. + * @param isEnabled true if the user's setting for Voice over Cross SIM is enabled, + * false otherwise + * @see #isCrossSimCallingEnabledByUser() + * @hide + */ + @SystemApi + @RequiresPermission(Manifest.permission.MODIFY_PHONE_STATE) + public void setCrossSimCallingEnabled(boolean isEnabled) throws ImsException { + ITelephony iTelephony = getITelephony(); + if (iTelephony == null) { + throw new ImsException("Could not find Telephony Service.", + ImsException.CODE_ERROR_SERVICE_UNAVAILABLE); + } + + try { + iTelephony.setCrossSimCallingEnabled(mSubId, isEnabled); + } catch (ServiceSpecificException sse) { + throw new ImsException(sse.getMessage(), sse.errorCode); + } catch (RemoteException e) { + e.rethrowAsRuntimeException(); + } + } + + /** * Returns the user's voice over WiFi roaming setting associated with the current subscription. * * <p>This API requires one of the following: diff --git a/telephony/java/android/telephony/ims/ImsRcsManager.java b/telephony/java/android/telephony/ims/ImsRcsManager.java index ad461c091493..f39e30b6d61f 100644 --- a/telephony/java/android/telephony/ims/ImsRcsManager.java +++ b/telephony/java/android/telephony/ims/ImsRcsManager.java @@ -390,6 +390,7 @@ public class ImsRcsManager { * @param capability The RCS capability to query. * @param radioTech The radio tech that this capability failed for, defined as * {@link ImsRegistrationImplBase#REGISTRATION_TECH_LTE} or + * {@link ImsRegistrationImplBase#REGISTRATION_TECH_CROSS_SIM} or * {@link ImsRegistrationImplBase#REGISTRATION_TECH_IWLAN}. * @return true if the RCS capability is capable for this subscription, false otherwise. This * does not necessarily mean that we are registered for IMS and the capability is available, but diff --git a/telephony/java/android/telephony/ims/RcsContactPresenceTuple.java b/telephony/java/android/telephony/ims/RcsContactPresenceTuple.java index e4d20e965c49..519d0164b0d6 100644 --- a/telephony/java/android/telephony/ims/RcsContactPresenceTuple.java +++ b/telephony/java/android/telephony/ims/RcsContactPresenceTuple.java @@ -34,7 +34,7 @@ import java.util.List; * network during a SUBSCRIBE request. See RFC3863 for more information. * @hide */ -public class RcsContactPresenceTuple implements Parcelable { +public final class RcsContactPresenceTuple implements Parcelable { /** The service id of the MMTEL */ public static final String SERVICE_ID_MMTEL = "org.3gpp.urn:urn-7:3gpp-service.ims.icsi.mmtel"; @@ -61,7 +61,7 @@ public class RcsContactPresenceTuple implements Parcelable { * An optional addition to the PIDF Presence Tuple containing service capabilities, which is * defined in the servcaps element. See RFC5196, section 3.2.1. */ - public static class ServiceCapabilities implements Parcelable { + public static final class ServiceCapabilities implements Parcelable { /** The service can simultaneously send and receive data. */ public static final String DUPLEX_MODE_FULL = "full"; @@ -88,7 +88,7 @@ public class RcsContactPresenceTuple implements Parcelable { /** * Builder to help construct {@link ServiceCapabilities} instances. */ - public static class Builder { + public static final class Builder { private ServiceCapabilities mCapabilities; @@ -106,7 +106,7 @@ public class RcsContactPresenceTuple implements Parcelable { * Add the supported duplex mode. * @param mode The supported duplex mode */ - public Builder addSupportedDuplexMode(@NonNull @DuplexMode String mode) { + public @NonNull Builder addSupportedDuplexMode(@NonNull @DuplexMode String mode) { mCapabilities.mSupportedDuplexModeList.add(mode); return this; } @@ -115,7 +115,7 @@ public class RcsContactPresenceTuple implements Parcelable { * Add the unsupported duplex mode. * @param mode The unsupported duplex mode */ - public Builder addUnsupportedDuplexMode(@NonNull @DuplexMode String mode) { + public @NonNull Builder addUnsupportedDuplexMode(@NonNull @DuplexMode String mode) { mCapabilities.mUnsupportedDuplexModeList.add(mode); return this; } @@ -123,7 +123,7 @@ public class RcsContactPresenceTuple implements Parcelable { /** * @return the ServiceCapabilities instance. */ - public ServiceCapabilities build() { + public @NonNull ServiceCapabilities build() { return mCapabilities; } } @@ -211,9 +211,9 @@ public class RcsContactPresenceTuple implements Parcelable { /** * Builder to help construct {@link RcsContactPresenceTuple} instances. */ - public static class Builder { + public static final class Builder { - private RcsContactPresenceTuple mPresenceTuple; + private final RcsContactPresenceTuple mPresenceTuple; /** * Builds a RcsContactPresenceTuple instance. @@ -230,7 +230,7 @@ public class RcsContactPresenceTuple implements Parcelable { /** * The optional SIP Contact URI associated with the PIDF tuple element. */ - public Builder addContactUri(@NonNull Uri contactUri) { + public @NonNull Builder addContactUri(@NonNull Uri contactUri) { mPresenceTuple.mContactUri = contactUri; return this; } @@ -239,7 +239,7 @@ public class RcsContactPresenceTuple implements Parcelable { * The optional timestamp indicating the data and time of the status change of this tuple. * See RFC3863, section 4.1.7 for more information on the expected format. */ - public Builder addTimeStamp(@NonNull String timestamp) { + public @NonNull Builder addTimeStamp(@NonNull String timestamp) { mPresenceTuple.mTimestamp = timestamp; return this; } @@ -248,7 +248,7 @@ public class RcsContactPresenceTuple implements Parcelable { * An optional parameter containing the description element of the service-description. See * OMA Presence SIMPLE specification v1.1 */ - public Builder addDescription(@NonNull String description) { + public @NonNull Builder addDescription(@NonNull String description) { mPresenceTuple.mServiceDescription = description; return this; } @@ -257,7 +257,7 @@ public class RcsContactPresenceTuple implements Parcelable { * An optional parameter containing the service capabilities of the presence tuple if they * are present in the servcaps element. */ - public Builder addServiceCapabilities(@NonNull ServiceCapabilities caps) { + public @NonNull Builder addServiceCapabilities(@NonNull ServiceCapabilities caps) { mPresenceTuple.mServiceCapabilities = caps; return this; } @@ -265,7 +265,7 @@ public class RcsContactPresenceTuple implements Parcelable { /** * @return the constructed instance. */ - public RcsContactPresenceTuple build() { + public @NonNull RcsContactPresenceTuple build() { return mPresenceTuple; } } diff --git a/telephony/java/android/telephony/ims/RcsContactUceCapability.java b/telephony/java/android/telephony/ims/RcsContactUceCapability.java index 5848be8b0bf2..d4715bfeeb3e 100644 --- a/telephony/java/android/telephony/ims/RcsContactUceCapability.java +++ b/telephony/java/android/telephony/ims/RcsContactUceCapability.java @@ -144,7 +144,7 @@ public final class RcsContactUceCapability implements Parcelable { * @param tag the supported feature tag * @return this OptionBuilder */ - public @NonNull OptionsBuilder addFeatureTag(String tag) { + public @NonNull OptionsBuilder addFeatureTag(@NonNull String tag) { mCapabilities.mFeatureTags.add(tag); return this; } @@ -154,7 +154,7 @@ public final class RcsContactUceCapability implements Parcelable { * @param tags the list of the supported feature tags * @return this OptionBuilder */ - public @NonNull OptionsBuilder addFeatureTags(List<String> tags) { + public @NonNull OptionsBuilder addFeatureTags(@NonNull List<String> tags) { mCapabilities.mFeatureTags.addAll(tags); return this; } @@ -195,7 +195,7 @@ public final class RcsContactUceCapability implements Parcelable { * @param tuple The {@link RcsContactPresenceTuple} to be added into. * @return this PresenceBuilder */ - public @NonNull PresenceBuilder addCapabilityTuple(RcsContactPresenceTuple tuple) { + public @NonNull PresenceBuilder addCapabilityTuple(@NonNull RcsContactPresenceTuple tuple) { mCapabilities.mPresenceTuples.add(tuple); return this; } @@ -205,7 +205,8 @@ public final class RcsContactUceCapability implements Parcelable { * @param tuples The list of the {@link RcsContactPresenceTuple} to be added into. * @return this PresenceBuilder */ - public @NonNull PresenceBuilder addCapabilityTuples(List<RcsContactPresenceTuple> tuples) { + public @NonNull PresenceBuilder addCapabilityTuples( + @NonNull List<RcsContactPresenceTuple> tuples) { mCapabilities.mPresenceTuples.addAll(tuples); return this; } @@ -282,7 +283,7 @@ public final class RcsContactUceCapability implements Parcelable { * @return The feature tags present in the OPTIONS response from the network. * <p> * Note: this is only populated if {@link #getCapabilityMechanism} is - * {@link CAPABILITY_MECHANISM_OPTIONS} + * {@link RcsContactUceCapability#CAPABILITY_MECHANISM_OPTIONS} */ public @NonNull List<String> getOptionsFeatureTags() { if (mCapabilityMechanism != CAPABILITY_MECHANISM_OPTIONS) { @@ -296,7 +297,7 @@ public final class RcsContactUceCapability implements Parcelable { * contained in the NOTIFY response from the network. * <p> * Note: this is only populated if {@link #getCapabilityMechanism} is - * {@link CAPABILITY_MECHANISM_PRESENCE} + * {@link RcsContactUceCapability#CAPABILITY_MECHANISM_PRESENCE} */ public @NonNull List<RcsContactPresenceTuple> getPresenceTuples() { if (mCapabilityMechanism != CAPABILITY_MECHANISM_PRESENCE) { @@ -312,9 +313,9 @@ public final class RcsContactUceCapability implements Parcelable { * * <p> * Note: this is only populated if {@link #getCapabilityMechanism} is - * {@link CAPABILITY_MECHANISM_PRESENCE} + * {@link RcsContactUceCapability#CAPABILITY_MECHANISM_PRESENCE} */ - public @Nullable RcsContactPresenceTuple getPresenceTuple(String serviceId) { + public @Nullable RcsContactPresenceTuple getPresenceTuple(@NonNull String serviceId) { if (mCapabilityMechanism != CAPABILITY_MECHANISM_PRESENCE) { return null; } diff --git a/telephony/java/android/telephony/ims/RcsUceAdapter.java b/telephony/java/android/telephony/ims/RcsUceAdapter.java index 8d7742b7510b..6c31466c2a89 100644 --- a/telephony/java/android/telephony/ims/RcsUceAdapter.java +++ b/telephony/java/android/telephony/ims/RcsUceAdapter.java @@ -36,7 +36,9 @@ import android.util.Log; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; +import java.util.HashMap; import java.util.List; +import java.util.Map; import java.util.concurrent.Executor; /** @@ -110,7 +112,7 @@ public class RcsUceAdapter { public static final int ERROR_FORBIDDEN = 6; /** - * The contact URI requested is not provisioned for VoLTE or it is not known as an IMS + * The contact URI requested is not provisioned for voice or it is not known as an IMS * subscriber to the carrier network. * @hide */ @@ -128,26 +130,26 @@ public class RcsUceAdapter { * The network did not respond to the capabilities request before the request timed out. * @hide */ - public static final int ERROR_REQUEST_TIMEOUT = 10; + public static final int ERROR_REQUEST_TIMEOUT = 9; /** * The request failed due to the service having insufficient memory. * @hide */ - public static final int ERROR_INSUFFICIENT_MEMORY = 11; + public static final int ERROR_INSUFFICIENT_MEMORY = 10; /** * The network was lost while trying to complete the request. * @hide */ - public static final int ERROR_LOST_NETWORK = 12; + public static final int ERROR_LOST_NETWORK = 11; /** * The network is temporarily unavailable or busy. Retries should only be done after the retry * time returned in {@link CapabilitiesCallback#onError} has elapsed. * @hide */ - public static final int ERROR_SERVER_UNAVAILABLE = 13; + public static final int ERROR_SERVER_UNAVAILABLE = 12; /**@hide*/ @Retention(RetentionPolicy.SOURCE) @@ -168,69 +170,93 @@ public class RcsUceAdapter { public @interface ErrorCode {} /** + * A capability update has been requested but the reason is unknown. + * @hide + */ + @SystemApi + public static final int CAPABILITY_UPDATE_TRIGGER_UNKNOWN = 0; + + /** * A capability update has been requested due to the Entity Tag (ETag) expiring. * @hide */ - public static final int CAPABILITY_UPDATE_TRIGGER_ETAG_EXPIRED = 0; + @SystemApi + public static final int CAPABILITY_UPDATE_TRIGGER_ETAG_EXPIRED = 1; + /** * A capability update has been requested due to moving to LTE with VoPS disabled. * @hide */ - public static final int CAPABILITY_UPDATE_TRIGGER_MOVE_TO_LTE_VOPS_DISABLED = 1; + @SystemApi + public static final int CAPABILITY_UPDATE_TRIGGER_MOVE_TO_LTE_VOPS_DISABLED = 2; + /** * A capability update has been requested due to moving to LTE with VoPS enabled. * @hide */ - public static final int CAPABILITY_UPDATE_TRIGGER_MOVE_TO_LTE_VOPS_ENABLED = 2; + @SystemApi + public static final int CAPABILITY_UPDATE_TRIGGER_MOVE_TO_LTE_VOPS_ENABLED = 3; + /** * A capability update has been requested due to moving to eHRPD. * @hide */ - public static final int CAPABILITY_UPDATE_TRIGGER_MOVE_TO_EHRPD = 3; + @SystemApi + public static final int CAPABILITY_UPDATE_TRIGGER_MOVE_TO_EHRPD = 4; + /** * A capability update has been requested due to moving to HSPA+. * @hide */ - public static final int CAPABILITY_UPDATE_TRIGGER_MOVE_TO_HSPAPLUS = 4; + @SystemApi + public static final int CAPABILITY_UPDATE_TRIGGER_MOVE_TO_HSPAPLUS = 5; + /** * A capability update has been requested due to moving to 3G. * @hide */ - public static final int CAPABILITY_UPDATE_TRIGGER_MOVE_TO_3G = 5; + @SystemApi + public static final int CAPABILITY_UPDATE_TRIGGER_MOVE_TO_3G = 6; + /** * A capability update has been requested due to moving to 2G. * @hide */ - public static final int CAPABILITY_UPDATE_TRIGGER_MOVE_TO_2G = 6; + @SystemApi + public static final int CAPABILITY_UPDATE_TRIGGER_MOVE_TO_2G = 7; + /** * A capability update has been requested due to moving to WLAN * @hide */ - public static final int CAPABILITY_UPDATE_TRIGGER_MOVE_TO_WLAN = 7; + @SystemApi + public static final int CAPABILITY_UPDATE_TRIGGER_MOVE_TO_WLAN = 8; + /** * A capability update has been requested due to moving to IWLAN * @hide */ - public static final int CAPABILITY_UPDATE_TRIGGER_MOVE_TO_IWLAN = 8; - /** - * A capability update has been requested but the reason is unknown. - * @hide - */ - public static final int CAPABILITY_UPDATE_TRIGGER_UNKNOWN = 9; + @SystemApi + public static final int CAPABILITY_UPDATE_TRIGGER_MOVE_TO_IWLAN = 9; + /** * A capability update has been requested due to moving to 5G NR with VoPS disabled. * @hide */ + @SystemApi public static final int CAPABILITY_UPDATE_TRIGGER_MOVE_TO_NR5G_VOPS_DISABLED = 10; + /** * A capability update has been requested due to moving to 5G NR with VoPS enabled. * @hide */ + @SystemApi public static final int CAPABILITY_UPDATE_TRIGGER_MOVE_TO_NR5G_VOPS_ENABLED = 11; /**@hide*/ @Retention(RetentionPolicy.SOURCE) @IntDef(prefix = "ERROR_", value = { + CAPABILITY_UPDATE_TRIGGER_UNKNOWN, CAPABILITY_UPDATE_TRIGGER_ETAG_EXPIRED, CAPABILITY_UPDATE_TRIGGER_MOVE_TO_LTE_VOPS_DISABLED, CAPABILITY_UPDATE_TRIGGER_MOVE_TO_LTE_VOPS_ENABLED, @@ -240,7 +266,6 @@ public class RcsUceAdapter { CAPABILITY_UPDATE_TRIGGER_MOVE_TO_2G, CAPABILITY_UPDATE_TRIGGER_MOVE_TO_WLAN, CAPABILITY_UPDATE_TRIGGER_MOVE_TO_IWLAN, - CAPABILITY_UPDATE_TRIGGER_UNKNOWN, CAPABILITY_UPDATE_TRIGGER_MOVE_TO_NR5G_VOPS_DISABLED, CAPABILITY_UPDATE_TRIGGER_MOVE_TO_NR5G_VOPS_ENABLED }) @@ -251,32 +276,37 @@ public class RcsUceAdapter { * UCE. * @hide */ + @SystemApi public static final int PUBLISH_STATE_OK = 1; /** * The hasn't published its capabilities since boot or hasn't gotten any publish response yet. * @hide */ + @SystemApi public static final int PUBLISH_STATE_NOT_PUBLISHED = 2; /** * The device has tried to publish its capabilities, which has resulted in an error. This error - * is related to the fact that the device is not VoLTE provisioned. + * is related to the fact that the device is not provisioned for voice. * @hide */ - public static final int PUBLISH_STATE_VOLTE_PROVISION_ERROR = 3; + @SystemApi + public static final int PUBLISH_STATE_VOICE_PROVISION_ERROR = 3; /** * The device has tried to publish its capabilities, which has resulted in an error. This error * is related to the fact that the device is not RCS or UCE provisioned. * @hide */ + @SystemApi public static final int PUBLISH_STATE_RCS_PROVISION_ERROR = 4; /** * The last publish resulted in a "408 Request Timeout" response. * @hide */ + @SystemApi public static final int PUBLISH_STATE_REQUEST_TIMEOUT = 5; /** @@ -286,6 +316,7 @@ public class RcsUceAdapter { * Device shall retry with exponential back-off. * @hide */ + @SystemApi public static final int PUBLISH_STATE_OTHER_ERROR = 6; /**@hide*/ @@ -293,7 +324,7 @@ public class RcsUceAdapter { @IntDef(prefix = "PUBLISH_STATE_", value = { PUBLISH_STATE_OK, PUBLISH_STATE_NOT_PUBLISHED, - PUBLISH_STATE_VOLTE_PROVISION_ERROR, + PUBLISH_STATE_VOICE_PROVISION_ERROR, PUBLISH_STATE_RCS_PROVISION_ERROR, PUBLISH_STATE_REQUEST_TIMEOUT, PUBLISH_STATE_OTHER_ERROR @@ -301,56 +332,62 @@ public class RcsUceAdapter { public @interface PublishState {} /** - * An application can use {@link #registerPublishStateCallback} to register a - * {@link PublishStateCallback), which will notify the user when the publish state to the - * network changes. + * An application can use {@link #addOnPublishStateChangedListener} to register a + * {@link OnPublishStateChangedListener ), which will notify the user when the publish state to + * the network changes. * @hide */ - public static class PublishStateCallback { + @SystemApi + public interface OnPublishStateChangedListener { + /** + * Notifies the callback when the publish state has changed. + * @param publishState The latest update to the publish state. + */ + void onPublishStateChange(@PublishState int publishState); + } - private static class PublishStateBinder extends IRcsUcePublishStateCallback.Stub { + /** + * An application can use {@link #addOnPublishStateChangedListener} to register a + * {@link OnPublishStateChangedListener ), which will notify the user when the publish state to + * the network changes. + * @hide + */ + public static class PublishStateCallbackAdapter { - private final PublishStateCallback mLocalCallback; - private Executor mExecutor; + private static class PublishStateBinder extends IRcsUcePublishStateCallback.Stub { + private final OnPublishStateChangedListener mPublishStateChangeListener; + private final Executor mExecutor; - PublishStateBinder(PublishStateCallback c) { - mLocalCallback = c; + PublishStateBinder(Executor executor, OnPublishStateChangedListener listener) { + mExecutor = executor; + mPublishStateChangeListener = listener; } @Override public void onPublishStateChanged(int publishState) { - if (mLocalCallback == null) return; + if (mPublishStateChangeListener == null) return; final long callingIdentity = Binder.clearCallingIdentity(); try { - mExecutor.execute(() -> mLocalCallback.onChanged(publishState)); + mExecutor.execute(() -> + mPublishStateChangeListener.onPublishStateChange(publishState)); } finally { restoreCallingIdentity(callingIdentity); } } - - private void setExecutor(Executor executor) { - mExecutor = executor; - } } - private final PublishStateBinder mBinder = new PublishStateBinder(this); + private final PublishStateBinder mBinder; + + public PublishStateCallbackAdapter(@NonNull Executor executor, + @NonNull OnPublishStateChangedListener listener) { + mBinder = new PublishStateBinder(executor, listener); + } /**@hide*/ public final IRcsUcePublishStateCallback getBinder() { return mBinder; } - - private void setExecutor(Executor executor) { - mBinder.setExecutor(executor); - } - - /** - * Notifies the callback when the publish state has changed. - * @param publishState The latest update to the publish state. - */ - public void onChanged(@PublishState int publishState) { - } } /** @@ -395,6 +432,8 @@ public class RcsUceAdapter { private final Context mContext; private final int mSubId; + private final Map<OnPublishStateChangedListener, PublishStateCallbackAdapter> + mPublishStateCallbacks; /** * Not to be instantiated directly, use {@link ImsRcsManager#getUceAdapter()} to instantiate @@ -404,6 +443,7 @@ public class RcsUceAdapter { RcsUceAdapter(Context context, int subId) { mContext = context; mSubId = subId; + mPublishStateCallbacks = new HashMap<>(); } /** @@ -588,6 +628,7 @@ public class RcsUceAdapter { * becomes inactive. See {@link ImsException#getCode()} for more information on the error codes. * @hide */ + @SystemApi @RequiresPermission(Manifest.permission.READ_PRIVILEGED_PHONE_STATE) public @PublishState int getUcePublishState() throws ImsException { IImsRcsController imsRcsController = getIImsRcsController(); @@ -609,81 +650,90 @@ public class RcsUceAdapter { } /** - * Registers a {@link PublishStateCallback} with the system, which will provide publish state - * updates for the subscription specified in {@link ImsManager@getRcsManager(subid)}. + * Registers a {@link OnPublishStateChangedListener} with the system, which will provide publish + * state updates for the subscription specified in {@link ImsManager@getRcsManager(subid)}. * <p> * Use {@link SubscriptionManager.OnSubscriptionsChangedListener} to listen to subscription * changed events and call {@link #unregisterPublishStateCallback} to clean up. * <p> - * The registered {@link PublishStateCallback} will also receive a callback when it is + * The registered {@link OnPublishStateChangedListener} will also receive a callback when it is * registered with the current publish state. * * @param executor The executor the listener callback events should be run on. - * @param c The {@link PublishStateCallback} to be added. + * @param listener The {@link OnPublishStateChangedListener} to be added. * @throws ImsException if the subscription associated with this callback is valid, but * the {@link ImsService} associated with the subscription is not available. This can happen if * the service crashed, for example. See {@link ImsException#getCode()} for a more detailed * reason. * @hide */ + @SystemApi @RequiresPermission(Manifest.permission.READ_PRIVILEGED_PHONE_STATE) - public void registerPublishStateCallback(@NonNull @CallbackExecutor Executor executor, - @NonNull PublishStateCallback c) throws ImsException { - if (c == null) { - throw new IllegalArgumentException("Must include a non-null PublishStateCallback."); - } + public void addOnPublishStateChangedListener(@NonNull @CallbackExecutor Executor executor, + @NonNull OnPublishStateChangedListener listener) throws ImsException { if (executor == null) { throw new IllegalArgumentException("Must include a non-null Executor."); } + if (listener == null) { + throw new IllegalArgumentException( + "Must include a non-null OnPublishStateChangedListener."); + } IImsRcsController imsRcsController = getIImsRcsController(); if (imsRcsController == null) { - Log.e(TAG, "registerPublishStateCallback : IImsRcsController is null"); + Log.e(TAG, "addOnPublishStateChangedListener : IImsRcsController is null"); throw new ImsException("Cannot find remote IMS service", - ImsException.CODE_ERROR_SERVICE_UNAVAILABLE); + ImsException.CODE_ERROR_SERVICE_UNAVAILABLE); } - c.setExecutor(executor); + PublishStateCallbackAdapter stateCallback = addPublishStateCallback(executor, listener); try { - imsRcsController.registerUcePublishStateCallback(mSubId, c.getBinder()); + imsRcsController.registerUcePublishStateCallback(mSubId, stateCallback.getBinder()); } catch (ServiceSpecificException e) { throw new ImsException(e.getMessage(), e.errorCode); } catch (RemoteException e) { Log.e(TAG, "Error calling IImsRcsController#registerUcePublishStateCallback", e); throw new ImsException("Remote IMS Service is not available", - ImsException.CODE_ERROR_SERVICE_UNAVAILABLE); + ImsException.CODE_ERROR_SERVICE_UNAVAILABLE); } } /** - * Removes an existing {@link PublishStateCallback}. + * Removes an existing {@link OnPublishStateChangedListener}. * <p> * When the subscription associated with this callback is removed * (SIM removed, ESIM swap,etc...), this callback will automatically be removed. If this method * is called for an inactive subscription, it will result in a no-op. * - * @param c The callback to be unregistered. + * @param listener The callback to be unregistered. * @throws ImsException if the subscription associated with this callback is valid, but * the {@link ImsService} associated with the subscription is not available. This can happen if * the service crashed, for example. See {@link ImsException#getCode()} for a more detailed * reason. * @hide */ + @SystemApi @RequiresPermission(Manifest.permission.READ_PRIVILEGED_PHONE_STATE) - public void unregisterPublishStateCallback(@NonNull PublishStateCallback c) - throws ImsException { - if (c == null) { - throw new IllegalArgumentException("Must include a non-null PublishStateCallback."); + public void removeOnPublishStateChangedListener( + @NonNull OnPublishStateChangedListener listener) throws ImsException { + if (listener == null) { + throw new IllegalArgumentException( + "Must include a non-null OnPublishStateChangedListener."); } IImsRcsController imsRcsController = getIImsRcsController(); if (imsRcsController == null) { - Log.e(TAG, "unregisterPublishStateCallback: IImsRcsController is null"); + Log.e(TAG, "removeOnPublishStateChangedListener: IImsRcsController is null"); throw new ImsException("Cannot find remote IMS service", ImsException.CODE_ERROR_SERVICE_UNAVAILABLE); } + PublishStateCallbackAdapter callback = removePublishStateCallback(listener); + if (callback == null) { + return; + } + try { - imsRcsController.unregisterUcePublishStateCallback(mSubId, c.getBinder()); + imsRcsController.unregisterUcePublishStateCallback(mSubId, callback.getBinder()); } catch (android.os.ServiceSpecificException e) { throw new ImsException(e.getMessage(), e.errorCode); } catch (RemoteException e) { @@ -763,6 +813,36 @@ public class RcsUceAdapter { } } + /** + * Add the {@link OnPublishStateChangedListener} to collection for tracking. + * @param executor The executor that will be used when the publish state is changed and the + * {@link OnPublishStateChangedListener} is called. + * @param listener The {@link OnPublishStateChangedListener} to call the publish state changed. + * @return The {@link PublishStateCallbackAdapter} to wrapper the + * {@link OnPublishStateChangedListener} + */ + private PublishStateCallbackAdapter addPublishStateCallback(@NonNull Executor executor, + @NonNull OnPublishStateChangedListener listener) { + PublishStateCallbackAdapter adapter = new PublishStateCallbackAdapter(executor, listener); + synchronized (mPublishStateCallbacks) { + mPublishStateCallbacks.put(listener, adapter); + } + return adapter; + } + + /** + * Remove the existing {@link OnPublishStateChangedListener}. + * @param listener The {@link OnPublishStateChangedListener} to remove from the collection. + * @return The wrapper class {@link PublishStateCallbackAdapter} associated with the + * {@link OnPublishStateChangedListener}. + */ + private PublishStateCallbackAdapter removePublishStateCallback( + @NonNull OnPublishStateChangedListener listener) { + synchronized (mPublishStateCallbacks) { + return mPublishStateCallbacks.remove(listener); + } + } + private IImsRcsController getIImsRcsController() { IBinder binder = TelephonyFrameworkInitializer .getTelephonyServiceManager() diff --git a/telephony/java/android/telephony/ims/RegistrationManager.java b/telephony/java/android/telephony/ims/RegistrationManager.java index 1a78e166932a..8ed48388d352 100644 --- a/telephony/java/android/telephony/ims/RegistrationManager.java +++ b/telephony/java/android/telephony/ims/RegistrationManager.java @@ -24,6 +24,7 @@ import android.annotation.Nullable; import android.annotation.RequiresPermission; import android.net.Uri; import android.os.Binder; +import android.os.Bundle; import android.telephony.AccessNetworkConstants; import android.telephony.ims.aidl.IImsRegistrationCallback; import android.telephony.ims.feature.ImsFeature; @@ -70,6 +71,29 @@ public interface RegistrationManager { */ int REGISTRATION_STATE_REGISTERED = 2; + /** + * @hide + */ + // Defines the underlying radio technology type that we have registered for IMS over. + @IntDef(prefix = "ATTR_", + value = { + ATTR_EPDG_OVER_CELL_INTERNET, + }, + flag = true) + @Retention(RetentionPolicy.SOURCE) + public @interface ImsAttributes {} + + /** + * Attribute to specify if EPDG tunnel is setup over cellular internet. + * if EPDG tunnel is setup over cellular internet then this bit will be set else the same will + * not be set. + */ + int ATTR_EPDG_OVER_CELL_INTERNET = 0x00000001; + + //****************************************************************************************** + // Next attribute value: 0x00000002 + //****************************************************************************************** + /**@hide*/ // Translate ImsRegistrationImplBase API to new AccessNetworkConstant because WLAN @@ -83,6 +107,11 @@ public interface RegistrationManager { AccessNetworkConstants.TRANSPORT_TYPE_WWAN); put(ImsRegistrationImplBase.REGISTRATION_TECH_IWLAN, AccessNetworkConstants.TRANSPORT_TYPE_WLAN); + /* As the cross sim will be using ePDG tunnel over internet, it behaves + like IWLAN in most cases. Hence setting the access type as IWLAN + */ + put(ImsRegistrationImplBase.REGISTRATION_TECH_CROSS_SIM, + AccessNetworkConstants.TRANSPORT_TYPE_WLAN); }}; /** @@ -96,6 +125,7 @@ public interface RegistrationManager { private final RegistrationCallback mLocalCallback; private Executor mExecutor; + private Bundle mBundle = new Bundle(); RegistrationBinder(RegistrationCallback localCallback) { mLocalCallback = localCallback; @@ -107,8 +137,18 @@ public interface RegistrationManager { final long callingIdentity = Binder.clearCallingIdentity(); try { + mExecutor.execute(() -> { + mLocalCallback.onRegistered(getAccessType(imsRadioTech)); + }); + int attributes = 0; + if (imsRadioTech == ImsRegistrationImplBase.REGISTRATION_TECH_CROSS_SIM) { + attributes = changeBitmask(attributes, ATTR_EPDG_OVER_CELL_INTERNET, + true); + } + final int finalattributes = attributes; mExecutor.execute(() -> - mLocalCallback.onRegistered(getAccessType(imsRadioTech))); + mLocalCallback.onRegistered(getAccessType(imsRadioTech), + finalattributes)); } finally { restoreCallingIdentity(callingIdentity); } @@ -122,6 +162,15 @@ public interface RegistrationManager { try { mExecutor.execute(() -> mLocalCallback.onRegistering(getAccessType(imsRadioTech))); + int attributes = 0; + if (imsRadioTech == ImsRegistrationImplBase.REGISTRATION_TECH_CROSS_SIM) { + attributes = changeBitmask(attributes, ATTR_EPDG_OVER_CELL_INTERNET, + true); + } + final int finalattributes = attributes; + mExecutor.execute(() -> + mLocalCallback.onRegistering(getAccessType(imsRadioTech), + finalattributes)); } finally { restoreCallingIdentity(callingIdentity); } @@ -175,6 +224,22 @@ public interface RegistrationManager { } return RegistrationManager.IMS_REG_TO_ACCESS_TYPE_MAP.get(regType); } + + /** + * Changes a attribute bit-mask to add or remove an attribute. + * + * @param bitmask The bit-mask. + * @param bitfield The bit-field to change. + * @param enabled Whether the bit-field should be set or removed. + * @return The bit-mask with the bit-field changed. + */ + private int changeBitmask(int bitmask, int bitfield, boolean enabled) { + if (enabled) { + return bitmask | bitfield; + } else { + return bitmask & ~bitfield; + } + } } private final RegistrationBinder mBinder = new RegistrationBinder(this); @@ -183,19 +248,49 @@ public interface RegistrationManager { * Notifies the framework when the IMS Provider is registered to the IMS network. * * @param imsTransportType the radio access technology. + * @deprecated Use {@link #onRegistered(int, int)} instead. */ + @Deprecated public void onRegistered(@AccessNetworkConstants.TransportType int imsTransportType) { } /** + * Notifies the framework when the IMS Provider is registered to the IMS network + * with corresponding attributes + * + * @param imsTransportType the radio access technology. + * @param registrationAttributes IMS registration attributes as a bitmap of attributes. + * Possible attributes are following + * <ul> + * <li>{@link #ATTR_EPDG_OVER_CELL_INTERNET}</li> + * </ul> + * + */ + public void onRegistered(@AccessNetworkConstants.TransportType int imsTransportType, + @ImsAttributes int registrationAttributes) { + } + + /** * Notifies the framework when the IMS Provider is trying to register the IMS network. * * @param imsTransportType the radio access technology. + * @deprecated Use {@link #onRegistering(int, int)} instead. */ public void onRegistering(@AccessNetworkConstants.TransportType int imsTransportType) { } /** + * Notifies the framework when the IMS Provider is trying to register the IMS network. + * + * @param imsTransportType the radio access technology. + * @param registrationAttributes IMS registration attributes as a bitmap of attributes. + * Possible attributes are following + */ + public void onRegistering(@AccessNetworkConstants.TransportType int imsTransportType, + @ImsAttributes int registrationAttributes) { + } + + /** * Notifies the framework when the IMS Provider is unregistered from the IMS network. * * @param info the {@link ImsReasonInfo} associated with why registration was disconnected. diff --git a/telephony/java/android/telephony/ims/aidl/CapabilityExchangeAidlWrapper.java b/telephony/java/android/telephony/ims/aidl/CapabilityExchangeAidlWrapper.java new file mode 100644 index 000000000000..4435640e008c --- /dev/null +++ b/telephony/java/android/telephony/ims/aidl/CapabilityExchangeAidlWrapper.java @@ -0,0 +1,115 @@ +/* + * Copyright (C) 2020 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.telephony.ims.aidl; + +import android.annotation.NonNull; +import android.annotation.Nullable; +import android.net.Uri; +import android.os.Binder; +import android.os.RemoteException; +import android.telephony.ims.RcsContactUceCapability; +import android.telephony.ims.stub.CapabilityExchangeEventListener; +import android.util.Log; + +import java.util.List; + +/** + * The ICapabilityExchangeEventListener wrapper class to store the listener which is registered by + * the framework. This wrapper class also delivers the request to the framework when receive the + * request from the network. + * @hide + */ +public class CapabilityExchangeAidlWrapper implements CapabilityExchangeEventListener { + + private static final String LOG_TAG = "CapExchangeListener"; + + private final ICapabilityExchangeEventListener mListenerBinder; + + public CapabilityExchangeAidlWrapper(@Nullable ICapabilityExchangeEventListener listener) { + mListenerBinder = listener; + } + + /** + * Receives the request of publishing capabilities from the network and deliver this request + * to the framework via the registered capability exchange event listener. + */ + public void onRequestPublishCapabilities(int publishTriggerType) { + ICapabilityExchangeEventListener listener = mListenerBinder; + if (listener == null) { + return; + } + try { + listener.onRequestPublishCapabilities(publishTriggerType); + } catch (RemoteException e) { + Log.w(LOG_TAG, "request publish capabilities exception: " + e); + } + } + + /** + * Receives the unpublish notification and deliver this callback to the framework. + */ + public void onUnpublish() { + ICapabilityExchangeEventListener listener = mListenerBinder; + if (listener == null) { + return; + } + try { + listener.onUnpublish(); + } catch (RemoteException e) { + Log.w(LOG_TAG, "Unpublish exception: " + e); + } + } + + /** + * Receives the callback of the remote capability request from the network and deliver this + * request to the framework. + */ + public void onRemoteCapabilityRequest(@NonNull Uri contactUri, + @NonNull List<String> remoteCapabilities, @NonNull OptionsRequestCallback callback) { + ICapabilityExchangeEventListener listener = mListenerBinder; + if (listener == null) { + return; + } + + IOptionsRequestCallback internalCallback = new IOptionsRequestCallback.Stub() { + @Override + public void respondToCapabilityRequest(RcsContactUceCapability ownCapabilities) { + final long callingIdentity = Binder.clearCallingIdentity(); + try { + callback.onRespondToCapabilityRequest(ownCapabilities); + } finally { + restoreCallingIdentity(callingIdentity); + } + } + @Override + public void respondToCapabilityRequestWithError(int code, String reason) { + final long callingIdentity = Binder.clearCallingIdentity(); + try { + callback.onRespondToCapabilityRequestWithError(code, reason); + } finally { + restoreCallingIdentity(callingIdentity); + } + } + }; + + try { + listener.onRemoteCapabilityRequest(contactUri, remoteCapabilities, internalCallback); + } catch (RemoteException e) { + Log.w(LOG_TAG, "Remote capability request exception: " + e); + } + } +} diff --git a/telephony/java/android/telephony/ims/aidl/ICapabilityExchangeEventListener.aidl b/telephony/java/android/telephony/ims/aidl/ICapabilityExchangeEventListener.aidl index a4ffbef9fa84..078ac919b75e 100644 --- a/telephony/java/android/telephony/ims/aidl/ICapabilityExchangeEventListener.aidl +++ b/telephony/java/android/telephony/ims/aidl/ICapabilityExchangeEventListener.aidl @@ -22,54 +22,15 @@ import android.telephony.ims.aidl.IOptionsRequestCallback; import java.util.List; /** - * Listener interface for the ImsService to use to notify the framework of UCE events. + * Listener interface for the ImsService to use to notify the framework of UCE + * events. + * + * See CapabilityExchangeEventListener for more information. * {@hide} */ oneway interface ICapabilityExchangeEventListener { - /** - * Trigger the framework to provide a capability update using - * {@link RcsCapabilityExchangeImplBase#publishCapabilities}. - * <p> - * This is typically used when trying to generate an initial PUBLISH for a new - * subscription to the network. The device will cache all presence publications - * after boot until this method is called the first time. - * @param publishTriggerType {@link StackPublishTriggerType} The reason for the - * capability update request. - * @throws ImsException If this {@link RcsPresenceExchangeImplBase} instance is - * not currently connected to the framework. This can happen if the - * {@link RcsFeature} is not {@link ImsFeature#STATE_READY} and the - * {@link RcsFeature} has not received the - * {@link ImsFeature#onFeatureReady()} callback. This may also happen in rare - * cases when the Telephony stack has crashed. - */ void onRequestPublishCapabilities(int publishTriggerType); - - /** - * Notify the framework that the device's capabilities have been unpublished from the network. - * - * @throws ImsException If this {@link RcsPresenceExchangeImplBase} instance is not currently - * connected to the framework. This can happen if the {@link RcsFeature} is not - * {@link ImsFeature#STATE_READY} and the {@link RcsFeature} has not received the - * {@link ImsFeature#onFeatureReady()} callback. This may also happen in rare cases when the - * Telephony stack has crashed. - */ void onUnpublish(); - - /** - * Inform the framework of a query for this device's UCE capabilities. - * <p> - * The framework will respond via the - * {@link IOptionsRequestCallback#respondToCapabilityRequest} or - * {@link IOptionsRequestCallback#respondToCapabilityRequestWithError} method. - * @param contactUri The URI associated with the remote contact that is requesting capabilities. - * @param remoteCapabilities The remote contact's capability information. - * @throws ImsException If this {@link RcsSipOptionsImplBase} instance is not currently - * connected to the framework. This can happen if the {@link RcsFeature} is not - * {@link ImsFeature#STATE_READY} and the {@link RcsFeature} has not received - * the {@link ImsFeature#onFeatureReady()} callback. This may also happen in rare cases when - * the Telephony stack has crashed. - */ void onRemoteCapabilityRequest(in Uri contactUri, - in List<String> remoteCapabilities, - IOptionsRequestCallback cb); + in List<String> remoteCapabilities, IOptionsRequestCallback cb); } diff --git a/telephony/java/android/telephony/ims/aidl/IOptionsRequestCallback.aidl b/telephony/java/android/telephony/ims/aidl/IOptionsRequestCallback.aidl index d55670dd313b..d4d5301f38fa 100644 --- a/telephony/java/android/telephony/ims/aidl/IOptionsRequestCallback.aidl +++ b/telephony/java/android/telephony/ims/aidl/IOptionsRequestCallback.aidl @@ -33,7 +33,6 @@ oneway interface IOptionsRequestCallback { /** * Respond to a remote capability request from the contact specified with the * specified error. - * @param contactUri A URI containing the remote contact. * @param code The SIP response code to respond with. * @param reason A non-null String containing the reason associated with the SIP code. */ diff --git a/telephony/java/android/telephony/ims/feature/CapabilityChangeRequest.java b/telephony/java/android/telephony/ims/feature/CapabilityChangeRequest.java index 87a6873d00b2..c5b1c90ced12 100644 --- a/telephony/java/android/telephony/ims/feature/CapabilityChangeRequest.java +++ b/telephony/java/android/telephony/ims/feature/CapabilityChangeRequest.java @@ -44,6 +44,7 @@ public final class CapabilityChangeRequest implements Parcelable { * along with an associated technology, defined as * {@link ImsRegistrationImplBase#REGISTRATION_TECH_LTE} or * {@link ImsRegistrationImplBase#REGISTRATION_TECH_IWLAN} + * {@link ImsRegistrationImplBase#REGISTRATION_TECH_CROSS_SIM} */ public static class CapabilityPair { private final int mCapability; @@ -92,8 +93,9 @@ public final class CapabilityChangeRequest implements Parcelable { /** * @return the stored radio technology, defined as - * {@link ImsRegistrationImplBase#REGISTRATION_TECH_LTE} or - * {@link ImsRegistrationImplBase#REGISTRATION_TECH_IWLAN} + * {@link ImsRegistrationImplBase#REGISTRATION_TECH_LTE}, + * {@link ImsRegistrationImplBase#REGISTRATION_TECH_IWLAN} or + * {@link ImsRegistrationImplBase#REGISTRATION_TECH_CROSS_SIM} */ public @ImsRegistrationImplBase.ImsRegistrationTech int getRadioTech() { return radioTech; diff --git a/telephony/java/android/telephony/ims/feature/ImsFeature.java b/telephony/java/android/telephony/ims/feature/ImsFeature.java index 96ca0225040f..b56aa9687aee 100644 --- a/telephony/java/android/telephony/ims/feature/ImsFeature.java +++ b/telephony/java/android/telephony/ims/feature/ImsFeature.java @@ -199,8 +199,9 @@ public abstract class ImsFeature { * {@link MmTelFeature.MmTelCapabilities#CAPABILITY_TYPE_UT}, or * {@link MmTelFeature.MmTelCapabilities#CAPABILITY_TYPE_SMS}. * @param radioTech The radio tech that this capability failed for, defined as - * {@link ImsRegistrationImplBase#REGISTRATION_TECH_LTE} or - * {@link ImsRegistrationImplBase#REGISTRATION_TECH_IWLAN}. + * {@link ImsRegistrationImplBase#REGISTRATION_TECH_LTE}, + * {@link ImsRegistrationImplBase#REGISTRATION_TECH_IWLAN} or + * {@link ImsRegistrationImplBase#REGISTRATION_TECH_CROSS_SIM}. * @param reason The reason this capability was unable to be changed, defined as * {@link #CAPABILITY_ERROR_GENERIC} or {@link #CAPABILITY_SUCCESS}. */ @@ -336,7 +337,7 @@ public abstract class ImsFeature { /** * @hide */ - public final void initialize(Context context, int slotId) { + public void initialize(Context context, int slotId) { mContext = context; mSlotId = slotId; } diff --git a/telephony/java/android/telephony/ims/feature/RcsFeature.java b/telephony/java/android/telephony/ims/feature/RcsFeature.java index cde7067e8bf3..22df921c4214 100644 --- a/telephony/java/android/telephony/ims/feature/RcsFeature.java +++ b/telephony/java/android/telephony/ims/feature/RcsFeature.java @@ -21,9 +21,11 @@ import android.annotation.IntDef; import android.annotation.NonNull; import android.annotation.Nullable; import android.annotation.SystemApi; +import android.content.Context; import android.net.Uri; import android.os.RemoteException; import android.telephony.ims.RcsUceAdapter; +import android.telephony.ims.aidl.CapabilityExchangeAidlWrapper; import android.telephony.ims.aidl.ICapabilityExchangeEventListener; import android.telephony.ims.aidl.IImsCapabilityCallback; import android.telephony.ims.aidl.IImsRcsFeature; @@ -33,6 +35,7 @@ import android.telephony.ims.aidl.ISubscribeResponseCallback; import android.telephony.ims.aidl.RcsOptionsResponseAidlWrapper; import android.telephony.ims.aidl.RcsPublishResponseAidlWrapper; import android.telephony.ims.aidl.RcsSubscribeResponseAidlWrapper; +import android.telephony.ims.stub.CapabilityExchangeEventListener; import android.telephony.ims.stub.ImsRegistrationImplBase; import android.telephony.ims.stub.RcsCapabilityExchangeImplBase; import android.telephony.ims.stub.RcsCapabilityExchangeImplBase.OptionsResponseCallback; @@ -114,8 +117,10 @@ public class RcsFeature extends ImsFeature { @Override public void setCapabilityExchangeEventListener( @Nullable ICapabilityExchangeEventListener listener) throws RemoteException { - executeMethodAsync(() -> mReference.setCapabilityExchangeEventListener(listener), - "setCapabilityExchangeEventListener"); + CapabilityExchangeEventListener listenerWrapper = + new CapabilityExchangeAidlWrapper(listener); + executeMethodAsync(() -> mReference.setCapabilityExchangeEventListener( + mExecutor, listenerWrapper), "setCapabilityExchangeEventListener"); } @Override @@ -245,9 +250,10 @@ public class RcsFeature extends ImsFeature { } } + private final Executor mExecutor; private final RcsFeatureBinder mImsRcsBinder; private RcsCapabilityExchangeImplBase mCapabilityExchangeImpl; - private ICapabilityExchangeEventListener mCapExchangeEventListener; + private CapabilityExchangeEventListener mCapExchangeEventListener; /** * Create a new RcsFeature. @@ -255,26 +261,45 @@ public class RcsFeature extends ImsFeature { * Method stubs called from the framework will be called asynchronously. To specify the * {@link Executor} that the methods stubs will be called, use * {@link RcsFeature#RcsFeature(Executor)} instead. + * + * @deprecated Use {@link #RcsFeature(Executor)} to create the RcsFeature. */ + @Deprecated public RcsFeature() { super(); + mExecutor = Runnable::run; // Run on the Binder threads that call them. - mImsRcsBinder = new RcsFeatureBinder(this, Runnable::run); + mImsRcsBinder = new RcsFeatureBinder(this, mExecutor); } /** * Create a new RcsFeature using the Executor specified for methods being called by the * framework. - * @param executor The executor for the framework to use when making calls to this service. - * @hide + * @param executor The executor for the framework to use when executing the methods overridden + * by the implementation of RcsFeature. */ public RcsFeature(@NonNull Executor executor) { super(); if (executor == null) { throw new IllegalArgumentException("executor can not be null."); } + mExecutor = executor; // Run on the Binder thread by default. - mImsRcsBinder = new RcsFeatureBinder(this, executor); + mImsRcsBinder = new RcsFeatureBinder(this, mExecutor); + } + + /** + * Called when the RcsFeature is initialized. + * + * @param context The context that is used in the ImsService. + * @param slotId The slot ID associated with the RcsFeature. + * @hide + */ + @Override + public void initialize(Context context, int slotId) { + super.initialize(context, slotId); + // Notify that the RcsFeature is ready. + mExecutor.execute(() -> onFeatureReady()); } /** @@ -348,13 +373,26 @@ public class RcsFeature extends ImsFeature { * operation and the RcsFeature sets the status of the capability to true using * {@link #notifyCapabilitiesStatusChanged(RcsImsCapabilities)}. * - * @return An instance of {@link RcsCapabilityExchangeImplBase} that implements presence + * @param executor The executor for the framework to use when request RCS resquests to this + * service. + * @param listener A {@link CapabilityExchangeEventListener} to send the capability exchange + * event to the framework. + * @return An instance of {@link RcsCapabilityExchangeImplBase} that implements capability * exchange if it is supported by the device. - * @hide */ - public @NonNull RcsCapabilityExchangeImplBase createCapabilityExchangeImpl() { + public @NonNull RcsCapabilityExchangeImplBase createCapabilityExchangeImpl( + @NonNull Executor executor, @NonNull CapabilityExchangeEventListener listener) { // Base Implementation, override to implement functionality - return new RcsCapabilityExchangeImplBase(); + return new RcsCapabilityExchangeImplBase(executor); + } + + /** + * Remove the given CapabilityExchangeImplBase instance. + * @param capExchangeImpl The {@link RcsCapabilityExchangeImplBase} instance to be removed. + */ + public void removeCapabilityExchangeImpl( + @NonNull RcsCapabilityExchangeImplBase capExchangeImpl) { + // Override to implement the process of removing RcsCapabilityExchangeImplBase instance. } /**{@inheritDoc}*/ @@ -377,18 +415,58 @@ public class RcsFeature extends ImsFeature { return mImsRcsBinder; } - private void setCapabilityExchangeEventListener(ICapabilityExchangeEventListener listener) { - mCapExchangeEventListener = listener; - if (mCapExchangeEventListener != null) { - onFeatureReady(); + /** + * Set the capability exchange listener. + * @param executor The executor for the framework to use when request RCS requests to this + * service. + * @param listener A {@link CapabilityExchangeEventListener} to send the capability exchange + * event to the framework. + */ + private void setCapabilityExchangeEventListener(@NonNull Executor executor, + @Nullable CapabilityExchangeEventListener listener) { + synchronized (mLock) { + mCapExchangeEventListener = listener; + if (mCapExchangeEventListener != null) { + initRcsCapabilityExchangeImplBase(executor, mCapExchangeEventListener); + } else { + // Remove the RcsCapabilityExchangeImplBase instance when the capability exchange + // instance has been removed in the framework. + if (mCapabilityExchangeImpl != null) { + removeCapabilityExchangeImpl(mCapabilityExchangeImpl); + } + mCapabilityExchangeImpl = null; + } } } - private RcsCapabilityExchangeImplBase getCapabilityExchangeImplBaseInternal() { + /** + * Initialize the RcsCapabilityExchangeImplBase instance if the capability exchange instance + * has already been created in the framework. + * @param executor The executor for the framework to use when request RCS requests to this + * service. + * @param listener A {@link CapabilityExchangeEventListener} to send the capability exchange + * event to the framework. + */ + private void initRcsCapabilityExchangeImplBase(@NonNull Executor executor, + @NonNull CapabilityExchangeEventListener listener) { + synchronized (mLock) { + // Remove the original instance + if (mCapabilityExchangeImpl != null) { + removeCapabilityExchangeImpl(mCapabilityExchangeImpl); + } + mCapabilityExchangeImpl = createCapabilityExchangeImpl(executor, listener); + } + } + + /** + * @return the {@link RcsCapabilityExchangeImplBase} associated with the RcsFeature. + */ + private @NonNull RcsCapabilityExchangeImplBase getCapabilityExchangeImplBaseInternal() { synchronized (mLock) { + // The method should not be called if the instance of RcsCapabilityExchangeImplBase has + // not been created yet. if (mCapabilityExchangeImpl == null) { - mCapabilityExchangeImpl = createCapabilityExchangeImpl(); - mCapabilityExchangeImpl.setEventListener(mCapExchangeEventListener); + throw new IllegalStateException("Session is not available."); } return mCapabilityExchangeImpl; } diff --git a/telephony/java/android/telephony/ims/stub/CapabilityExchangeEventListener.java b/telephony/java/android/telephony/ims/stub/CapabilityExchangeEventListener.java new file mode 100644 index 000000000000..d2cb9761a028 --- /dev/null +++ b/telephony/java/android/telephony/ims/stub/CapabilityExchangeEventListener.java @@ -0,0 +1,108 @@ +/* + * Copyright (C) 2020 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.telephony.ims.stub; + +import android.annotation.NonNull; +import android.annotation.SystemApi; +import android.net.Uri; +import android.telephony.ims.ImsException; +import android.telephony.ims.RcsContactUceCapability; +import android.telephony.ims.RcsUceAdapter; +import android.telephony.ims.feature.ImsFeature; +import android.telephony.ims.feature.RcsFeature; + +import java.util.List; + +/** + * The interface of the capabilities event listener for ImsService to notify the framework of the + * UCE request and status updated. + * @hide + */ +@SystemApi +public interface CapabilityExchangeEventListener { + /** + * Interface used by the framework to respond to OPTIONS requests. + * @hide + */ + interface OptionsRequestCallback { + /** + * Respond to a remote capability request from the contact specified with the + * capabilities of this device. + * @param ownCapabilities The capabilities of this device. + */ + void onRespondToCapabilityRequest(@NonNull RcsContactUceCapability ownCapabilities); + + /** + * Respond to a remote capability request from the contact specified with the + * specified error. + * @param code The SIP response code to respond with. + * @param reason A non-null String containing the reason associated with the SIP code. + */ + void onRespondToCapabilityRequestWithError(int code, @NonNull String reason); + } + + /** + * Trigger the framework to provide a capability update using + * {@link RcsCapabilityExchangeImplBase#publishCapabilities}. + * <p> + * This is typically used when trying to generate an initial PUBLISH for a new subscription to + * the network. The device will cache all presence publications after boot until this method is + * called the first time. + * @param publishTriggerType {@link RcsUceAdapter#StackPublishTriggerType} The reason for the + * capability update request. + * @throws ImsException If this {@link RcsCapabilityExchangeImplBase} instance is not currently + * connected to the framework. This can happen if the {@link RcsFeature} is not + * {@link ImsFeature#STATE_READY} and the {@link RcsFeature} has not received the + * {@link ImsFeature#onFeatureReady()} callback. This may also happen in rare cases when the + * Telephony stack has crashed. + */ + void onRequestPublishCapabilities( + @RcsUceAdapter.StackPublishTriggerType int publishTriggerType) throws ImsException; + + /** + * Notify the framework that the device's capabilities have been unpublished + * from the network. + * + * @throws ImsException If this {@link RcsCapabilityExchangeImplBase} instance is not currently + * connected to the framework. This can happen if the {@link RcsFeature} is not + * {@link ImsFeature#STATE_READY} and the {@link RcsFeature} has not received the + * {@link ImsFeature#onFeatureReady()} callback. This may also happen in rare cases when the + * Telephony stack has crashed. + */ + void onUnpublish() throws ImsException; + + /** + * Inform the framework of a query for this device's UCE capabilities. + * <p> + * The framework will respond via the + * {@link OptionsRequestCallback#onRespondToCapabilityRequest} or + * {@link OptionsRequestCallback#onRespondToCapabilityRequestWithError} + * @param contactUri The URI associated with the remote contact that is + * requesting capabilities. + * @param remoteCapabilities The remote contact's capability information. + * @param callback The callback of this request which is sent from the remote user. + * @throws ImsException If this {@link RcsCapabilityExchangeImplBase} instance is not + * currently connected to the framework. This can happen if the {@link RcsFeature} is not + * {@link ImsFeature#STATE_READY} and the {@link RcsFeature} has not received + * the {@link ImsFeature#onFeatureReady()} callback. This may also happen in rare + * cases when the Telephony stack has crashed. + * @hide + */ + void onRemoteCapabilityRequest(@NonNull Uri contactUri, + @NonNull List<String> remoteCapabilities, + @NonNull OptionsRequestCallback callback) throws ImsException; +} diff --git a/telephony/java/android/telephony/ims/stub/ImsRegistrationImplBase.java b/telephony/java/android/telephony/ims/stub/ImsRegistrationImplBase.java index 088a7e26a9d0..4f753c308f7e 100644 --- a/telephony/java/android/telephony/ims/stub/ImsRegistrationImplBase.java +++ b/telephony/java/android/telephony/ims/stub/ImsRegistrationImplBase.java @@ -55,7 +55,8 @@ public class ImsRegistrationImplBase { @IntDef(value = { REGISTRATION_TECH_NONE, REGISTRATION_TECH_LTE, - REGISTRATION_TECH_IWLAN + REGISTRATION_TECH_IWLAN, + REGISTRATION_TECH_CROSS_SIM }) @Retention(RetentionPolicy.SOURCE) public @interface ImsRegistrationTech {} @@ -72,6 +73,11 @@ public class ImsRegistrationImplBase { */ public static final int REGISTRATION_TECH_IWLAN = 1; + /** + * IMS is registered to IMS via internet over second subscription. + */ + public static final int REGISTRATION_TECH_CROSS_SIM = 2; + // Registration states, used to notify new ImsRegistrationImplBase#Callbacks of the current // state. // The unknown state is set as the initialization state. This is so that we do not call back @@ -196,7 +202,8 @@ public class ImsRegistrationImplBase { * Notify the framework that the device is connected to the IMS network. * * @param imsRadioTech the radio access technology. Valid values are defined as - * {@link #REGISTRATION_TECH_LTE} and {@link #REGISTRATION_TECH_IWLAN}. + * {@link #REGISTRATION_TECH_LTE}, {@link #REGISTRATION_TECH_IWLAN} and + * {@link #REGISTRATION_TECH_CROSS_SIM}. */ public final void onRegistered(@ImsRegistrationTech int imsRadioTech) { updateToState(imsRadioTech, RegistrationManager.REGISTRATION_STATE_REGISTERED); @@ -214,7 +221,8 @@ public class ImsRegistrationImplBase { * Notify the framework that the device is trying to connect the IMS network. * * @param imsRadioTech the radio access technology. Valid values are defined as - * {@link #REGISTRATION_TECH_LTE} and {@link #REGISTRATION_TECH_IWLAN}. + * {@link #REGISTRATION_TECH_LTE}, {@link #REGISTRATION_TECH_IWLAN} and + * {@link #REGISTRATION_TECH_CROSS_SIM}. */ public final void onRegistering(@ImsRegistrationTech int imsRadioTech) { updateToState(imsRadioTech, RegistrationManager.REGISTRATION_STATE_REGISTERING); @@ -262,7 +270,8 @@ public class ImsRegistrationImplBase { * Notify the framework that the handover from the current radio technology to the technology * defined in {@code imsRadioTech} has failed. * @param imsRadioTech The technology that has failed to be changed. Valid values are - * {@link #REGISTRATION_TECH_LTE} and {@link #REGISTRATION_TECH_IWLAN}. + * {@link #REGISTRATION_TECH_LTE}, {@link #REGISTRATION_TECH_IWLAN} and + * {@link #REGISTRATION_TECH_CROSS_SIM}. * @param info The {@link ImsReasonInfo} for the failure to change technology. */ public final void onTechnologyChangeFailed(@ImsRegistrationTech int imsRadioTech, @@ -329,7 +338,8 @@ public class ImsRegistrationImplBase { /** * @return the current registration connection type. Valid values are - * {@link #REGISTRATION_TECH_LTE} and {@link #REGISTRATION_TECH_IWLAN} + * {@link #REGISTRATION_TECH_LTE}, {@link #REGISTRATION_TECH_IWLAN} and + * {@link #REGISTRATION_TECH_CROSS_SIM}. * @hide */ @VisibleForTesting diff --git a/telephony/java/android/telephony/ims/stub/RcsCapabilityExchangeImplBase.java b/telephony/java/android/telephony/ims/stub/RcsCapabilityExchangeImplBase.java index 3a0fb6edb2fb..c84e23c38e97 100644 --- a/telephony/java/android/telephony/ims/stub/RcsCapabilityExchangeImplBase.java +++ b/telephony/java/android/telephony/ims/stub/RcsCapabilityExchangeImplBase.java @@ -20,20 +20,28 @@ import android.annotation.IntDef; import android.annotation.IntRange; import android.annotation.NonNull; import android.annotation.Nullable; +import android.annotation.SuppressLint; +import android.annotation.SystemApi; import android.net.Uri; import android.telephony.ims.ImsException; -import android.telephony.ims.aidl.ICapabilityExchangeEventListener; +import android.telephony.ims.feature.ImsFeature; +import android.telephony.ims.feature.RcsFeature; import android.util.Log; import android.util.Pair; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; import java.util.List; +import java.util.concurrent.Executor; /** - * Base class for different types of Capability exchange. + * Extend this base class to implement RCS User Capability Exchange (UCE) for the AOSP platform + * using the vendor ImsService. + * <p> + * See RCC.07 for more details on UCE as well as how UCE should be implemented. * @hide */ +@SystemApi public class RcsCapabilityExchangeImplBase { private static final String LOG_TAG = "RcsCapExchangeImplBase"; @@ -70,13 +78,11 @@ public class RcsCapabilityExchangeImplBase { /** * Network connection is lost. - * @hide */ public static final int COMMAND_CODE_LOST_NETWORK_CONNECTION = 6; /** * Requested feature/resource is not supported. - * @hide */ public static final int COMMAND_CODE_NOT_SUPPORTED = 7; @@ -117,7 +123,8 @@ public class RcsCapabilityExchangeImplBase { */ public interface PublishResponseCallback { /** - * Notify the framework that the command associated with this callback has failed. + * Notify the framework that the command associated with the + * {@link #publishCapabilities(String, PublishResponseCallback)} has failed. * * @param code The reason why the associated command has failed. * @throws ImsException If this {@link RcsCapabilityExchangeImplBase} instance is @@ -128,15 +135,15 @@ public class RcsCapabilityExchangeImplBase { */ void onCommandError(@CommandCode int code) throws ImsException; - /** * Provide the framework with a subsequent network response update to * {@link #publishCapabilities(String, PublishResponseCallback)}. * * @param code The SIP response code sent from the network for the operation * token specified. - * @param reason The optional reason response from the network. If the network - * provided no reason with the code, the string should be empty. + * @param reason The optional reason response from the network. If there is a reason header + * included in the response, that should take precedence over the reason provided in the + * status line. If the network provided no reason with the code, the string should be empty. * @throws ImsException If this {@link RcsCapabilityExchangeImplBase} instance is * not currently connected to the framework. This can happen if the {@link RcsFeature} * is not {@link ImsFeature#STATE_READY} and the {@link RcsFeature} has not received @@ -149,6 +156,7 @@ public class RcsCapabilityExchangeImplBase { /** * Interface used by the framework to respond to OPTIONS requests. + * @hide */ public interface OptionsResponseCallback { /** @@ -171,7 +179,7 @@ public class RcsCapabilityExchangeImplBase { * If none was sent, this should be an empty string. * @param theirCaps the contact's UCE capabilities associated with the * capability request. - * @throws ImsException If this {@link RcsSipOptionsImplBase} instance is not + * @throws ImsException If this {@link RcsCapabilityExchangeImplBase} instance is not * currently connected to the framework. This can happen if the * {@link RcsFeature} is not {@link ImsFeature#STATE_READY} and the * {@link RcsFeature} has not received the @@ -184,6 +192,7 @@ public class RcsCapabilityExchangeImplBase { /** * Interface used by the framework to receive the response of the subscribe request. + * @hide */ public interface SubscribeResponseCallback { /** @@ -219,17 +228,16 @@ public class RcsCapabilityExchangeImplBase { /** * Provides the framework with latest XML PIDF documents included in the * network response for the requested contacts' capabilities requested by the - * Framework using {@link #requestCapabilities(List, int)}. This should be + * Framework using {@link #requestCapabilities(List, int)}. This should be * called every time a new NOTIFY event is received with new capability * information. * * @throws ImsException If this {@link RcsCapabilityExchangeImplBase} instance is - * not currently - * connected to the framework. This can happen if the {@link RcsFeature} is not - * {@link ImsFeature#STATE_READY} and the {@link RcsFeature} has not received - * the {@link ImsFeature#onFeatureReady()} callback. This may also happen in - * rare cases when the - * Telephony stack has crashed. + * not currently connected to the framework. + * This can happen if the {@link RcsFeature} is not {@link ImsFeature#STATE_READY} and the + * {@link RcsFeature} {@link ImsFeature#STATE_READY} and the {@link RcsFeature} has not + * received the {@link ImsFeature#onFeatureReady()} callback. This may also happen in + * rare cases when the Telephony stack has crashed. */ void onNotifyCapabilitiesUpdate(@NonNull List<String> pidfXmls) throws ImsException; @@ -250,24 +258,21 @@ public class RcsCapabilityExchangeImplBase { * This allows the framework to know that there will no longer be any * capability updates for the requested operationToken. */ - void onTerminated(String reason, long retryAfterMilliseconds) throws ImsException; + void onTerminated(@NonNull String reason, long retryAfterMilliseconds) throws ImsException; } - - private ICapabilityExchangeEventListener mListener; + private final Executor mBinderExecutor; /** - * Set the event listener to send the request to Framework. + * Create a new RcsCapabilityExchangeImplBase instance. + * + * @param executor The executor that remote calls from the framework will be called on. */ - public void setEventListener(ICapabilityExchangeEventListener listener) { - mListener = listener; - } - - /** - * Get the event listener. - */ - public ICapabilityExchangeEventListener getEventListener() { - return mListener; + public RcsCapabilityExchangeImplBase(@NonNull Executor executor) { + if (executor == null) { + throw new IllegalArgumentException("executor must not be null"); + } + mBinderExecutor = executor; } /** @@ -284,7 +289,10 @@ public class RcsCapabilityExchangeImplBase { * @param uris A {@link List} of the {@link Uri}s that the framework is requesting the UCE * capabilities for. * @param cb The callback of the subscribe request. + * @hide */ + // executor used is defined in the constructor. + @SuppressLint("ExecutorRegistration") public void subscribeForCapabilities(@NonNull List<Uri> uris, @NonNull SubscribeResponseCallback cb) { // Stub - to be implemented by service @@ -300,11 +308,13 @@ public class RcsCapabilityExchangeImplBase { * The capabilities of this device have been updated and should be published to the network. * <p> * If this operation succeeds, network response updates should be sent to the framework using - * {@link #onNetworkResponse(int, String)}. + * {@link PublishResponseCallback#onNetworkResponse(int, String)}. * @param pidfXml The XML PIDF document containing the capabilities of this device to be sent * to the carrier’s presence server. * @param cb The callback of the publish request */ + // executor used is defined in the constructor. + @SuppressLint("ExecutorRegistration") public void publishCapabilities(@NonNull String pidfXml, @NonNull PublishResponseCallback cb) { // Stub - to be implemented by service Log.w(LOG_TAG, "publishCapabilities called with no implementation."); @@ -324,7 +334,10 @@ public class RcsCapabilityExchangeImplBase { * @param contactUri The URI of the remote user that we wish to get the capabilities of. * @param myCapabilities The capabilities of this device to send to the remote user. * @param callback The callback of this request which is sent from the remote user. + * @hide */ + // executor used is defined in the constructor. + @SuppressLint("ExecutorRegistration") public void sendOptionsCapabilityRequest(@NonNull Uri contactUri, @NonNull List<String> myCapabilities, @NonNull OptionsResponseCallback callback) { // Stub - to be implemented by service diff --git a/telephony/java/com/android/internal/telephony/ITelephony.aidl b/telephony/java/com/android/internal/telephony/ITelephony.aidl index 74753ca1560a..83fb38be820d 100644 --- a/telephony/java/com/android/internal/telephony/ITelephony.aidl +++ b/telephony/java/com/android/internal/telephony/ITelephony.aidl @@ -629,7 +629,7 @@ interface ITelephony { * successful iccOpenLogicalChannel. * @return true if the channel was closed successfully. */ - @UnsupportedAppUsage(maxTargetSdk = 30, trackingBug = 170729553) + @UnsupportedAppUsage(trackingBug = 171933273) boolean iccCloseLogicalChannel(int subId, int channel); /** @@ -671,7 +671,7 @@ interface ITelephony { * @return The APDU response from the ICC card with the status appended at * the end. */ - @UnsupportedAppUsage(maxTargetSdk = 30, trackingBug = 170729553) + @UnsupportedAppUsage(trackingBug = 171933273) String iccTransmitApduLogicalChannel(int subId, int channel, int cla, int instruction, int p1, int p2, int p3, String data); @@ -1261,6 +1261,9 @@ interface ITelephony { */ int getRadioAccessFamily(in int phoneId, String callingPackage); + void uploadCallComposerPicture(int subscriptionId, String callingPackage, + in ParcelFileDescriptor fd, in ResultReceiver callback); + /** * Enables or disables video calling. * @@ -1964,6 +1967,16 @@ interface ITelephony { void setVoWiFiSettingEnabled(int subId, boolean isEnabled); /** + * return true if the user's setting for Voice over Cross SIM is enabled and false if it is not + */ + boolean isCrossSimCallingEnabledByUser(int subId); + + /** + * Sets the user's setting for whether or not Voice over Cross SIM is enabled. + */ + void setCrossSimCallingEnabled(int subId, boolean isEnabled); + + /** * return true if the user's setting for Voice over WiFi while roaming is enabled. */ boolean isVoWiFiRoamingSettingEnabled(int subId); diff --git a/tests/net/common/Android.bp b/tests/net/common/Android.bp index 373aac604b2a..c271f49ee537 100644 --- a/tests/net/common/Android.bp +++ b/tests/net/common/Android.bp @@ -24,6 +24,7 @@ java_library { "androidx.test.rules", "junit", "mockito-target-minus-junit4", + "modules-utils-build", "net-tests-utils", "net-utils-framework-common", "platform-test-annotations", diff --git a/tests/net/common/java/android/net/CaptivePortalDataTest.kt b/tests/net/common/java/android/net/CaptivePortalDataTest.kt index bd1847b7c440..8710d23730b6 100644 --- a/tests/net/common/java/android/net/CaptivePortalDataTest.kt +++ b/tests/net/common/java/android/net/CaptivePortalDataTest.kt @@ -41,13 +41,14 @@ class CaptivePortalDataTest { .setBytesRemaining(456L) .setExpiryTime(789L) .setCaptive(true) + .setVenueFriendlyName("venue friendly name") .build() private fun makeBuilder() = CaptivePortalData.Builder(data) @Test fun testParcelUnparcel() { - assertParcelSane(data, fieldCount = 7) + assertParcelSane(data, fieldCount = 8) assertParcelingIsLossless(makeBuilder().setUserPortalUrl(null).build()) assertParcelingIsLossless(makeBuilder().setVenueInfoUrl(null).build()) @@ -66,6 +67,8 @@ class CaptivePortalDataTest { assertNotEqualsAfterChange { it.setBytesRemaining(789L) } assertNotEqualsAfterChange { it.setExpiryTime(12L) } assertNotEqualsAfterChange { it.setCaptive(false) } + assertNotEqualsAfterChange { it.setVenueFriendlyName("another friendly name") } + assertNotEqualsAfterChange { it.setVenueFriendlyName(null) } } @Test @@ -108,6 +111,11 @@ class CaptivePortalDataTest { assertFalse(makeBuilder().setCaptive(false).build().isCaptive) } + @Test + fun testVenueFriendlyName() { + assertEquals("venue friendly name", data.venueFriendlyName) + } + private fun CaptivePortalData.mutate(mutator: (CaptivePortalData.Builder) -> Unit) = CaptivePortalData.Builder(this).apply { mutator(this) }.build() diff --git a/tests/net/common/java/android/net/NetworkCapabilitiesTest.java b/tests/net/common/java/android/net/NetworkCapabilitiesTest.java index 6b7ea66df233..5d0e016d50fa 100644 --- a/tests/net/common/java/android/net/NetworkCapabilitiesTest.java +++ b/tests/net/common/java/android/net/NetworkCapabilitiesTest.java @@ -42,9 +42,11 @@ import static android.net.NetworkCapabilities.TRANSPORT_VPN; import static android.net.NetworkCapabilities.TRANSPORT_WIFI; import static android.net.NetworkCapabilities.TRANSPORT_WIFI_AWARE; import static android.net.NetworkCapabilities.UNRESTRICTED_CAPABILITIES; +import static android.os.Process.INVALID_UID; import static com.android.testutils.ParcelUtils.assertParcelSane; import static com.android.testutils.ParcelUtils.assertParcelingIsLossless; +import static com.android.testutils.ParcelUtils.parcelingRoundTrip; import static org.junit.Assert.assertArrayEquals; import static org.junit.Assert.assertEquals; @@ -53,18 +55,19 @@ import static org.junit.Assert.assertNotEquals; import static org.junit.Assert.assertNull; import static org.junit.Assert.assertTrue; import static org.junit.Assert.fail; +import static org.junit.Assume.assumeTrue; +import android.net.wifi.WifiInfo; import android.net.wifi.aware.DiscoverySession; import android.net.wifi.aware.PeerHandle; import android.net.wifi.aware.WifiAwareNetworkSpecifier; import android.os.Build; -import android.os.Process; import android.test.suitebuilder.annotation.SmallTest; import android.util.ArraySet; -import androidx.core.os.BuildCompat; import androidx.test.runner.AndroidJUnit4; +import com.android.modules.utils.build.SdkLevel; import com.android.testutils.DevSdkIgnoreRule; import com.android.testutils.DevSdkIgnoreRule.IgnoreUpTo; @@ -89,10 +92,11 @@ public class NetworkCapabilitiesTest { private PeerHandle mPeerHandle = Mockito.mock(PeerHandle.class); private boolean isAtLeastR() { - // BuildCompat.isAtLeastR() is used to check the Android version before releasing Android R. - // Build.VERSION.SDK_INT > Build.VERSION_CODES.Q is used to check the Android version after - // releasing Android R. - return BuildCompat.isAtLeastR() || Build.VERSION.SDK_INT > Build.VERSION_CODES.Q; + return SdkLevel.isAtLeastR(); + } + + private boolean isAtLeastS() { + return SdkLevel.isAtLeastS(); } @Test @@ -324,8 +328,59 @@ public class NetworkCapabilitiesTest { testParcelSane(netCap); } + private NetworkCapabilities createNetworkCapabilitiesWithWifiInfo() { + // uses a real WifiInfo to test parceling of sensitive data. + final WifiInfo wifiInfo = new WifiInfo.Builder() + .setSsid("sssid1234".getBytes()) + .setBssid("00:11:22:33:44:55") + .build(); + return new NetworkCapabilities() + .addCapability(NET_CAPABILITY_INTERNET) + .addCapability(NET_CAPABILITY_EIMS) + .addCapability(NET_CAPABILITY_NOT_METERED) + .setSSID(TEST_SSID) + .setTransportInfo(wifiInfo) + .setRequestorPackageName("com.android.test") + .setRequestorUid(9304); + } + + @Test + public void testParcelNetworkCapabilitiesWithLocationSensitiveFields() { + assumeTrue(isAtLeastS()); + + final NetworkCapabilities netCap = createNetworkCapabilitiesWithWifiInfo(); + final NetworkCapabilities netCapWithLocationSensitiveFields = + new NetworkCapabilities(netCap, true); + + assertParcelingIsLossless(netCapWithLocationSensitiveFields); + testParcelSane(netCapWithLocationSensitiveFields); + + assertEquals(netCapWithLocationSensitiveFields, + parcelingRoundTrip(netCapWithLocationSensitiveFields)); + } + + @Test + public void testParcelNetworkCapabilitiesWithoutLocationSensitiveFields() { + assumeTrue(isAtLeastS()); + + final NetworkCapabilities netCap = createNetworkCapabilitiesWithWifiInfo(); + final NetworkCapabilities netCapWithoutLocationSensitiveFields = + new NetworkCapabilities(netCap, false); + + final NetworkCapabilities sanitizedNetCap = + new NetworkCapabilities(netCapWithoutLocationSensitiveFields); + final WifiInfo sanitizedWifiInfo = new WifiInfo.Builder() + .setSsid(new byte[0]) + .setBssid(WifiInfo.DEFAULT_MAC_ADDRESS) + .build(); + sanitizedNetCap.setTransportInfo(sanitizedWifiInfo); + assertEquals(sanitizedNetCap, parcelingRoundTrip(netCapWithoutLocationSensitiveFields)); + } + private void testParcelSane(NetworkCapabilities cap) { - if (isAtLeastR()) { + if (isAtLeastS()) { + assertParcelSane(cap, 16); + } else if (isAtLeastR()) { assertParcelSane(cap, 15); } else { assertParcelSane(cap, 11); @@ -639,26 +694,23 @@ public class NetworkCapabilitiesTest { // Sequence 1: Transport + Transport + TransportInfo NetworkCapabilities nc1 = new NetworkCapabilities(); nc1.addTransportType(TRANSPORT_CELLULAR).addTransportType(TRANSPORT_WIFI) - .setTransportInfo(new TransportInfo() {}); + .setTransportInfo(new TestTransportInfo()); // Sequence 2: Transport + NetworkSpecifier + Transport NetworkCapabilities nc2 = new NetworkCapabilities(); - nc2.addTransportType(TRANSPORT_CELLULAR).setTransportInfo(new TransportInfo() {}) + nc2.addTransportType(TRANSPORT_CELLULAR).setTransportInfo(new TestTransportInfo()) .addTransportType(TRANSPORT_WIFI); } @Test public void testCombineTransportInfo() { NetworkCapabilities nc1 = new NetworkCapabilities(); - nc1.setTransportInfo(new TransportInfo() { - // empty - }); + nc1.setTransportInfo(new TestTransportInfo()); + NetworkCapabilities nc2 = new NetworkCapabilities(); // new TransportInfo so that object is not #equals to nc1's TransportInfo (that's where // combine fails) - nc2.setTransportInfo(new TransportInfo() { - // empty - }); + nc2.setTransportInfo(new TestTransportInfo()); try { nc1.combineCapabilities(nc2); @@ -761,7 +813,7 @@ public class NetworkCapabilitiesTest { // Test default owner uid. // If the owner uid is not set, the default value should be Process.INVALID_UID. final NetworkCapabilities nc1 = new NetworkCapabilities.Builder().build(); - assertEquals(Process.INVALID_UID, nc1.getOwnerUid()); + assertEquals(INVALID_UID, nc1.getOwnerUid()); // Test setAdministratorUids and getAdministratorUids. final int[] administratorUids = {1001, 10001}; final NetworkCapabilities nc2 = new NetworkCapabilities.Builder() @@ -906,6 +958,16 @@ public class NetworkCapabilitiesTest { private class TestTransportInfo implements TransportInfo { TestTransportInfo() { } + + @Override + public TransportInfo makeCopy(boolean parcelLocationSensitiveFields) { + return this; + } + + @Override + public boolean hasLocationSensitiveFields() { + return false; + } } @Test @IgnoreUpTo(Build.VERSION_CODES.Q) diff --git a/tests/net/java/com/android/server/ConnectivityServiceTest.java b/tests/net/java/com/android/server/ConnectivityServiceTest.java index e318207cdc75..31d566d4bd47 100644 --- a/tests/net/java/com/android/server/ConnectivityServiceTest.java +++ b/tests/net/java/com/android/server/ConnectivityServiceTest.java @@ -63,6 +63,7 @@ import static android.net.NetworkCapabilities.NET_CAPABILITY_NOT_METERED; import static android.net.NetworkCapabilities.NET_CAPABILITY_NOT_RESTRICTED; import static android.net.NetworkCapabilities.NET_CAPABILITY_NOT_ROAMING; import static android.net.NetworkCapabilities.NET_CAPABILITY_NOT_SUSPENDED; +import static android.net.NetworkCapabilities.NET_CAPABILITY_NOT_VCN_MANAGED; import static android.net.NetworkCapabilities.NET_CAPABILITY_NOT_VPN; import static android.net.NetworkCapabilities.NET_CAPABILITY_PARTIAL_CONNECTIVITY; import static android.net.NetworkCapabilities.NET_CAPABILITY_RCS; @@ -161,7 +162,6 @@ import android.net.DataStallReportParcelable; import android.net.EthernetManager; import android.net.IConnectivityDiagnosticsCallback; import android.net.IDnsResolver; -import android.net.IIpConnectivityMetrics; import android.net.INetd; import android.net.INetworkMonitor; import android.net.INetworkMonitorCallbacks; @@ -202,6 +202,7 @@ import android.net.metrics.IpConnectivityLog; import android.net.shared.NetworkMonitorUtils; import android.net.shared.PrivateDnsConfig; import android.net.util.MultinetworkPolicyTracker; +import android.net.wifi.WifiInfo; import android.os.BadParcelableException; import android.os.Binder; import android.os.Build; @@ -344,6 +345,11 @@ public class ConnectivityServiceTest { private static final String INTERFACE_NAME = "interface"; + private static final String TEST_VENUE_URL_NA = "https://android.com/"; + private static final String TEST_VENUE_URL_CAPPORT = "https://android.com/capport/"; + private static final String TEST_FRIENDLY_NAME = "Network friendly name"; + private static final String TEST_REDIRECT_URL = "http://example.com/firstPath"; + private MockContext mServiceContext; private HandlerThread mCsHandlerThread; private ConnectivityService.Dependencies mDeps; @@ -359,7 +365,6 @@ public class ConnectivityServiceTest { private HandlerThread mAlarmManagerThread; private TestNetIdManager mNetIdManager; - @Mock IIpConnectivityMetrics mIpConnectivityMetrics; @Mock IpConnectivityMetrics.Logger mMetricsService; @Mock DefaultNetworkMetrics mDefaultNetworkMetrics; @Mock DeviceIdleInternal mDeviceIdleInternal; @@ -869,7 +874,7 @@ public class ConnectivityServiceTest { mProbesSucceeded = probesSucceeded; } - void notifyCaptivePortalDataChanged(CaptivePortalData data) { + void notifyCapportApiDataChanged(CaptivePortalData data) { try { mNmCallbacks.notifyCaptivePortalDataChanged(data); } catch (RemoteException e) { @@ -1373,7 +1378,6 @@ public class ConnectivityServiceTest { doReturn(mock(ProxyTracker.class)).when(deps).makeProxyTracker(any(), any()); doReturn(mMetricsService).when(deps).getMetricsLogger(); doReturn(true).when(deps).queryUserAccess(anyInt(), anyInt()); - doReturn(mIpConnectivityMetrics).when(deps).getIpConnectivityMetrics(); doReturn(mBatteryStatsService).when(deps).getBatteryStatsService(); doAnswer(inv -> { mPolicyTracker = new WrappedMultinetworkPolicyTracker( @@ -2007,7 +2011,7 @@ public class ConnectivityServiceTest { Objects.equals(expectedCapportUrl, lp.getCaptivePortalApiUrl())); final CaptivePortalData expectedCapportData = sanitized ? null : capportData; - mWiFiNetworkAgent.notifyCaptivePortalDataChanged(capportData); + mWiFiNetworkAgent.notifyCapportApiDataChanged(capportData); callback.expectLinkPropertiesThat(mWiFiNetworkAgent, lp -> Objects.equals(expectedCapportData, lp.getCaptivePortalData())); defaultCallback.expectLinkPropertiesThat(mWiFiNetworkAgent, lp -> @@ -3045,7 +3049,7 @@ public class ConnectivityServiceTest { .setBytesRemaining(12345L) .build(); - mWiFiNetworkAgent.notifyCaptivePortalDataChanged(testData); + mWiFiNetworkAgent.notifyCapportApiDataChanged(testData); captivePortalCallback.expectLinkPropertiesThat(mWiFiNetworkAgent, lp -> testData.equals(lp.getCaptivePortalData())); @@ -3058,6 +3062,136 @@ public class ConnectivityServiceTest { lp -> testData.equals(lp.getCaptivePortalData()) && lp.getMtu() == 1234); } + private TestNetworkCallback setupNetworkCallbackAndConnectToWifi() throws Exception { + // Grant NETWORK_SETTINGS permission to be able to receive LinkProperties change callbacks + // with sensitive (captive portal) data + mServiceContext.setPermission( + android.Manifest.permission.NETWORK_SETTINGS, PERMISSION_GRANTED); + + final TestNetworkCallback captivePortalCallback = new TestNetworkCallback(); + final NetworkRequest captivePortalRequest = new NetworkRequest.Builder() + .addCapability(NET_CAPABILITY_CAPTIVE_PORTAL).build(); + mCm.registerNetworkCallback(captivePortalRequest, captivePortalCallback); + + mWiFiNetworkAgent = new TestNetworkAgentWrapper(TRANSPORT_WIFI); + + mWiFiNetworkAgent.connectWithCaptivePortal(TEST_REDIRECT_URL, false /* isStrictMode */); + captivePortalCallback.expectAvailableCallbacksUnvalidated(mWiFiNetworkAgent); + return captivePortalCallback; + } + + private class CaptivePortalTestData { + CaptivePortalTestData(CaptivePortalData naData, CaptivePortalData capportData, + CaptivePortalData expectedMergedData) { + mNaData = naData; + mCapportData = capportData; + mExpectedMergedData = expectedMergedData; + } + + public final CaptivePortalData mNaData; + public final CaptivePortalData mCapportData; + public final CaptivePortalData mExpectedMergedData; + } + + private CaptivePortalTestData setupCaptivePortalData() { + final CaptivePortalData capportData = new CaptivePortalData.Builder() + .setUserPortalUrl(Uri.parse(TEST_REDIRECT_URL)) + .setVenueInfoUrl(Uri.parse(TEST_VENUE_URL_CAPPORT)) + .setExpiryTime(1000000L) + .setBytesRemaining(12345L) + .build(); + + final CaptivePortalData naData = new CaptivePortalData.Builder() + .setBytesRemaining(80802L) + .setVenueInfoUrl(Uri.parse(TEST_VENUE_URL_NA)) + .setVenueFriendlyName(TEST_FRIENDLY_NAME).build(); + + final CaptivePortalData expectedMergedData = new CaptivePortalData.Builder() + .setUserPortalUrl(Uri.parse(TEST_REDIRECT_URL)) + .setBytesRemaining(12345L) + .setExpiryTime(1000000L) + .setVenueInfoUrl(Uri.parse(TEST_VENUE_URL_NA)) + .setVenueFriendlyName(TEST_FRIENDLY_NAME).build(); + + return new CaptivePortalTestData(naData, capportData, expectedMergedData); + } + + @Test + public void testMergeCaptivePortalApiWithFriendlyNameAndVenueUrl() throws Exception { + final TestNetworkCallback captivePortalCallback = setupNetworkCallbackAndConnectToWifi(); + final CaptivePortalTestData captivePortalTestData = setupCaptivePortalData(); + + // Baseline capport data + mWiFiNetworkAgent.notifyCapportApiDataChanged(captivePortalTestData.mCapportData); + + captivePortalCallback.expectLinkPropertiesThat(mWiFiNetworkAgent, + lp -> captivePortalTestData.mCapportData.equals(lp.getCaptivePortalData())); + + // Venue URL and friendly name from Network agent, confirm that API data gets precedence + // on the bytes remaining. + final LinkProperties linkProperties = new LinkProperties(); + linkProperties.setCaptivePortalData(captivePortalTestData.mNaData); + mWiFiNetworkAgent.sendLinkProperties(linkProperties); + + // Make sure that the capport data is merged + captivePortalCallback.expectLinkPropertiesThat(mWiFiNetworkAgent, + lp -> captivePortalTestData.mExpectedMergedData.equals(lp.getCaptivePortalData())); + + // Create a new LP with no Network agent capport data + final LinkProperties newLps = new LinkProperties(); + newLps.setMtu(1234); + mWiFiNetworkAgent.sendLinkProperties(newLps); + // CaptivePortalData is not lost and has the original values when LPs are received from the + // NetworkAgent + captivePortalCallback.expectLinkPropertiesThat(mWiFiNetworkAgent, + lp -> captivePortalTestData.mCapportData.equals(lp.getCaptivePortalData()) + && lp.getMtu() == 1234); + + // Now send capport data only from the Network agent + mWiFiNetworkAgent.notifyCapportApiDataChanged(null); + captivePortalCallback.expectLinkPropertiesThat(mWiFiNetworkAgent, + lp -> lp.getCaptivePortalData() == null); + + newLps.setCaptivePortalData(captivePortalTestData.mNaData); + mWiFiNetworkAgent.sendLinkProperties(newLps); + + // Make sure that only the network agent capport data is available + captivePortalCallback.expectLinkPropertiesThat(mWiFiNetworkAgent, + lp -> captivePortalTestData.mNaData.equals(lp.getCaptivePortalData())); + } + + @Test + public void testMergeCaptivePortalDataFromNetworkAgentFirstThenCapport() throws Exception { + final TestNetworkCallback captivePortalCallback = setupNetworkCallbackAndConnectToWifi(); + final CaptivePortalTestData captivePortalTestData = setupCaptivePortalData(); + + // Venue URL and friendly name from Network agent, confirm that API data gets precedence + // on the bytes remaining. + final LinkProperties linkProperties = new LinkProperties(); + linkProperties.setCaptivePortalData(captivePortalTestData.mNaData); + mWiFiNetworkAgent.sendLinkProperties(linkProperties); + + // Make sure that the data is saved correctly + captivePortalCallback.expectLinkPropertiesThat(mWiFiNetworkAgent, + lp -> captivePortalTestData.mNaData.equals(lp.getCaptivePortalData())); + + // Expected merged data: Network agent data is preferred, and values that are not used by + // it are merged from capport data + mWiFiNetworkAgent.notifyCapportApiDataChanged(captivePortalTestData.mCapportData); + + // Make sure that the Capport data is merged correctly + captivePortalCallback.expectLinkPropertiesThat(mWiFiNetworkAgent, + lp -> captivePortalTestData.mExpectedMergedData.equals(lp.getCaptivePortalData())); + + // Now set the naData to null + linkProperties.setCaptivePortalData(null); + mWiFiNetworkAgent.sendLinkProperties(linkProperties); + + // Make sure that the Capport data is retained correctly + captivePortalCallback.expectLinkPropertiesThat(mWiFiNetworkAgent, + lp -> captivePortalTestData.mCapportData.equals(lp.getCaptivePortalData())); + } + private NetworkRequest.Builder newWifiRequestBuilder() { return new NetworkRequest.Builder().addTransportType(TRANSPORT_WIFI); } @@ -3363,6 +3497,7 @@ public class ConnectivityServiceTest { assertEquals(null, mCm.getActiveNetwork()); mMockVpn.establishForMyUid(); + assertUidRangesUpdatedForMyUid(true); defaultNetworkCallback.expectAvailableThenValidatedCallbacks(mMockVpn); assertEquals(defaultNetworkCallback.getLastAvailableNetwork(), mCm.getActiveNetwork()); @@ -5053,6 +5188,7 @@ public class ConnectivityServiceTest { lp.setInterfaceName(VPN_IFNAME); mMockVpn.establishForMyUid(lp); + assertUidRangesUpdatedForMyUid(true); final Network[] cellAndVpn = new Network[] { mCellNetworkAgent.getNetwork(), mMockVpn.getNetwork()}; @@ -5638,6 +5774,7 @@ public class ConnectivityServiceTest { // (and doing so is difficult without using reflection) but it's good to test that the code // behaves approximately correctly. mMockVpn.establishForMyUid(false, true, false); + assertUidRangesUpdatedForMyUid(true); final Network wifiNetwork = new Network(mNetIdManager.peekNextNetId()); mService.setUnderlyingNetworksForVpn(new Network[]{wifiNetwork}); callback.expectAvailableCallbacksUnvalidated(mMockVpn); @@ -5795,6 +5932,7 @@ public class ConnectivityServiceTest { mMockVpn.establishForMyUid(true /* validated */, false /* hasInternet */, false /* isStrictMode */); + assertUidRangesUpdatedForMyUid(true); defaultCallback.assertNoCallback(); assertEquals(defaultCallback.getLastAvailableNetwork(), mCm.getActiveNetwork()); @@ -5820,6 +5958,7 @@ public class ConnectivityServiceTest { mMockVpn.establishForMyUid(true /* validated */, true /* hasInternet */, false /* isStrictMode */); + assertUidRangesUpdatedForMyUid(true); defaultCallback.expectAvailableThenValidatedCallbacks(mMockVpn); assertEquals(defaultCallback.getLastAvailableNetwork(), mCm.getActiveNetwork()); @@ -5845,6 +5984,7 @@ public class ConnectivityServiceTest { // Bring up a VPN that has the INTERNET capability, initially unvalidated. mMockVpn.establishForMyUid(false /* validated */, true /* hasInternet */, false /* isStrictMode */); + assertUidRangesUpdatedForMyUid(true); // Even though the VPN is unvalidated, it becomes the default network for our app. callback.expectAvailableCallbacksUnvalidated(mMockVpn); @@ -5896,6 +6036,7 @@ public class ConnectivityServiceTest { mMockVpn.establishForMyUid(true /* validated */, false /* hasInternet */, false /* isStrictMode */); + assertUidRangesUpdatedForMyUid(true); vpnNetworkCallback.expectAvailableCallbacks(mMockVpn.getNetwork(), false /* suspended */, false /* validated */, false /* blocked */, TIMEOUT_MS); @@ -5937,6 +6078,7 @@ public class ConnectivityServiceTest { mMockVpn.establishForMyUid(true /* validated */, false /* hasInternet */, false /* isStrictMode */); + assertUidRangesUpdatedForMyUid(true); vpnNetworkCallback.expectAvailableThenValidatedCallbacks(mMockVpn); nc = mCm.getNetworkCapabilities(mMockVpn.getNetwork()); @@ -6104,6 +6246,7 @@ public class ConnectivityServiceTest { mMockVpn.establishForMyUid(true /* validated */, false /* hasInternet */, false /* isStrictMode */); + assertUidRangesUpdatedForMyUid(true); vpnNetworkCallback.expectAvailableThenValidatedCallbacks(mMockVpn); nc = mCm.getNetworkCapabilities(mMockVpn.getNetwork()); @@ -6162,6 +6305,7 @@ public class ConnectivityServiceTest { // Bring up a VPN mMockVpn.establishForMyUid(); + assertUidRangesUpdatedForMyUid(true); callback.expectAvailableThenValidatedCallbacks(mMockVpn); callback.assertNoCallback(); @@ -6316,6 +6460,7 @@ public class ConnectivityServiceTest { // Connect VPN network. By default it is using current default network (Cell). mMockVpn.establishForMyUid(); + assertUidRangesUpdatedForMyUid(true); // Ensure VPN is now the active network. assertEquals(mMockVpn.getNetwork(), mCm.getActiveNetwork()); @@ -6368,6 +6513,7 @@ public class ConnectivityServiceTest { // Connect VPN network. mMockVpn.establishForMyUid(); + assertUidRangesUpdatedForMyUid(true); // Ensure VPN is now the active network. assertEquals(mMockVpn.getNetwork(), mCm.getActiveNetwork()); @@ -6742,6 +6888,7 @@ public class ConnectivityServiceTest { assertNetworkInfo(TYPE_WIFI, DetailedState.BLOCKED); mMockVpn.establishForMyUid(); + assertUidRangesUpdatedForMyUid(true); defaultCallback.expectAvailableThenValidatedCallbacks(mMockVpn); vpnUidCallback.assertNoCallback(); // vpnUidCallback has NOT_VPN capability. assertEquals(mMockVpn.getNetwork(), mCm.getActiveNetwork()); @@ -6761,36 +6908,58 @@ public class ConnectivityServiceTest { mCm.unregisterNetworkCallback(vpnUidCallback); } + /** + * Test mutable and requestable network capabilities such as + * {@link NetworkCapabilities#NET_CAPABILITY_TRUSTED} and + * {@link NetworkCapabilities#NET_CAPABILITY_NOT_VCN_MANAGED}. Verify that the + * {@code ConnectivityService} re-assign the networks accordingly. + */ @Test - public final void testLoseTrusted() throws Exception { - final NetworkRequest trustedRequest = new NetworkRequest.Builder() - .addCapability(NET_CAPABILITY_TRUSTED) - .build(); - final TestNetworkCallback trustedCallback = new TestNetworkCallback(); - mCm.requestNetwork(trustedRequest, trustedCallback); - - mCellNetworkAgent = new TestNetworkAgentWrapper(TRANSPORT_CELLULAR); - mCellNetworkAgent.connect(true); - trustedCallback.expectAvailableThenValidatedCallbacks(mCellNetworkAgent); - verify(mMockNetd).networkSetDefault(eq(mCellNetworkAgent.getNetwork().netId)); - reset(mMockNetd); + public final void testLoseMutableAndRequestableCaps() throws Exception { + final int[] testCaps = new int [] { + NET_CAPABILITY_TRUSTED, + NET_CAPABILITY_NOT_VCN_MANAGED + }; + for (final int testCap : testCaps) { + // Create requests with and without the testing capability. + final TestNetworkCallback callbackWithCap = new TestNetworkCallback(); + final TestNetworkCallback callbackWithoutCap = new TestNetworkCallback(); + mCm.requestNetwork(new NetworkRequest.Builder().addCapability(testCap).build(), + callbackWithCap); + mCm.requestNetwork(new NetworkRequest.Builder().removeCapability(testCap).build(), + callbackWithoutCap); + + // Setup networks with testing capability and verify the default network changes. + mCellNetworkAgent = new TestNetworkAgentWrapper(TRANSPORT_CELLULAR); + mCellNetworkAgent.connect(true); + callbackWithCap.expectAvailableThenValidatedCallbacks(mCellNetworkAgent); + callbackWithoutCap.expectAvailableThenValidatedCallbacks(mCellNetworkAgent); + verify(mMockNetd).networkSetDefault(eq(mCellNetworkAgent.getNetwork().netId)); + reset(mMockNetd); - mWiFiNetworkAgent = new TestNetworkAgentWrapper(TRANSPORT_WIFI); - mWiFiNetworkAgent.connect(true); - trustedCallback.expectAvailableDoubleValidatedCallbacks(mWiFiNetworkAgent); - verify(mMockNetd).networkSetDefault(eq(mWiFiNetworkAgent.getNetwork().netId)); - reset(mMockNetd); + mWiFiNetworkAgent = new TestNetworkAgentWrapper(TRANSPORT_WIFI); + mWiFiNetworkAgent.connect(true); + callbackWithCap.expectAvailableDoubleValidatedCallbacks(mWiFiNetworkAgent); + callbackWithoutCap.expectAvailableDoubleValidatedCallbacks(mWiFiNetworkAgent); + verify(mMockNetd).networkSetDefault(eq(mWiFiNetworkAgent.getNetwork().netId)); + reset(mMockNetd); - mWiFiNetworkAgent.removeCapability(NET_CAPABILITY_TRUSTED); - trustedCallback.expectAvailableCallbacksValidated(mCellNetworkAgent); - verify(mMockNetd).networkSetDefault(eq(mCellNetworkAgent.getNetwork().netId)); - reset(mMockNetd); + // Remove the testing capability on wifi, verify the callback and default network + // changes back to cellular. + mWiFiNetworkAgent.removeCapability(testCap); + callbackWithCap.expectAvailableCallbacksValidated(mCellNetworkAgent); + callbackWithoutCap.expectCapabilitiesWithout(testCap, mWiFiNetworkAgent); + verify(mMockNetd).networkSetDefault(eq(mCellNetworkAgent.getNetwork().netId)); + reset(mMockNetd); - mCellNetworkAgent.removeCapability(NET_CAPABILITY_TRUSTED); - trustedCallback.expectCallback(CallbackEntry.LOST, mCellNetworkAgent); - verify(mMockNetd).networkClearDefault(); + mCellNetworkAgent.removeCapability(testCap); + callbackWithCap.expectCallback(CallbackEntry.LOST, mCellNetworkAgent); + callbackWithoutCap.assertNoCallback(); + verify(mMockNetd).networkClearDefault(); - mCm.unregisterNetworkCallback(trustedCallback); + mCm.unregisterNetworkCallback(callbackWithCap); + mCm.unregisterNetworkCallback(callbackWithoutCap); + } } @Ignore // 40%+ flakiness : figure out why and re-enable. @@ -7260,7 +7429,7 @@ public class ConnectivityServiceTest { mCellNetworkAgent.connect(true); networkCallback.expectAvailableThenValidatedCallbacks(mCellNetworkAgent); verify(mNetworkManagementService, times(1)).addIdleTimer(eq(MOBILE_IFNAME), anyInt(), - eq(ConnectivityManager.TYPE_MOBILE)); + eq(NetworkCapabilities.TRANSPORT_CELLULAR)); mWiFiNetworkAgent = new TestNetworkAgentWrapper(TRANSPORT_WIFI); final LinkProperties wifiLp = new LinkProperties(); @@ -7274,7 +7443,7 @@ public class ConnectivityServiceTest { networkCallback.expectCallback(CallbackEntry.LOSING, mCellNetworkAgent); networkCallback.expectCapabilitiesWith(NET_CAPABILITY_VALIDATED, mWiFiNetworkAgent); verify(mNetworkManagementService, times(1)).addIdleTimer(eq(WIFI_IFNAME), anyInt(), - eq(ConnectivityManager.TYPE_WIFI)); + eq(NetworkCapabilities.TRANSPORT_WIFI)); verify(mNetworkManagementService, times(1)).removeIdleTimer(eq(MOBILE_IFNAME)); // Disconnect wifi and switch back to cell @@ -7284,7 +7453,7 @@ public class ConnectivityServiceTest { assertNoCallbacks(networkCallback); verify(mNetworkManagementService, times(1)).removeIdleTimer(eq(WIFI_IFNAME)); verify(mNetworkManagementService, times(1)).addIdleTimer(eq(MOBILE_IFNAME), anyInt(), - eq(ConnectivityManager.TYPE_MOBILE)); + eq(NetworkCapabilities.TRANSPORT_CELLULAR)); // reconnect wifi mWiFiNetworkAgent = new TestNetworkAgentWrapper(TRANSPORT_WIFI); @@ -7399,6 +7568,7 @@ public class ConnectivityServiceTest { LinkProperties testLinkProperties = new LinkProperties(); testLinkProperties.setHttpProxy(testProxyInfo); mMockVpn.establishForMyUid(testLinkProperties); + assertUidRangesUpdatedForMyUid(true); // Test that the VPN network returns a proxy, and the WiFi does not. assertEquals(testProxyInfo, mService.getProxyForNetwork(mMockVpn.getNetwork())); @@ -7436,6 +7606,7 @@ public class ConnectivityServiceTest { // The uid range needs to cover the test app so the network is visible to it. final Set<UidRange> vpnRange = Collections.singleton(UidRange.createForUser(VPN_USER)); mMockVpn.establish(lp, VPN_UID, vpnRange); + assertVpnUidRangesUpdated(true, vpnRange, VPN_UID); // A connected VPN should have interface rules set up. There are two expected invocations, // one during the VPN initial connection, one during the VPN LinkProperties update. @@ -7463,6 +7634,7 @@ public class ConnectivityServiceTest { // The uid range needs to cover the test app so the network is visible to it. final Set<UidRange> vpnRange = Collections.singleton(UidRange.createForUser(VPN_USER)); mMockVpn.establish(lp, Process.SYSTEM_UID, vpnRange); + assertVpnUidRangesUpdated(true, vpnRange, Process.SYSTEM_UID); // Legacy VPN should not have interface rules set up verify(mMockNetd, never()).firewallAddUidInterfaceRules(any(), any()); @@ -7478,6 +7650,7 @@ public class ConnectivityServiceTest { // The uid range needs to cover the test app so the network is visible to it. final Set<UidRange> vpnRange = Collections.singleton(UidRange.createForUser(VPN_USER)); mMockVpn.establish(lp, Process.SYSTEM_UID, vpnRange); + assertVpnUidRangesUpdated(true, vpnRange, Process.SYSTEM_UID); // IPv6 unreachable route should not be misinterpreted as a default route verify(mMockNetd, never()).firewallAddUidInterfaceRules(any(), any()); @@ -7492,6 +7665,7 @@ public class ConnectivityServiceTest { // The uid range needs to cover the test app so the network is visible to it. final Set<UidRange> vpnRange = Collections.singleton(UidRange.createForUser(VPN_USER)); mMockVpn.establish(lp, VPN_UID, vpnRange); + assertVpnUidRangesUpdated(true, vpnRange, VPN_UID); // Connected VPN should have interface rules set up. There are two expected invocations, // one during VPN uid update, one during VPN LinkProperties update @@ -7542,7 +7716,9 @@ public class ConnectivityServiceTest { lp.addRoute(new RouteInfo(new IpPrefix(Inet6Address.ANY, 0), null)); // The uid range needs to cover the test app so the network is visible to it. final UidRange vpnRange = UidRange.createForUser(VPN_USER); - mMockVpn.establish(lp, VPN_UID, Collections.singleton(vpnRange)); + final Set<UidRange> vpnRanges = Collections.singleton(vpnRange); + mMockVpn.establish(lp, VPN_UID, vpnRanges); + assertVpnUidRangesUpdated(true, vpnRanges, VPN_UID); reset(mMockNetd); InOrder inOrder = inOrder(mMockNetd); @@ -7626,51 +7802,76 @@ public class ConnectivityServiceTest { private int getOwnerUidNetCapsForCallerPermission(int ownerUid, int callerUid) { final NetworkCapabilities netCap = new NetworkCapabilities().setOwnerUid(ownerUid); - return mService - .maybeSanitizeLocationInfoForCaller(netCap, callerUid, mContext.getPackageName()) - .getOwnerUid(); + return mService.createWithLocationInfoSanitizedIfNecessaryWhenParceled( + netCap, callerUid, mContext.getPackageName()).getOwnerUid(); + } + + private void verifyWifiInfoCopyNetCapsForCallerPermission( + int callerUid, boolean shouldMakeCopyWithLocationSensitiveFieldsParcelable) { + final WifiInfo wifiInfo = mock(WifiInfo.class); + when(wifiInfo.hasLocationSensitiveFields()).thenReturn(true); + final NetworkCapabilities netCap = new NetworkCapabilities().setTransportInfo(wifiInfo); + + mService.createWithLocationInfoSanitizedIfNecessaryWhenParceled( + netCap, callerUid, mContext.getPackageName()); + verify(wifiInfo).makeCopy(eq(shouldMakeCopyWithLocationSensitiveFieldsParcelable)); } @Test - public void testMaybeSanitizeLocationInfoForCallerWithFineLocationAfterQ() throws Exception { + public void testCreateForCallerWithLocationInfoSanitizedWithFineLocationAfterQ() + throws Exception { setupLocationPermissions(Build.VERSION_CODES.Q, true, AppOpsManager.OPSTR_FINE_LOCATION, Manifest.permission.ACCESS_FINE_LOCATION); final int myUid = Process.myUid(); assertEquals(myUid, getOwnerUidNetCapsForCallerPermission(myUid, myUid)); + + verifyWifiInfoCopyNetCapsForCallerPermission(myUid, + true /* shouldMakeCopyWithLocationSensitiveFieldsParcelable */); } @Test - public void testMaybeSanitizeLocationInfoForCallerWithCoarseLocationPreQ() throws Exception { + public void testCreateForCallerWithLocationInfoSanitizedWithCoarseLocationPreQ() + throws Exception { setupLocationPermissions(Build.VERSION_CODES.P, true, AppOpsManager.OPSTR_COARSE_LOCATION, Manifest.permission.ACCESS_COARSE_LOCATION); final int myUid = Process.myUid(); assertEquals(myUid, getOwnerUidNetCapsForCallerPermission(myUid, myUid)); + + verifyWifiInfoCopyNetCapsForCallerPermission(myUid, + true /* shouldMakeCopyWithLocationSensitiveFieldsParcelable */); } @Test - public void testMaybeSanitizeLocationInfoForCallerLocationOff() throws Exception { + public void testCreateForCallerWithLocationInfoSanitizedLocationOff() throws Exception { // Test that even with fine location permission, and UIDs matching, the UID is sanitized. setupLocationPermissions(Build.VERSION_CODES.Q, false, AppOpsManager.OPSTR_FINE_LOCATION, Manifest.permission.ACCESS_FINE_LOCATION); final int myUid = Process.myUid(); assertEquals(Process.INVALID_UID, getOwnerUidNetCapsForCallerPermission(myUid, myUid)); + + verifyWifiInfoCopyNetCapsForCallerPermission(myUid, + false/* shouldMakeCopyWithLocationSensitiveFieldsParcelable */); } @Test - public void testMaybeSanitizeLocationInfoForCallerWrongUid() throws Exception { + public void testCreateForCallerWithLocationInfoSanitizedWrongUid() throws Exception { // Test that even with fine location permission, not being the owner leads to sanitization. setupLocationPermissions(Build.VERSION_CODES.Q, true, AppOpsManager.OPSTR_FINE_LOCATION, Manifest.permission.ACCESS_FINE_LOCATION); final int myUid = Process.myUid(); assertEquals(Process.INVALID_UID, getOwnerUidNetCapsForCallerPermission(myUid + 1, myUid)); + + verifyWifiInfoCopyNetCapsForCallerPermission(myUid, + true /* shouldMakeCopyWithLocationSensitiveFieldsParcelable */); } @Test - public void testMaybeSanitizeLocationInfoForCallerWithCoarseLocationAfterQ() throws Exception { + public void testCreateForCallerWithLocationInfoSanitizedWithCoarseLocationAfterQ() + throws Exception { // Test that not having fine location permission leads to sanitization. setupLocationPermissions(Build.VERSION_CODES.Q, true, AppOpsManager.OPSTR_COARSE_LOCATION, Manifest.permission.ACCESS_COARSE_LOCATION); @@ -7678,21 +7879,29 @@ public class ConnectivityServiceTest { // Test that without the location permission, the owner field is sanitized. final int myUid = Process.myUid(); assertEquals(Process.INVALID_UID, getOwnerUidNetCapsForCallerPermission(myUid, myUid)); + + verifyWifiInfoCopyNetCapsForCallerPermission(myUid, + false/* shouldMakeCopyWithLocationSensitiveFieldsParcelable */); } @Test - public void testMaybeSanitizeLocationInfoForCallerWithoutLocationPermission() throws Exception { + public void testCreateForCallerWithLocationInfoSanitizedWithoutLocationPermission() + throws Exception { setupLocationPermissions(Build.VERSION_CODES.Q, true, null /* op */, null /* perm */); // Test that without the location permission, the owner field is sanitized. final int myUid = Process.myUid(); assertEquals(Process.INVALID_UID, getOwnerUidNetCapsForCallerPermission(myUid, myUid)); + + verifyWifiInfoCopyNetCapsForCallerPermission(myUid, + false/* shouldMakeCopyWithLocationSensitiveFieldsParcelable */); } private void setupConnectionOwnerUid(int vpnOwnerUid, @VpnManager.VpnType int vpnType) throws Exception { final Set<UidRange> vpnRange = Collections.singleton(UidRange.createForUser(VPN_USER)); mMockVpn.establish(new LinkProperties(), vpnOwnerUid, vpnRange); + assertVpnUidRangesUpdated(true, vpnRange, vpnOwnerUid); mMockVpn.setVpnType(vpnType); final VpnInfo vpnInfo = new VpnInfo(); @@ -7950,6 +8159,7 @@ public class ConnectivityServiceTest { Manifest.permission.ACCESS_FINE_LOCATION); mMockVpn.establishForMyUid(); + assertUidRangesUpdatedForMyUid(true); // Wait for networks to connect and broadcasts to be sent before removing permissions. waitForIdle(); @@ -8229,4 +8439,54 @@ public class ConnectivityServiceTest { assertTrue(isRequestIdInOrder); } } + + private void assertUidRangesUpdatedForMyUid(boolean add) throws Exception { + final int uid = Process.myUid(); + assertVpnUidRangesUpdated(add, uidRangesForUid(uid), uid); + } + + private void assertVpnUidRangesUpdated(boolean add, Set<UidRange> vpnRanges, int exemptUid) + throws Exception { + InOrder inOrder = inOrder(mMockNetd); + ArgumentCaptor<int[]> exemptUidCaptor = ArgumentCaptor.forClass(int[].class); + + inOrder.verify(mMockNetd, times(1)).socketDestroy(eq(toUidRangeStableParcels(vpnRanges)), + exemptUidCaptor.capture()); + assertContainsExactly(exemptUidCaptor.getValue(), Process.VPN_UID, exemptUid); + + if (add) { + inOrder.verify(mMockNetd, times(1)).networkAddUidRanges(eq(mMockVpn.getNetId()), + eq(toUidRangeStableParcels(vpnRanges))); + } else { + inOrder.verify(mMockNetd, times(1)).networkRemoveUidRanges(eq(mMockVpn.getNetId()), + eq(toUidRangeStableParcels(vpnRanges))); + } + + inOrder.verify(mMockNetd, times(1)).socketDestroy(eq(toUidRangeStableParcels(vpnRanges)), + exemptUidCaptor.capture()); + assertContainsExactly(exemptUidCaptor.getValue(), Process.VPN_UID, exemptUid); + } + + @Test + public void testVpnUidRangesUpdate() throws Exception { + LinkProperties lp = new LinkProperties(); + lp.setInterfaceName("tun0"); + lp.addRoute(new RouteInfo(new IpPrefix(Inet4Address.ANY, 0), null)); + lp.addRoute(new RouteInfo(new IpPrefix(Inet6Address.ANY, 0), null)); + final UidRange vpnRange = UidRange.createForUser(VPN_USER); + Set<UidRange> vpnRanges = Collections.singleton(vpnRange); + mMockVpn.establish(lp, VPN_UID, vpnRanges); + assertVpnUidRangesUpdated(true, vpnRanges, VPN_UID); + + reset(mMockNetd); + // Update to new range which is old range minus APP1, i.e. only APP2 + final Set<UidRange> newRanges = new HashSet<>(Arrays.asList( + new UidRange(vpnRange.start, APP1_UID - 1), + new UidRange(APP1_UID + 1, vpnRange.stop))); + mMockVpn.setUids(newRanges); + waitForIdle(); + + assertVpnUidRangesUpdated(true, newRanges, VPN_UID); + assertVpnUidRangesUpdated(false, vpnRanges, VPN_UID); + } } diff --git a/tests/net/java/com/android/server/connectivity/IpConnectivityMetricsTest.java b/tests/net/java/com/android/server/connectivity/IpConnectivityMetricsTest.java index 3a071667a542..8c5d1d6d05e5 100644 --- a/tests/net/java/com/android/server/connectivity/IpConnectivityMetricsTest.java +++ b/tests/net/java/com/android/server/connectivity/IpConnectivityMetricsTest.java @@ -124,6 +124,22 @@ public class IpConnectivityMetricsTest { assertEquals("", output2); } + private void logDefaultNetworkEvent(long timeMs, NetworkAgentInfo nai, + NetworkAgentInfo oldNai) { + final Network network = (nai != null) ? nai.network() : null; + final int score = (nai != null) ? nai.getCurrentScore() : 0; + final boolean validated = (nai != null) ? nai.lastValidated : false; + final LinkProperties lp = (nai != null) ? nai.linkProperties : null; + final NetworkCapabilities nc = (nai != null) ? nai.networkCapabilities : null; + + final Network prevNetwork = (oldNai != null) ? oldNai.network() : null; + final int prevScore = (oldNai != null) ? oldNai.getCurrentScore() : 0; + final LinkProperties prevLp = (oldNai != null) ? oldNai.linkProperties : null; + final NetworkCapabilities prevNc = (oldNai != null) ? oldNai.networkCapabilities : null; + + mService.mDefaultNetworkMetrics.logDefaultNetworkEvent(timeMs, network, score, validated, + lp, nc, prevNetwork, prevScore, prevLp, prevNc); + } @Test public void testDefaultNetworkEvents() throws Exception { final long cell = BitUtils.packBits(new int[]{NetworkCapabilities.TRANSPORT_CELLULAR}); @@ -147,7 +163,7 @@ public class IpConnectivityMetricsTest { for (NetworkAgentInfo[] pair : defaultNetworks) { timeMs += durationMs; durationMs += durationMs; - mService.mDefaultNetworkMetrics.logDefaultNetworkEvent(timeMs, pair[1], pair[0]); + logDefaultNetworkEvent(timeMs, pair[1], pair[0]); } String want = String.join("\n", @@ -331,8 +347,8 @@ public class IpConnectivityMetricsTest { final long wifi = BitUtils.packBits(new int[]{NetworkCapabilities.TRANSPORT_WIFI}); NetworkAgentInfo cellNai = makeNai(100, 50, false, true, cell); NetworkAgentInfo wifiNai = makeNai(101, 60, true, false, wifi); - mService.mDefaultNetworkMetrics.logDefaultNetworkEvent(timeMs + 200, cellNai, null); - mService.mDefaultNetworkMetrics.logDefaultNetworkEvent(timeMs + 300, wifiNai, cellNai); + logDefaultNetworkEvent(timeMs + 200L, cellNai, null); + logDefaultNetworkEvent(timeMs + 300L, wifiNai, cellNai); String want = String.join("\n", "dropped_events: 0", |