diff options
126 files changed, 4365 insertions, 1936 deletions
diff --git a/Android.bp b/Android.bp index 71023bf647c8..c450acf53bfd 100644 --- a/Android.bp +++ b/Android.bp @@ -104,6 +104,7 @@ filegroup { ":android.security.apc-java-source", ":android.security.authorization-java-source", ":android.security.maintenance-java-source", + ":android.security.metrics-java-source", ":android.security.vpnprofilestore-java-source", ":android.system.keystore2-V1-java-source", ":credstore_aidl", diff --git a/apex/appsearch/Android.bp b/apex/appsearch/Android.bp index 827842633942..ac97e04020a0 100644 --- a/apex/appsearch/Android.bp +++ b/apex/appsearch/Android.bp @@ -50,6 +50,20 @@ bootclasspath_fragment { name: "com.android.appsearch-bootclasspath-fragment", contents: ["framework-appsearch"], apex_available: ["com.android.appsearch"], + + // The bootclasspath_fragments that provide APIs on which this depends. + fragments: [ + { + apex: "com.android.art", + module: "art-bootclasspath-fragment", + }, + ], + + // Additional stubs libraries that this fragment's contents use which are + // not provided by another bootclasspath_fragment. + additional_stubs: [ + "android-non-updatable", + ], } // Encapsulate the contributions made by the com.android.appsearch to the systemserverclasspath. diff --git a/apex/jobscheduler/framework/java/android/app/AlarmManager.java b/apex/jobscheduler/framework/java/android/app/AlarmManager.java index 4843415fdbdd..9c0c3657bff3 100644 --- a/apex/jobscheduler/framework/java/android/app/AlarmManager.java +++ b/apex/jobscheduler/framework/java/android/app/AlarmManager.java @@ -494,6 +494,9 @@ public class AlarmManager { * exact alarms, rescheduling each time as described above. Legacy applications * whose {@code targetSdkVersion} is earlier than API 19 will continue to have all * of their alarms, including repeating alarms, treated as exact. + * <p>Apps targeting {@link Build.VERSION_CODES#S} will need to set the flag + * {@link PendingIntent#FLAG_MUTABLE} on the {@link PendingIntent} being used to set this alarm, + * if they want the alarm count to be supplied with the key {@link Intent#EXTRA_ALARM_COUNT}. * * @param type type of alarm. * @param triggerAtMillis time in milliseconds that the alarm should first @@ -516,6 +519,7 @@ public class AlarmManager { * @see #ELAPSED_REALTIME_WAKEUP * @see #RTC * @see #RTC_WAKEUP + * @see Intent#EXTRA_ALARM_COUNT */ public void setRepeating(@AlarmType int type, long triggerAtMillis, long intervalMillis, PendingIntent operation) { @@ -1004,6 +1008,9 @@ public class AlarmManager { * been available since API 3, your application can safely call it and be * assured that it will get similar behavior on both current and older versions * of Android. + * <p>Apps targeting {@link Build.VERSION_CODES#S} will need to set the flag + * {@link PendingIntent#FLAG_MUTABLE} on the {@link PendingIntent} being used to set this alarm, + * if they want the alarm count to be supplied with the key {@link Intent#EXTRA_ALARM_COUNT}. * * @param type type of alarm. * @param triggerAtMillis time in milliseconds that the alarm should first @@ -1038,6 +1045,7 @@ public class AlarmManager { * @see #INTERVAL_HOUR * @see #INTERVAL_HALF_DAY * @see #INTERVAL_DAY + * @see Intent#EXTRA_ALARM_COUNT */ public void setInexactRepeating(@AlarmType int type, long triggerAtMillis, long intervalMillis, PendingIntent operation) { @@ -1286,22 +1294,31 @@ public class AlarmManager { /** * Called to check if the caller can schedule exact alarms. + * Your app schedules exact alarms when it calls any of the {@code setExact...} or + * {@link #setAlarmClock(AlarmClockInfo, PendingIntent) setAlarmClock} API methods. * <p> - * Apps targeting {@link Build.VERSION_CODES#S} or higher can schedule exact alarms if they - * have the {@link Manifest.permission#SCHEDULE_EXACT_ALARM} permission. These apps can also + * Apps targeting {@link Build.VERSION_CODES#S} or higher can schedule exact alarms only if they + * have the {@link Manifest.permission#SCHEDULE_EXACT_ALARM} permission or they are on the + * device's power-save exemption list. + * These apps can also * start {@link android.provider.Settings#ACTION_REQUEST_SCHEDULE_EXACT_ALARM} to - * request this from the user. + * request this permission from the user. * <p> * Apps targeting lower sdk versions, can always schedule exact alarms. * - * @return {@code true} if the caller can schedule exact alarms. + * @return {@code true} if the caller can schedule exact alarms, {@code false} otherwise. * @see android.provider.Settings#ACTION_REQUEST_SCHEDULE_EXACT_ALARM * @see #setExact(int, long, PendingIntent) * @see #setExactAndAllowWhileIdle(int, long, PendingIntent) * @see #setAlarmClock(AlarmClockInfo, PendingIntent) + * @see android.os.PowerManager#isIgnoringBatteryOptimizations(String) */ public boolean canScheduleExactAlarms() { - return hasScheduleExactAlarm(mContext.getOpPackageName(), mContext.getUserId()); + try { + return mService.canScheduleExactAlarms(mContext.getOpPackageName()); + } catch (RemoteException re) { + throw re.rethrowFromSystemServer(); + } } /** diff --git a/apex/jobscheduler/framework/java/android/app/IAlarmManager.aidl b/apex/jobscheduler/framework/java/android/app/IAlarmManager.aidl index cd7c1e8ab4fd..9d11ca470397 100644 --- a/apex/jobscheduler/framework/java/android/app/IAlarmManager.aidl +++ b/apex/jobscheduler/framework/java/android/app/IAlarmManager.aidl @@ -41,6 +41,7 @@ interface IAlarmManager { @UnsupportedAppUsage(maxTargetSdk = 30, trackingBug = 170729553) AlarmManager.AlarmClockInfo getNextAlarmClock(int userId); long currentNetworkTimeMillis(); + boolean canScheduleExactAlarms(String packageName); boolean hasScheduleExactAlarm(String packageName, int userId); int getConfigVersion(); } diff --git a/apex/jobscheduler/framework/java/android/os/PowerExemptionManager.java b/apex/jobscheduler/framework/java/android/os/PowerExemptionManager.java index 42e953b72a69..a1a46afcffe6 100644 --- a/apex/jobscheduler/framework/java/android/os/PowerExemptionManager.java +++ b/apex/jobscheduler/framework/java/android/os/PowerExemptionManager.java @@ -190,6 +190,8 @@ public class PowerExemptionManager { * @hide */ public static final int REASON_TEMP_ALLOWED_WHILE_IN_USE = 70; + /** @hide */ + public static final int REASON_CURRENT_INPUT_METHOD = 71; /* BG-FGS-launch is allowed by temp-allow-list or system-allow-list. Reason code for temp and system allow list starts here. @@ -381,6 +383,7 @@ public class PowerExemptionManager { REASON_ACTIVITY_VISIBILITY_GRACE_PERIOD, REASON_OP_ACTIVATE_VPN, REASON_OP_ACTIVATE_PLATFORM_VPN, + REASON_CURRENT_INPUT_METHOD, REASON_TEMP_ALLOWED_WHILE_IN_USE, // temp and system allow list reasons. REASON_GEOFENCING, @@ -649,6 +652,8 @@ public class PowerExemptionManager { return "OP_ACTIVATE_VPN"; case REASON_OP_ACTIVATE_PLATFORM_VPN: return "OP_ACTIVATE_PLATFORM_VPN"; + case REASON_CURRENT_INPUT_METHOD: + return "CURRENT_INPUT_METHOD"; case REASON_TEMP_ALLOWED_WHILE_IN_USE: return "TEMP_ALLOWED_WHILE_IN_USE"; case REASON_GEOFENCING: 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 70e548d4c547..ed80ddbd2cd7 100644 --- a/apex/jobscheduler/service/java/com/android/server/alarm/AlarmManagerService.java +++ b/apex/jobscheduler/service/java/com/android/server/alarm/AlarmManagerService.java @@ -1740,7 +1740,7 @@ public class AlarmManagerService extends SystemService { if (!isExactAlarmChangeEnabled(a.packageName, UserHandle.getUserId(a.uid))) { return false; } - return a.alarmClock != null || !isExemptFromExactAlarmPermission(a.uid); + return !isExemptFromExactAlarmPermission(a.uid); }; removeAlarmsInternalLocked(whichAlarms, REMOVE_REASON_EXACT_PERMISSION_REVOKED); } @@ -2414,6 +2414,7 @@ public class AlarmManagerService extends SystemService { /** * Returns true if the given uid does not require SCHEDULE_EXACT_ALARM to set exact, * allow-while-idle alarms. + * Note: It is ok to call this method without the lock {@link #mLock} held. */ boolean isExemptFromExactAlarmPermission(int uid) { return (UserHandle.isSameApp(mSystemUiUid, uid) @@ -2515,7 +2516,7 @@ public class AlarmManagerService extends SystemService { idleOptions = allowWhileIdle ? mOptsWithFgs.toBundle() : null; } if (needsPermission && !hasScheduleExactAlarmInternal(callingPackage, callingUid)) { - if (alarmClock != null || !isExemptFromExactAlarmPermission(callingUid)) { + if (!isExemptFromExactAlarmPermission(callingUid)) { final String errorMessage = "Caller " + callingPackage + " needs to hold " + Manifest.permission.SCHEDULE_EXACT_ALARM + " to set " + "exact alarms."; @@ -2527,10 +2528,16 @@ public class AlarmManagerService extends SystemService { } else { allowListed = true; } - // If the app is on the full system power allow-list (not except-idle), or we're - // in a soft failure mode, we still allow the alarms. - // We give temporary allowlist to allow-while-idle alarms but without FGS - // capability. Note that apps that are in the power allow-list do not need it. + // If the app is on the full system power allow-list (not except-idle), or the + // user-elected allow-list, or we're in a soft failure mode, we still allow the + // alarms. + // In both cases, ALLOW_WHILE_IDLE alarms get a lower quota equivalent to what + // pre-S apps got. Note that user-allow-listed apps don't use the flag + // ALLOW_WHILE_IDLE. + // We grant temporary allow-list to allow-while-idle alarms but without FGS + // capability. AlarmClock alarms do not get the temporary allow-list. This is + // consistent with pre-S behavior. Note that apps that are in either of the + // power-save allow-lists do not need it. idleOptions = allowWhileIdle ? mOptsWithoutFgs.toBundle() : null; lowerQuota = allowWhileIdle; } @@ -2561,6 +2568,22 @@ public class AlarmManagerService extends SystemService { } @Override + public boolean canScheduleExactAlarms(String packageName) { + final int callingUid = mInjector.getCallingUid(); + final int userId = UserHandle.getUserId(callingUid); + final int packageUid = mPackageManagerInternal.getPackageUid(packageName, 0, userId); + if (callingUid != packageUid) { + throw new SecurityException("Uid " + callingUid + + " cannot query canScheduleExactAlarms for package " + packageName); + } + if (!isExactAlarmChangeEnabled(packageName, userId)) { + return true; + } + return isExemptFromExactAlarmPermission(packageUid) + || hasScheduleExactAlarmInternal(packageName, packageUid); + } + + @Override public boolean hasScheduleExactAlarm(String packageName, int userId) { final int callingUid = mInjector.getCallingUid(); if (UserHandle.getUserId(callingUid) != userId) { @@ -2572,9 +2595,6 @@ public class AlarmManagerService extends SystemService { throw new SecurityException("Uid " + callingUid + " cannot query hasScheduleExactAlarm for uid " + uid); } - if (!isExactAlarmChangeEnabled(packageName, userId)) { - return true; - } return (uid > 0) ? hasScheduleExactAlarmInternal(packageName, uid) : false; } @@ -3577,17 +3597,14 @@ public class AlarmManagerService extends SystemService { * This is not expected to get called frequently. */ void removeExactAlarmsOnPermissionRevokedLocked(int uid, String packageName) { - Slog.w(TAG, "Package " + packageName + ", uid " + uid + " lost SCHEDULE_EXACT_ALARM!"); - if (!isExactAlarmChangeEnabled(packageName, UserHandle.getUserId(uid))) { + if (isExemptFromExactAlarmPermission(uid) + || !isExactAlarmChangeEnabled(packageName, UserHandle.getUserId(uid))) { return; } + Slog.w(TAG, "Package " + packageName + ", uid " + uid + " lost SCHEDULE_EXACT_ALARM!"); - final Predicate<Alarm> whichAlarms = a -> { - if (a.uid == uid && a.packageName.equals(packageName) && a.windowLength == 0) { - return a.alarmClock != null || !isExemptFromExactAlarmPermission(uid); - } - return false; - }; + final Predicate<Alarm> whichAlarms = a -> (a.uid == uid && a.packageName.equals(packageName) + && a.windowLength == 0); removeAlarmsInternalLocked(whichAlarms, REMOVE_REASON_EXACT_PERMISSION_REVOKED); if (mConstants.KILL_ON_SCHEDULE_EXACT_ALARM_REVOKED) { diff --git a/boot/hiddenapi/hiddenapi-max-target-o.txt b/boot/hiddenapi/hiddenapi-max-target-o.txt index 770547e205e6..0ec918b11723 100644 --- a/boot/hiddenapi/hiddenapi-max-target-o.txt +++ b/boot/hiddenapi/hiddenapi-max-target-o.txt @@ -31612,12 +31612,6 @@ Landroid/media/MediaSession2$CommandButton;->getIconResId()I Landroid/media/MediaSession2$CommandButton;->getProvider()Landroid/media/update/MediaSession2Provider$CommandButtonProvider; Landroid/media/MediaSession2$CommandButton;->isEnabled()Z Landroid/media/MediaSession2$CommandButton;->mProvider:Landroid/media/update/MediaSession2Provider$CommandButtonProvider; -Landroid/media/MediaSession2$ControllerInfo;-><init>(Landroid/content/Context;IILjava/lang/String;Landroid/os/IInterface;)V -Landroid/media/MediaSession2$ControllerInfo;->getPackageName()Ljava/lang/String; -Landroid/media/MediaSession2$ControllerInfo;->getProvider()Landroid/media/update/MediaSession2Provider$ControllerInfoProvider; -Landroid/media/MediaSession2$ControllerInfo;->getUid()I -Landroid/media/MediaSession2$ControllerInfo;->isTrusted()Z -Landroid/media/MediaSession2$ControllerInfo;->mProvider:Landroid/media/update/MediaSession2Provider$ControllerInfoProvider; Landroid/media/MediaSession2$OnDataSourceMissingHelper;->onDataSourceMissing(Landroid/media/MediaSession2;Landroid/media/MediaItem2;)Landroid/media/DataSourceDesc; Landroid/media/MediaSession2$SessionCallback;-><init>()V Landroid/media/MediaSession2$SessionCallback;->onBufferingStateChanged(Landroid/media/MediaSession2;Landroid/media/MediaPlayerBase;Landroid/media/MediaItem2;I)V @@ -35333,159 +35327,6 @@ Landroid/mtp/MtpStorageManager;->removeObjectFromCache(Landroid/mtp/MtpStorageMa Landroid/mtp/MtpStorageManager;->sDebug:Z Landroid/mtp/MtpStorageManager;->setSubdirectories(Ljava/util/Set;)V Landroid/mtp/MtpStorageManager;->TAG:Ljava/lang/String; -Landroid/net/CaptivePortal;-><init>(Landroid/os/IBinder;)V -Landroid/net/CaptivePortal;->APP_RETURN_DISMISSED:I -Landroid/net/CaptivePortal;->APP_RETURN_UNWANTED:I -Landroid/net/CaptivePortal;->APP_RETURN_WANTED_AS_IS:I -Landroid/net/CaptivePortal;->mBinder:Landroid/os/IBinder; -Landroid/net/CaptivePortal;->useNetwork()V -Landroid/net/ConnectivityManager$CallbackHandler;->DBG:Z -Landroid/net/ConnectivityManager$CallbackHandler;->getObject(Landroid/os/Message;Ljava/lang/Class;)Ljava/lang/Object; -Landroid/net/ConnectivityManager$CallbackHandler;->TAG:Ljava/lang/String; -Landroid/net/ConnectivityManager$Errors;->TOO_MANY_REQUESTS:I -Landroid/net/ConnectivityManager$LegacyRequest;-><init>()V -Landroid/net/ConnectivityManager$LegacyRequest;->clearDnsBinding()V -Landroid/net/ConnectivityManager$LegacyRequest;->currentNetwork:Landroid/net/Network; -Landroid/net/ConnectivityManager$LegacyRequest;->delay:I -Landroid/net/ConnectivityManager$LegacyRequest;->expireSequenceNumber:I -Landroid/net/ConnectivityManager$LegacyRequest;->networkCallback:Landroid/net/ConnectivityManager$NetworkCallback; -Landroid/net/ConnectivityManager$LegacyRequest;->networkCapabilities:Landroid/net/NetworkCapabilities; -Landroid/net/ConnectivityManager$LegacyRequest;->networkRequest:Landroid/net/NetworkRequest; -Landroid/net/ConnectivityManager$NetworkCallback;->networkRequest:Landroid/net/NetworkRequest; -Landroid/net/ConnectivityManager$NetworkCallback;->onAvailable(Landroid/net/Network;Landroid/net/NetworkCapabilities;Landroid/net/LinkProperties;)V -Landroid/net/ConnectivityManager$NetworkCallback;->onNetworkResumed(Landroid/net/Network;)V -Landroid/net/ConnectivityManager$NetworkCallback;->onNetworkSuspended(Landroid/net/Network;)V -Landroid/net/ConnectivityManager$NetworkCallback;->onPreCheck(Landroid/net/Network;)V -Landroid/net/ConnectivityManager$PacketKeepalive;->BINDER_DIED:I -Landroid/net/ConnectivityManager$PacketKeepalive;->ERROR_HARDWARE_ERROR:I -Landroid/net/ConnectivityManager$PacketKeepalive;->ERROR_HARDWARE_UNSUPPORTED:I -Landroid/net/ConnectivityManager$PacketKeepalive;->ERROR_INVALID_INTERVAL:I -Landroid/net/ConnectivityManager$PacketKeepalive;->ERROR_INVALID_IP_ADDRESS:I -Landroid/net/ConnectivityManager$PacketKeepalive;->ERROR_INVALID_LENGTH:I -Landroid/net/ConnectivityManager$PacketKeepalive;->ERROR_INVALID_NETWORK:I -Landroid/net/ConnectivityManager$PacketKeepalive;->ERROR_INVALID_PORT:I -Landroid/net/ConnectivityManager$PacketKeepalive;->mCallback:Landroid/net/ConnectivityManager$PacketKeepaliveCallback; -Landroid/net/ConnectivityManager$PacketKeepalive;->MIN_INTERVAL:I -Landroid/net/ConnectivityManager$PacketKeepalive;->mLooper:Landroid/os/Looper; -Landroid/net/ConnectivityManager$PacketKeepalive;->mMessenger:Landroid/os/Messenger; -Landroid/net/ConnectivityManager$PacketKeepalive;->mNetwork:Landroid/net/Network; -Landroid/net/ConnectivityManager$PacketKeepalive;->mSlot:Ljava/lang/Integer; -Landroid/net/ConnectivityManager$PacketKeepalive;->NATT_PORT:I -Landroid/net/ConnectivityManager$PacketKeepalive;->NO_KEEPALIVE:I -Landroid/net/ConnectivityManager$PacketKeepalive;->stopLooper()V -Landroid/net/ConnectivityManager$PacketKeepalive;->SUCCESS:I -Landroid/net/ConnectivityManager$PacketKeepalive;->TAG:Ljava/lang/String; -Landroid/net/ConnectivityManager$TooManyRequestsException;-><init>()V -Landroid/net/ConnectivityManager;-><init>(Landroid/content/Context;Landroid/net/IConnectivityManager;)V -Landroid/net/ConnectivityManager;->ACTION_CAPTIVE_PORTAL_TEST_COMPLETED:Ljava/lang/String; -Landroid/net/ConnectivityManager;->ACTION_DATA_ACTIVITY_CHANGE:Ljava/lang/String; -Landroid/net/ConnectivityManager;->ACTION_PROMPT_LOST_VALIDATION:Ljava/lang/String; -Landroid/net/ConnectivityManager;->ACTION_PROMPT_UNVALIDATED:Ljava/lang/String; -Landroid/net/ConnectivityManager;->ALREADY_UNREGISTERED:Landroid/net/NetworkRequest; -Landroid/net/ConnectivityManager;->BASE:I -Landroid/net/ConnectivityManager;->CALLBACK_AVAILABLE:I -Landroid/net/ConnectivityManager;->CALLBACK_CAP_CHANGED:I -Landroid/net/ConnectivityManager;->CALLBACK_IP_CHANGED:I -Landroid/net/ConnectivityManager;->CALLBACK_LOSING:I -Landroid/net/ConnectivityManager;->CALLBACK_LOST:I -Landroid/net/ConnectivityManager;->CALLBACK_PRECHECK:I -Landroid/net/ConnectivityManager;->CALLBACK_RESUMED:I -Landroid/net/ConnectivityManager;->CALLBACK_SUSPENDED:I -Landroid/net/ConnectivityManager;->CALLBACK_UNAVAIL:I -Landroid/net/ConnectivityManager;->checkCallbackNotNull(Landroid/net/ConnectivityManager$NetworkCallback;)V -Landroid/net/ConnectivityManager;->checkLegacyRoutingApiAccess()V -Landroid/net/ConnectivityManager;->checkMobileProvisioning(I)I -Landroid/net/ConnectivityManager;->checkPendingIntentNotNull(Landroid/app/PendingIntent;)V -Landroid/net/ConnectivityManager;->checkTimeout(I)V -Landroid/net/ConnectivityManager;->CONNECTIVITY_ACTION_SUPL:Ljava/lang/String; -Landroid/net/ConnectivityManager;->convertServiceException(Landroid/os/ServiceSpecificException;)Ljava/lang/RuntimeException; -Landroid/net/ConnectivityManager;->enforceChangePermission(Landroid/content/Context;)V -Landroid/net/ConnectivityManager;->enforceTetherChangePermission(Landroid/content/Context;Ljava/lang/String;)V -Landroid/net/ConnectivityManager;->expireRequest(Landroid/net/NetworkCapabilities;I)V -Landroid/net/ConnectivityManager;->EXPIRE_LEGACY_REQUEST:I -Landroid/net/ConnectivityManager;->EXTRA_ACTIVE_LOCAL_ONLY:Ljava/lang/String; -Landroid/net/ConnectivityManager;->EXTRA_ADD_TETHER_TYPE:Ljava/lang/String; -Landroid/net/ConnectivityManager;->EXTRA_CAPTIVE_PORTAL_PROBE_SPEC:Ljava/lang/String; -Landroid/net/ConnectivityManager;->EXTRA_CAPTIVE_PORTAL_USER_AGENT:Ljava/lang/String; -Landroid/net/ConnectivityManager;->EXTRA_DEVICE_TYPE:Ljava/lang/String; -Landroid/net/ConnectivityManager;->EXTRA_INET_CONDITION:Ljava/lang/String; -Landroid/net/ConnectivityManager;->EXTRA_IS_ACTIVE:Ljava/lang/String; -Landroid/net/ConnectivityManager;->EXTRA_IS_CAPTIVE_PORTAL:Ljava/lang/String; -Landroid/net/ConnectivityManager;->EXTRA_PROVISION_CALLBACK:Ljava/lang/String; -Landroid/net/ConnectivityManager;->EXTRA_REALTIME_NS:Ljava/lang/String; -Landroid/net/ConnectivityManager;->EXTRA_REM_TETHER_TYPE:Ljava/lang/String; -Landroid/net/ConnectivityManager;->EXTRA_RUN_PROVISION:Ljava/lang/String; -Landroid/net/ConnectivityManager;->EXTRA_SET_ALARM:Ljava/lang/String; -Landroid/net/ConnectivityManager;->factoryReset()V -Landroid/net/ConnectivityManager;->findRequestForFeature(Landroid/net/NetworkCapabilities;)Landroid/net/NetworkRequest; -Landroid/net/ConnectivityManager;->getActiveNetworkForUid(I)Landroid/net/Network; -Landroid/net/ConnectivityManager;->getActiveNetworkForUid(IZ)Landroid/net/Network; -Landroid/net/ConnectivityManager;->getActiveNetworkInfoForUid(IZ)Landroid/net/NetworkInfo; -Landroid/net/ConnectivityManager;->getAlwaysOnVpnPackageForUser(I)Ljava/lang/String; -Landroid/net/ConnectivityManager;->getCallbackName(I)Ljava/lang/String; -Landroid/net/ConnectivityManager;->getDefaultHandler()Landroid/net/ConnectivityManager$CallbackHandler; -Landroid/net/ConnectivityManager;->getGlobalProxy()Landroid/net/ProxyInfo; -Landroid/net/ConnectivityManager;->getInstanceOrNull()Landroid/net/ConnectivityManager; -Landroid/net/ConnectivityManager;->getMobileProvisioningUrl()Ljava/lang/String; -Landroid/net/ConnectivityManager;->getNetworkInfoForUid(Landroid/net/Network;IZ)Landroid/net/NetworkInfo; -Landroid/net/ConnectivityManager;->getNetworkManagementService()Landroid/os/INetworkManagementService; -Landroid/net/ConnectivityManager;->getNetworkPolicyManager()Landroid/net/INetworkPolicyManager; -Landroid/net/ConnectivityManager;->getProxyForNetwork(Landroid/net/Network;)Landroid/net/ProxyInfo; -Landroid/net/ConnectivityManager;->getTetheredDhcpRanges()[Ljava/lang/String; -Landroid/net/ConnectivityManager;->inferLegacyTypeForNetworkCapabilities(Landroid/net/NetworkCapabilities;)I -Landroid/net/ConnectivityManager;->isAlwaysOnVpnPackageSupportedForUser(ILjava/lang/String;)Z -Landroid/net/ConnectivityManager;->isNetworkTypeWifi(I)Z -Landroid/net/ConnectivityManager;->legacyTypeForNetworkCapabilities(Landroid/net/NetworkCapabilities;)I -Landroid/net/ConnectivityManager;->LISTEN:I -Landroid/net/ConnectivityManager;->MAX_NETWORK_TYPE:I -Landroid/net/ConnectivityManager;->MAX_RADIO_TYPE:I -Landroid/net/ConnectivityManager;->mContext:Landroid/content/Context; -Landroid/net/ConnectivityManager;->MIN_NETWORK_TYPE:I -Landroid/net/ConnectivityManager;->mNetworkActivityListeners:Landroid/util/ArrayMap; -Landroid/net/ConnectivityManager;->mNMService:Landroid/os/INetworkManagementService; -Landroid/net/ConnectivityManager;->mNPManager:Landroid/net/INetworkPolicyManager; -Landroid/net/ConnectivityManager;->MULTIPATH_PREFERENCE_UNMETERED:I -Landroid/net/ConnectivityManager;->NETID_UNSET:I -Landroid/net/ConnectivityManager;->networkCapabilitiesForType(I)Landroid/net/NetworkCapabilities; -Landroid/net/ConnectivityManager;->PRIVATE_DNS_DEFAULT_MODE_FALLBACK:Ljava/lang/String; -Landroid/net/ConnectivityManager;->PRIVATE_DNS_MODE_OFF:Ljava/lang/String; -Landroid/net/ConnectivityManager;->PRIVATE_DNS_MODE_OPPORTUNISTIC:Ljava/lang/String; -Landroid/net/ConnectivityManager;->PRIVATE_DNS_MODE_PROVIDER_HOSTNAME:Ljava/lang/String; -Landroid/net/ConnectivityManager;->registerNetworkAgent(Landroid/os/Messenger;Landroid/net/NetworkInfo;Landroid/net/LinkProperties;Landroid/net/NetworkCapabilities;ILandroid/net/NetworkMisc;)I -Landroid/net/ConnectivityManager;->renewRequestLocked(Landroid/net/ConnectivityManager$LegacyRequest;)V -Landroid/net/ConnectivityManager;->reportInetCondition(II)V -Landroid/net/ConnectivityManager;->REQUEST:I -Landroid/net/ConnectivityManager;->requestNetwork(Landroid/net/NetworkRequest;Landroid/net/ConnectivityManager$NetworkCallback;IILandroid/os/Handler;)V -Landroid/net/ConnectivityManager;->REQUEST_ID_UNSET:I -Landroid/net/ConnectivityManager;->sCallbackHandler:Landroid/net/ConnectivityManager$CallbackHandler; -Landroid/net/ConnectivityManager;->sCallbacks:Ljava/util/HashMap; -Landroid/net/ConnectivityManager;->sendExpireMsgForFeature(Landroid/net/NetworkCapabilities;II)V -Landroid/net/ConnectivityManager;->sendRequestForNetwork(Landroid/net/NetworkCapabilities;Landroid/net/ConnectivityManager$NetworkCallback;IIILandroid/net/ConnectivityManager$CallbackHandler;)Landroid/net/NetworkRequest; -Landroid/net/ConnectivityManager;->setAcceptUnvalidated(Landroid/net/Network;ZZ)V -Landroid/net/ConnectivityManager;->setAlwaysOnVpnPackageForUser(ILjava/lang/String;Z)Z -Landroid/net/ConnectivityManager;->setAvoidUnvalidated(Landroid/net/Network;)V -Landroid/net/ConnectivityManager;->setGlobalProxy(Landroid/net/ProxyInfo;)V -Landroid/net/ConnectivityManager;->setProvisioningNotificationVisible(ZILjava/lang/String;)V -Landroid/net/ConnectivityManager;->sInstance:Landroid/net/ConnectivityManager; -Landroid/net/ConnectivityManager;->sLegacyTypeToCapability:Landroid/util/SparseIntArray; -Landroid/net/ConnectivityManager;->sLegacyTypeToTransport:Landroid/util/SparseIntArray; -Landroid/net/ConnectivityManager;->startCaptivePortalApp(Landroid/net/Network;)V -Landroid/net/ConnectivityManager;->TAG:Ljava/lang/String; -Landroid/net/ConnectivityManager;->TETHERING_INVALID:I -Landroid/net/ConnectivityManager;->TETHER_ERROR_DISABLE_NAT_ERROR:I -Landroid/net/ConnectivityManager;->TETHER_ERROR_ENABLE_NAT_ERROR:I -Landroid/net/ConnectivityManager;->TETHER_ERROR_IFACE_CFG_ERROR:I -Landroid/net/ConnectivityManager;->TETHER_ERROR_MASTER_ERROR:I -Landroid/net/ConnectivityManager;->TETHER_ERROR_NO_ERROR:I -Landroid/net/ConnectivityManager;->TETHER_ERROR_PROVISION_FAILED:I -Landroid/net/ConnectivityManager;->TETHER_ERROR_SERVICE_UNAVAIL:I -Landroid/net/ConnectivityManager;->TETHER_ERROR_TETHER_IFACE_ERROR:I -Landroid/net/ConnectivityManager;->TETHER_ERROR_UNAVAIL_IFACE:I -Landroid/net/ConnectivityManager;->TETHER_ERROR_UNKNOWN_IFACE:I -Landroid/net/ConnectivityManager;->TETHER_ERROR_UNSUPPORTED:I -Landroid/net/ConnectivityManager;->TETHER_ERROR_UNTETHER_IFACE_ERROR:I -Landroid/net/ConnectivityManager;->unsupportedStartingFrom(I)V -Landroid/net/ConnectivityManager;->updateLockdownVpn()Z Landroid/net/ConnectivityMetricsEvent;-><init>()V Landroid/net/ConnectivityMetricsEvent;-><init>(Landroid/os/Parcel;)V Landroid/net/ConnectivityMetricsEvent;->CREATOR:Landroid/os/Parcelable$Creator; @@ -35494,12 +35335,6 @@ Landroid/net/ConnectivityMetricsEvent;->ifname:Ljava/lang/String; Landroid/net/ConnectivityMetricsEvent;->netId:I Landroid/net/ConnectivityMetricsEvent;->timestamp:J Landroid/net/ConnectivityMetricsEvent;->transports:J -Landroid/net/ConnectivityThread$Singleton;-><init>()V -Landroid/net/ConnectivityThread$Singleton;->INSTANCE:Landroid/net/ConnectivityThread; -Landroid/net/ConnectivityThread;-><init>()V -Landroid/net/ConnectivityThread;->createInstance()Landroid/net/ConnectivityThread; -Landroid/net/ConnectivityThread;->get()Landroid/net/ConnectivityThread; -Landroid/net/ConnectivityThread;->getInstanceLooper()Landroid/os/Looper; Landroid/net/Credentials;->gid:I Landroid/net/Credentials;->pid:I Landroid/net/Credentials;->uid:I @@ -35510,9 +35345,6 @@ Landroid/net/DataUsageRequest;->requestId:I Landroid/net/DataUsageRequest;->REQUEST_ID_UNSET:I Landroid/net/DataUsageRequest;->template:Landroid/net/NetworkTemplate; Landroid/net/DataUsageRequest;->thresholdInBytes:J -Landroid/net/DhcpInfo;-><init>(Landroid/net/DhcpInfo;)V -Landroid/net/DhcpInfo;->CREATOR:Landroid/os/Parcelable$Creator; -Landroid/net/DhcpInfo;->putAddress(Ljava/lang/StringBuffer;I)V Landroid/net/DhcpResults;->addDns(Ljava/lang/String;)Z Landroid/net/DhcpResults;->clear()V Landroid/net/DhcpResults;->CREATOR:Landroid/os/Parcelable$Creator; @@ -35566,224 +35398,6 @@ Landroid/net/http/X509TrustManagerExtensions;->mCheckServerTrusted:Ljava/lang/re Landroid/net/http/X509TrustManagerExtensions;->mDelegate:Lcom/android/org/conscrypt/TrustManagerImpl; Landroid/net/http/X509TrustManagerExtensions;->mIsSameTrustConfiguration:Ljava/lang/reflect/Method; Landroid/net/http/X509TrustManagerExtensions;->mTrustManager:Ljavax/net/ssl/X509TrustManager; -Landroid/net/ICaptivePortal$Stub$Proxy;-><init>(Landroid/os/IBinder;)V -Landroid/net/ICaptivePortal$Stub$Proxy;->appResponse(I)V -Landroid/net/ICaptivePortal$Stub$Proxy;->getInterfaceDescriptor()Ljava/lang/String; -Landroid/net/ICaptivePortal$Stub$Proxy;->mRemote:Landroid/os/IBinder; -Landroid/net/ICaptivePortal$Stub;-><init>()V -Landroid/net/ICaptivePortal$Stub;->asInterface(Landroid/os/IBinder;)Landroid/net/ICaptivePortal; -Landroid/net/ICaptivePortal$Stub;->DESCRIPTOR:Ljava/lang/String; -Landroid/net/ICaptivePortal$Stub;->TRANSACTION_appResponse:I -Landroid/net/ICaptivePortal;->appResponse(I)V -Landroid/net/IConnectivityManager$Stub$Proxy;->addVpnAddress(Ljava/lang/String;I)Z -Landroid/net/IConnectivityManager$Stub$Proxy;->checkMobileProvisioning(I)I -Landroid/net/IConnectivityManager$Stub$Proxy;->establishVpn(Lcom/android/internal/net/VpnConfig;)Landroid/os/ParcelFileDescriptor; -Landroid/net/IConnectivityManager$Stub$Proxy;->factoryReset()V -Landroid/net/IConnectivityManager$Stub$Proxy;->getActiveNetwork()Landroid/net/Network; -Landroid/net/IConnectivityManager$Stub$Proxy;->getActiveNetworkForUid(IZ)Landroid/net/Network; -Landroid/net/IConnectivityManager$Stub$Proxy;->getActiveNetworkInfoForUid(IZ)Landroid/net/NetworkInfo; -Landroid/net/IConnectivityManager$Stub$Proxy;->getActiveNetworkQuotaInfo()Landroid/net/NetworkQuotaInfo; -Landroid/net/IConnectivityManager$Stub$Proxy;->getAllNetworkState()[Landroid/net/NetworkState; -Landroid/net/IConnectivityManager$Stub$Proxy;->getAllVpnInfo()[Lcom/android/internal/net/VpnInfo; -Landroid/net/IConnectivityManager$Stub$Proxy;->getAlwaysOnVpnPackage(I)Ljava/lang/String; -Landroid/net/IConnectivityManager$Stub$Proxy;->getCaptivePortalServerUrl()Ljava/lang/String; -Landroid/net/IConnectivityManager$Stub$Proxy;->getDefaultNetworkCapabilitiesForUser(I)[Landroid/net/NetworkCapabilities; -Landroid/net/IConnectivityManager$Stub$Proxy;->getGlobalProxy()Landroid/net/ProxyInfo; -Landroid/net/IConnectivityManager$Stub$Proxy;->getInterfaceDescriptor()Ljava/lang/String; -Landroid/net/IConnectivityManager$Stub$Proxy;->getLastTetherError(Ljava/lang/String;)I -Landroid/net/IConnectivityManager$Stub$Proxy;->getLegacyVpnInfo(I)Lcom/android/internal/net/LegacyVpnInfo; -Landroid/net/IConnectivityManager$Stub$Proxy;->getLinkProperties(Landroid/net/Network;)Landroid/net/LinkProperties; -Landroid/net/IConnectivityManager$Stub$Proxy;->getLinkPropertiesForType(I)Landroid/net/LinkProperties; -Landroid/net/IConnectivityManager$Stub$Proxy;->getMobileProvisioningUrl()Ljava/lang/String; -Landroid/net/IConnectivityManager$Stub$Proxy;->getMultipathPreference(Landroid/net/Network;)I -Landroid/net/IConnectivityManager$Stub$Proxy;->getNetworkCapabilities(Landroid/net/Network;)Landroid/net/NetworkCapabilities; -Landroid/net/IConnectivityManager$Stub$Proxy;->getNetworkForType(I)Landroid/net/Network; -Landroid/net/IConnectivityManager$Stub$Proxy;->getNetworkInfo(I)Landroid/net/NetworkInfo; -Landroid/net/IConnectivityManager$Stub$Proxy;->getNetworkInfoForUid(Landroid/net/Network;IZ)Landroid/net/NetworkInfo; -Landroid/net/IConnectivityManager$Stub$Proxy;->getNetworkWatchlistConfigHash()[B -Landroid/net/IConnectivityManager$Stub$Proxy;->getProxyForNetwork(Landroid/net/Network;)Landroid/net/ProxyInfo; -Landroid/net/IConnectivityManager$Stub$Proxy;->getRestoreDefaultNetworkDelay(I)I -Landroid/net/IConnectivityManager$Stub$Proxy;->getTetherableBluetoothRegexs()[Ljava/lang/String; -Landroid/net/IConnectivityManager$Stub$Proxy;->getTetherableWifiRegexs()[Ljava/lang/String; -Landroid/net/IConnectivityManager$Stub$Proxy;->getTetheredDhcpRanges()[Ljava/lang/String; -Landroid/net/IConnectivityManager$Stub$Proxy;->getTetheringErroredIfaces()[Ljava/lang/String; -Landroid/net/IConnectivityManager$Stub$Proxy;->getVpnConfig(I)Lcom/android/internal/net/VpnConfig; -Landroid/net/IConnectivityManager$Stub$Proxy;->isActiveNetworkMetered()Z -Landroid/net/IConnectivityManager$Stub$Proxy;->isAlwaysOnVpnPackageSupported(ILjava/lang/String;)Z -Landroid/net/IConnectivityManager$Stub$Proxy;->isNetworkSupported(I)Z -Landroid/net/IConnectivityManager$Stub$Proxy;->isTetheringSupported(Ljava/lang/String;)Z -Landroid/net/IConnectivityManager$Stub$Proxy;->listenForNetwork(Landroid/net/NetworkCapabilities;Landroid/os/Messenger;Landroid/os/IBinder;)Landroid/net/NetworkRequest; -Landroid/net/IConnectivityManager$Stub$Proxy;->pendingListenForNetwork(Landroid/net/NetworkCapabilities;Landroid/app/PendingIntent;)V -Landroid/net/IConnectivityManager$Stub$Proxy;->pendingRequestForNetwork(Landroid/net/NetworkCapabilities;Landroid/app/PendingIntent;)Landroid/net/NetworkRequest; -Landroid/net/IConnectivityManager$Stub$Proxy;->prepareVpn(Ljava/lang/String;Ljava/lang/String;I)Z -Landroid/net/IConnectivityManager$Stub$Proxy;->registerNetworkAgent(Landroid/os/Messenger;Landroid/net/NetworkInfo;Landroid/net/LinkProperties;Landroid/net/NetworkCapabilities;ILandroid/net/NetworkMisc;)I -Landroid/net/IConnectivityManager$Stub$Proxy;->registerNetworkFactory(Landroid/os/Messenger;Ljava/lang/String;)V -Landroid/net/IConnectivityManager$Stub$Proxy;->releaseNetworkRequest(Landroid/net/NetworkRequest;)V -Landroid/net/IConnectivityManager$Stub$Proxy;->releasePendingNetworkRequest(Landroid/app/PendingIntent;)V -Landroid/net/IConnectivityManager$Stub$Proxy;->removeVpnAddress(Ljava/lang/String;I)Z -Landroid/net/IConnectivityManager$Stub$Proxy;->reportInetCondition(II)V -Landroid/net/IConnectivityManager$Stub$Proxy;->reportNetworkConnectivity(Landroid/net/Network;Z)V -Landroid/net/IConnectivityManager$Stub$Proxy;->requestBandwidthUpdate(Landroid/net/Network;)Z -Landroid/net/IConnectivityManager$Stub$Proxy;->requestNetwork(Landroid/net/NetworkCapabilities;Landroid/os/Messenger;ILandroid/os/IBinder;I)Landroid/net/NetworkRequest; -Landroid/net/IConnectivityManager$Stub$Proxy;->requestRouteToHostAddress(I[B)Z -Landroid/net/IConnectivityManager$Stub$Proxy;->setAcceptUnvalidated(Landroid/net/Network;ZZ)V -Landroid/net/IConnectivityManager$Stub$Proxy;->setAirplaneMode(Z)V -Landroid/net/IConnectivityManager$Stub$Proxy;->setAlwaysOnVpnPackage(ILjava/lang/String;Z)Z -Landroid/net/IConnectivityManager$Stub$Proxy;->setAvoidUnvalidated(Landroid/net/Network;)V -Landroid/net/IConnectivityManager$Stub$Proxy;->setGlobalProxy(Landroid/net/ProxyInfo;)V -Landroid/net/IConnectivityManager$Stub$Proxy;->setProvisioningNotificationVisible(ZILjava/lang/String;)V -Landroid/net/IConnectivityManager$Stub$Proxy;->setUnderlyingNetworksForVpn([Landroid/net/Network;)Z -Landroid/net/IConnectivityManager$Stub$Proxy;->setUsbTethering(ZLjava/lang/String;)I -Landroid/net/IConnectivityManager$Stub$Proxy;->setVpnPackageAuthorization(Ljava/lang/String;IZ)V -Landroid/net/IConnectivityManager$Stub$Proxy;->startCaptivePortalApp(Landroid/net/Network;)V -Landroid/net/IConnectivityManager$Stub$Proxy;->startLegacyVpn(Lcom/android/internal/net/VpnProfile;)V -Landroid/net/IConnectivityManager$Stub$Proxy;->startNattKeepalive(Landroid/net/Network;ILandroid/os/Messenger;Landroid/os/IBinder;Ljava/lang/String;ILjava/lang/String;)V -Landroid/net/IConnectivityManager$Stub$Proxy;->startTethering(ILandroid/os/ResultReceiver;ZLjava/lang/String;)V -Landroid/net/IConnectivityManager$Stub$Proxy;->stopKeepalive(Landroid/net/Network;I)V -Landroid/net/IConnectivityManager$Stub$Proxy;->stopTethering(ILjava/lang/String;)V -Landroid/net/IConnectivityManager$Stub$Proxy;->tether(Ljava/lang/String;Ljava/lang/String;)I -Landroid/net/IConnectivityManager$Stub$Proxy;->unregisterNetworkFactory(Landroid/os/Messenger;)V -Landroid/net/IConnectivityManager$Stub$Proxy;->untether(Ljava/lang/String;Ljava/lang/String;)I -Landroid/net/IConnectivityManager$Stub$Proxy;->updateLockdownVpn()Z -Landroid/net/IConnectivityManager$Stub;->DESCRIPTOR:Ljava/lang/String; -Landroid/net/IConnectivityManager$Stub;->TRANSACTION_addVpnAddress:I -Landroid/net/IConnectivityManager$Stub;->TRANSACTION_checkMobileProvisioning:I -Landroid/net/IConnectivityManager$Stub;->TRANSACTION_establishVpn:I -Landroid/net/IConnectivityManager$Stub;->TRANSACTION_factoryReset:I -Landroid/net/IConnectivityManager$Stub;->TRANSACTION_getActiveLinkProperties:I -Landroid/net/IConnectivityManager$Stub;->TRANSACTION_getActiveNetwork:I -Landroid/net/IConnectivityManager$Stub;->TRANSACTION_getActiveNetworkForUid:I -Landroid/net/IConnectivityManager$Stub;->TRANSACTION_getActiveNetworkInfo:I -Landroid/net/IConnectivityManager$Stub;->TRANSACTION_getActiveNetworkInfoForUid:I -Landroid/net/IConnectivityManager$Stub;->TRANSACTION_getActiveNetworkQuotaInfo:I -Landroid/net/IConnectivityManager$Stub;->TRANSACTION_getAllNetworkInfo:I -Landroid/net/IConnectivityManager$Stub;->TRANSACTION_getAllNetworks:I -Landroid/net/IConnectivityManager$Stub;->TRANSACTION_getAllNetworkState:I -Landroid/net/IConnectivityManager$Stub;->TRANSACTION_getAllVpnInfo:I -Landroid/net/IConnectivityManager$Stub;->TRANSACTION_getAlwaysOnVpnPackage:I -Landroid/net/IConnectivityManager$Stub;->TRANSACTION_getCaptivePortalServerUrl:I -Landroid/net/IConnectivityManager$Stub;->TRANSACTION_getDefaultNetworkCapabilitiesForUser:I -Landroid/net/IConnectivityManager$Stub;->TRANSACTION_getGlobalProxy:I -Landroid/net/IConnectivityManager$Stub;->TRANSACTION_getLastTetherError:I -Landroid/net/IConnectivityManager$Stub;->TRANSACTION_getLegacyVpnInfo:I -Landroid/net/IConnectivityManager$Stub;->TRANSACTION_getLinkProperties:I -Landroid/net/IConnectivityManager$Stub;->TRANSACTION_getLinkPropertiesForType:I -Landroid/net/IConnectivityManager$Stub;->TRANSACTION_getMobileProvisioningUrl:I -Landroid/net/IConnectivityManager$Stub;->TRANSACTION_getMultipathPreference:I -Landroid/net/IConnectivityManager$Stub;->TRANSACTION_getNetworkCapabilities:I -Landroid/net/IConnectivityManager$Stub;->TRANSACTION_getNetworkForType:I -Landroid/net/IConnectivityManager$Stub;->TRANSACTION_getNetworkInfo:I -Landroid/net/IConnectivityManager$Stub;->TRANSACTION_getNetworkInfoForUid:I -Landroid/net/IConnectivityManager$Stub;->TRANSACTION_getNetworkWatchlistConfigHash:I -Landroid/net/IConnectivityManager$Stub;->TRANSACTION_getProxyForNetwork:I -Landroid/net/IConnectivityManager$Stub;->TRANSACTION_getRestoreDefaultNetworkDelay:I -Landroid/net/IConnectivityManager$Stub;->TRANSACTION_getTetherableBluetoothRegexs:I -Landroid/net/IConnectivityManager$Stub;->TRANSACTION_getTetherableIfaces:I -Landroid/net/IConnectivityManager$Stub;->TRANSACTION_getTetherableUsbRegexs:I -Landroid/net/IConnectivityManager$Stub;->TRANSACTION_getTetherableWifiRegexs:I -Landroid/net/IConnectivityManager$Stub;->TRANSACTION_getTetheredDhcpRanges:I -Landroid/net/IConnectivityManager$Stub;->TRANSACTION_getTetheredIfaces:I -Landroid/net/IConnectivityManager$Stub;->TRANSACTION_getTetheringErroredIfaces:I -Landroid/net/IConnectivityManager$Stub;->TRANSACTION_getVpnConfig:I -Landroid/net/IConnectivityManager$Stub;->TRANSACTION_isActiveNetworkMetered:I -Landroid/net/IConnectivityManager$Stub;->TRANSACTION_isAlwaysOnVpnPackageSupported:I -Landroid/net/IConnectivityManager$Stub;->TRANSACTION_isNetworkSupported:I -Landroid/net/IConnectivityManager$Stub;->TRANSACTION_isTetheringSupported:I -Landroid/net/IConnectivityManager$Stub;->TRANSACTION_listenForNetwork:I -Landroid/net/IConnectivityManager$Stub;->TRANSACTION_pendingListenForNetwork:I -Landroid/net/IConnectivityManager$Stub;->TRANSACTION_pendingRequestForNetwork:I -Landroid/net/IConnectivityManager$Stub;->TRANSACTION_prepareVpn:I -Landroid/net/IConnectivityManager$Stub;->TRANSACTION_registerNetworkAgent:I -Landroid/net/IConnectivityManager$Stub;->TRANSACTION_registerNetworkFactory:I -Landroid/net/IConnectivityManager$Stub;->TRANSACTION_releaseNetworkRequest:I -Landroid/net/IConnectivityManager$Stub;->TRANSACTION_releasePendingNetworkRequest:I -Landroid/net/IConnectivityManager$Stub;->TRANSACTION_removeVpnAddress:I -Landroid/net/IConnectivityManager$Stub;->TRANSACTION_reportInetCondition:I -Landroid/net/IConnectivityManager$Stub;->TRANSACTION_reportNetworkConnectivity:I -Landroid/net/IConnectivityManager$Stub;->TRANSACTION_requestBandwidthUpdate:I -Landroid/net/IConnectivityManager$Stub;->TRANSACTION_requestNetwork:I -Landroid/net/IConnectivityManager$Stub;->TRANSACTION_requestRouteToHostAddress:I -Landroid/net/IConnectivityManager$Stub;->TRANSACTION_setAcceptUnvalidated:I -Landroid/net/IConnectivityManager$Stub;->TRANSACTION_setAirplaneMode:I -Landroid/net/IConnectivityManager$Stub;->TRANSACTION_setAlwaysOnVpnPackage:I -Landroid/net/IConnectivityManager$Stub;->TRANSACTION_setAvoidUnvalidated:I -Landroid/net/IConnectivityManager$Stub;->TRANSACTION_setGlobalProxy:I -Landroid/net/IConnectivityManager$Stub;->TRANSACTION_setProvisioningNotificationVisible:I -Landroid/net/IConnectivityManager$Stub;->TRANSACTION_setUnderlyingNetworksForVpn:I -Landroid/net/IConnectivityManager$Stub;->TRANSACTION_setUsbTethering:I -Landroid/net/IConnectivityManager$Stub;->TRANSACTION_setVpnPackageAuthorization:I -Landroid/net/IConnectivityManager$Stub;->TRANSACTION_startCaptivePortalApp:I -Landroid/net/IConnectivityManager$Stub;->TRANSACTION_startLegacyVpn:I -Landroid/net/IConnectivityManager$Stub;->TRANSACTION_startNattKeepalive:I -Landroid/net/IConnectivityManager$Stub;->TRANSACTION_startTethering:I -Landroid/net/IConnectivityManager$Stub;->TRANSACTION_stopKeepalive:I -Landroid/net/IConnectivityManager$Stub;->TRANSACTION_stopTethering:I -Landroid/net/IConnectivityManager$Stub;->TRANSACTION_tether:I -Landroid/net/IConnectivityManager$Stub;->TRANSACTION_unregisterNetworkFactory:I -Landroid/net/IConnectivityManager$Stub;->TRANSACTION_untether:I -Landroid/net/IConnectivityManager$Stub;->TRANSACTION_updateLockdownVpn:I -Landroid/net/IConnectivityManager;->addVpnAddress(Ljava/lang/String;I)Z -Landroid/net/IConnectivityManager;->checkMobileProvisioning(I)I -Landroid/net/IConnectivityManager;->establishVpn(Lcom/android/internal/net/VpnConfig;)Landroid/os/ParcelFileDescriptor; -Landroid/net/IConnectivityManager;->factoryReset()V -Landroid/net/IConnectivityManager;->getActiveNetwork()Landroid/net/Network; -Landroid/net/IConnectivityManager;->getActiveNetworkForUid(IZ)Landroid/net/Network; -Landroid/net/IConnectivityManager;->getActiveNetworkInfoForUid(IZ)Landroid/net/NetworkInfo; -Landroid/net/IConnectivityManager;->getActiveNetworkQuotaInfo()Landroid/net/NetworkQuotaInfo; -Landroid/net/IConnectivityManager;->getAllNetworks()[Landroid/net/Network; -Landroid/net/IConnectivityManager;->getAllVpnInfo()[Lcom/android/internal/net/VpnInfo; -Landroid/net/IConnectivityManager;->getAlwaysOnVpnPackage(I)Ljava/lang/String; -Landroid/net/IConnectivityManager;->getCaptivePortalServerUrl()Ljava/lang/String; -Landroid/net/IConnectivityManager;->getDefaultNetworkCapabilitiesForUser(I)[Landroid/net/NetworkCapabilities; -Landroid/net/IConnectivityManager;->getGlobalProxy()Landroid/net/ProxyInfo; -Landroid/net/IConnectivityManager;->getLegacyVpnInfo(I)Lcom/android/internal/net/LegacyVpnInfo; -Landroid/net/IConnectivityManager;->getLinkProperties(Landroid/net/Network;)Landroid/net/LinkProperties; -Landroid/net/IConnectivityManager;->getLinkPropertiesForType(I)Landroid/net/LinkProperties; -Landroid/net/IConnectivityManager;->getMobileProvisioningUrl()Ljava/lang/String; -Landroid/net/IConnectivityManager;->getMultipathPreference(Landroid/net/Network;)I -Landroid/net/IConnectivityManager;->getNetworkCapabilities(Landroid/net/Network;)Landroid/net/NetworkCapabilities; -Landroid/net/IConnectivityManager;->getNetworkForType(I)Landroid/net/Network; -Landroid/net/IConnectivityManager;->getNetworkInfoForUid(Landroid/net/Network;IZ)Landroid/net/NetworkInfo; -Landroid/net/IConnectivityManager;->getNetworkWatchlistConfigHash()[B -Landroid/net/IConnectivityManager;->getProxyForNetwork(Landroid/net/Network;)Landroid/net/ProxyInfo; -Landroid/net/IConnectivityManager;->getRestoreDefaultNetworkDelay(I)I -Landroid/net/IConnectivityManager;->getTetherableBluetoothRegexs()[Ljava/lang/String; -Landroid/net/IConnectivityManager;->getTetheredDhcpRanges()[Ljava/lang/String; -Landroid/net/IConnectivityManager;->getVpnConfig(I)Lcom/android/internal/net/VpnConfig; -Landroid/net/IConnectivityManager;->isActiveNetworkMetered()Z -Landroid/net/IConnectivityManager;->isAlwaysOnVpnPackageSupported(ILjava/lang/String;)Z -Landroid/net/IConnectivityManager;->isNetworkSupported(I)Z -Landroid/net/IConnectivityManager;->isTetheringSupported(Ljava/lang/String;)Z -Landroid/net/IConnectivityManager;->listenForNetwork(Landroid/net/NetworkCapabilities;Landroid/os/Messenger;Landroid/os/IBinder;)Landroid/net/NetworkRequest; -Landroid/net/IConnectivityManager;->pendingListenForNetwork(Landroid/net/NetworkCapabilities;Landroid/app/PendingIntent;)V -Landroid/net/IConnectivityManager;->pendingRequestForNetwork(Landroid/net/NetworkCapabilities;Landroid/app/PendingIntent;)Landroid/net/NetworkRequest; -Landroid/net/IConnectivityManager;->prepareVpn(Ljava/lang/String;Ljava/lang/String;I)Z -Landroid/net/IConnectivityManager;->registerNetworkAgent(Landroid/os/Messenger;Landroid/net/NetworkInfo;Landroid/net/LinkProperties;Landroid/net/NetworkCapabilities;ILandroid/net/NetworkMisc;)I -Landroid/net/IConnectivityManager;->registerNetworkFactory(Landroid/os/Messenger;Ljava/lang/String;)V -Landroid/net/IConnectivityManager;->releaseNetworkRequest(Landroid/net/NetworkRequest;)V -Landroid/net/IConnectivityManager;->releasePendingNetworkRequest(Landroid/app/PendingIntent;)V -Landroid/net/IConnectivityManager;->removeVpnAddress(Ljava/lang/String;I)Z -Landroid/net/IConnectivityManager;->reportNetworkConnectivity(Landroid/net/Network;Z)V -Landroid/net/IConnectivityManager;->requestBandwidthUpdate(Landroid/net/Network;)Z -Landroid/net/IConnectivityManager;->requestNetwork(Landroid/net/NetworkCapabilities;Landroid/os/Messenger;ILandroid/os/IBinder;I)Landroid/net/NetworkRequest; -Landroid/net/IConnectivityManager;->requestRouteToHostAddress(I[B)Z -Landroid/net/IConnectivityManager;->setAcceptUnvalidated(Landroid/net/Network;ZZ)V -Landroid/net/IConnectivityManager;->setAlwaysOnVpnPackage(ILjava/lang/String;Z)Z -Landroid/net/IConnectivityManager;->setAvoidUnvalidated(Landroid/net/Network;)V -Landroid/net/IConnectivityManager;->setGlobalProxy(Landroid/net/ProxyInfo;)V -Landroid/net/IConnectivityManager;->setProvisioningNotificationVisible(ZILjava/lang/String;)V -Landroid/net/IConnectivityManager;->setUnderlyingNetworksForVpn([Landroid/net/Network;)Z -Landroid/net/IConnectivityManager;->setUsbTethering(ZLjava/lang/String;)I -Landroid/net/IConnectivityManager;->setVpnPackageAuthorization(Ljava/lang/String;IZ)V -Landroid/net/IConnectivityManager;->startCaptivePortalApp(Landroid/net/Network;)V -Landroid/net/IConnectivityManager;->startNattKeepalive(Landroid/net/Network;ILandroid/os/Messenger;Landroid/os/IBinder;Ljava/lang/String;ILjava/lang/String;)V -Landroid/net/IConnectivityManager;->startTethering(ILandroid/os/ResultReceiver;ZLjava/lang/String;)V -Landroid/net/IConnectivityManager;->stopKeepalive(Landroid/net/Network;I)V -Landroid/net/IConnectivityManager;->stopTethering(ILjava/lang/String;)V -Landroid/net/IConnectivityManager;->tether(Ljava/lang/String;Ljava/lang/String;)I -Landroid/net/IConnectivityManager;->unregisterNetworkFactory(Landroid/os/Messenger;)V -Landroid/net/IConnectivityManager;->untether(Ljava/lang/String;Ljava/lang/String;)I -Landroid/net/IConnectivityManager;->updateLockdownVpn()Z Landroid/net/IEthernetManager$Stub$Proxy;-><init>(Landroid/os/IBinder;)V Landroid/net/IEthernetManager$Stub$Proxy;->addListener(Landroid/net/IEthernetServiceListener;)V Landroid/net/IEthernetManager$Stub$Proxy;->getAvailableInterfaces()[Ljava/lang/String; @@ -36300,41 +35914,6 @@ Landroid/net/InterfaceConfiguration;->mFlags:Ljava/util/HashSet; Landroid/net/InterfaceConfiguration;->mHwAddr:Ljava/lang/String; Landroid/net/InterfaceConfiguration;->setHardwareAddress(Ljava/lang/String;)V Landroid/net/InterfaceConfiguration;->validateFlag(Ljava/lang/String;)V -Landroid/net/IpConfiguration$IpAssignment;->DHCP:Landroid/net/IpConfiguration$IpAssignment; -Landroid/net/IpConfiguration$IpAssignment;->UNASSIGNED:Landroid/net/IpConfiguration$IpAssignment; -Landroid/net/IpConfiguration$IpAssignment;->valueOf(Ljava/lang/String;)Landroid/net/IpConfiguration$IpAssignment; -Landroid/net/IpConfiguration$IpAssignment;->values()[Landroid/net/IpConfiguration$IpAssignment; -Landroid/net/IpConfiguration$ProxySettings;->PAC:Landroid/net/IpConfiguration$ProxySettings; -Landroid/net/IpConfiguration$ProxySettings;->STATIC:Landroid/net/IpConfiguration$ProxySettings; -Landroid/net/IpConfiguration$ProxySettings;->UNASSIGNED:Landroid/net/IpConfiguration$ProxySettings; -Landroid/net/IpConfiguration$ProxySettings;->valueOf(Ljava/lang/String;)Landroid/net/IpConfiguration$ProxySettings; -Landroid/net/IpConfiguration$ProxySettings;->values()[Landroid/net/IpConfiguration$ProxySettings; -Landroid/net/IpConfiguration;-><init>()V -Landroid/net/IpConfiguration;-><init>(Landroid/net/IpConfiguration;)V -Landroid/net/IpConfiguration;->CREATOR:Landroid/os/Parcelable$Creator; -Landroid/net/IpConfiguration;->getHttpProxy()Landroid/net/ProxyInfo; -Landroid/net/IpConfiguration;->getIpAssignment()Landroid/net/IpConfiguration$IpAssignment; -Landroid/net/IpConfiguration;->getProxySettings()Landroid/net/IpConfiguration$ProxySettings; -Landroid/net/IpConfiguration;->getStaticIpConfiguration()Landroid/net/StaticIpConfiguration; -Landroid/net/IpConfiguration;->init(Landroid/net/IpConfiguration$IpAssignment;Landroid/net/IpConfiguration$ProxySettings;Landroid/net/StaticIpConfiguration;Landroid/net/ProxyInfo;)V -Landroid/net/IpConfiguration;->ipAssignment:Landroid/net/IpConfiguration$IpAssignment; -Landroid/net/IpConfiguration;->proxySettings:Landroid/net/IpConfiguration$ProxySettings; -Landroid/net/IpConfiguration;->setHttpProxy(Landroid/net/ProxyInfo;)V -Landroid/net/IpConfiguration;->setIpAssignment(Landroid/net/IpConfiguration$IpAssignment;)V -Landroid/net/IpConfiguration;->setProxySettings(Landroid/net/IpConfiguration$ProxySettings;)V -Landroid/net/IpConfiguration;->setStaticIpConfiguration(Landroid/net/StaticIpConfiguration;)V -Landroid/net/IpConfiguration;->staticIpConfiguration:Landroid/net/StaticIpConfiguration; -Landroid/net/IpConfiguration;->TAG:Ljava/lang/String; -Landroid/net/IpPrefix;-><init>(Ljava/lang/String;)V -Landroid/net/IpPrefix;-><init>(Ljava/net/InetAddress;I)V -Landroid/net/IpPrefix;-><init>([BI)V -Landroid/net/IpPrefix;->address:[B -Landroid/net/IpPrefix;->checkAndMaskAddressAndPrefixLength()V -Landroid/net/IpPrefix;->containsPrefix(Landroid/net/IpPrefix;)Z -Landroid/net/IpPrefix;->isIPv4()Z -Landroid/net/IpPrefix;->isIPv6()Z -Landroid/net/IpPrefix;->lengthComparator()Ljava/util/Comparator; -Landroid/net/IpPrefix;->prefixLength:I Landroid/net/IpSecAlgorithm;->checkValidOrThrow(Ljava/lang/String;II)V Landroid/net/IpSecAlgorithm;->CRYPT_NULL:Ljava/lang/String; Landroid/net/IpSecAlgorithm;->equals(Landroid/net/IpSecAlgorithm;Landroid/net/IpSecAlgorithm;)Z @@ -36516,73 +36095,6 @@ Landroid/net/ITetheringStatsProvider$Stub;->TRANSACTION_setInterfaceQuota:I Landroid/net/ITetheringStatsProvider;->getTetherStats(I)Landroid/net/NetworkStats; Landroid/net/ITetheringStatsProvider;->QUOTA_UNLIMITED:I Landroid/net/ITetheringStatsProvider;->setInterfaceQuota(Ljava/lang/String;J)V -Landroid/net/KeepalivePacketData$InvalidPacketException;-><init>(I)V -Landroid/net/KeepalivePacketData$InvalidPacketException;->error:I -Landroid/net/KeepalivePacketData;-><init>(Landroid/os/Parcel;)V -Landroid/net/KeepalivePacketData;-><init>(Ljava/net/InetAddress;ILjava/net/InetAddress;I[B)V -Landroid/net/KeepalivePacketData;->CREATOR:Landroid/os/Parcelable$Creator; -Landroid/net/KeepalivePacketData;->dstAddress:Ljava/net/InetAddress; -Landroid/net/KeepalivePacketData;->dstPort:I -Landroid/net/KeepalivePacketData;->getPacket()[B -Landroid/net/KeepalivePacketData;->IPV4_HEADER_LENGTH:I -Landroid/net/KeepalivePacketData;->mPacket:[B -Landroid/net/KeepalivePacketData;->nattKeepalivePacket(Ljava/net/InetAddress;ILjava/net/InetAddress;I)Landroid/net/KeepalivePacketData; -Landroid/net/KeepalivePacketData;->srcAddress:Ljava/net/InetAddress; -Landroid/net/KeepalivePacketData;->srcPort:I -Landroid/net/KeepalivePacketData;->TAG:Ljava/lang/String; -Landroid/net/KeepalivePacketData;->UDP_HEADER_LENGTH:I -Landroid/net/LinkAddress;-><init>(Ljava/lang/String;II)V -Landroid/net/LinkAddress;-><init>(Ljava/net/InetAddress;III)V -Landroid/net/LinkAddress;-><init>(Ljava/net/InterfaceAddress;)V -Landroid/net/LinkAddress;->flags:I -Landroid/net/LinkAddress;->init(Ljava/net/InetAddress;III)V -Landroid/net/LinkAddress;->isGlobalPreferred()Z -Landroid/net/LinkAddress;->isIPv4()Z -Landroid/net/LinkAddress;->isIPv6ULA()Z -Landroid/net/LinkAddress;->scope:I -Landroid/net/LinkAddress;->scopeForUnicastAddress(Ljava/net/InetAddress;)I -Landroid/net/LinkProperties$CompareResult;-><init>()V -Landroid/net/LinkProperties$CompareResult;-><init>(Ljava/util/Collection;Ljava/util/Collection;)V -Landroid/net/LinkProperties$CompareResult;->added:Ljava/util/List; -Landroid/net/LinkProperties$CompareResult;->removed:Ljava/util/List; -Landroid/net/LinkProperties$ProvisioningChange;->valueOf(Ljava/lang/String;)Landroid/net/LinkProperties$ProvisioningChange; -Landroid/net/LinkProperties;->addValidatedPrivateDnsServer(Ljava/net/InetAddress;)Z -Landroid/net/LinkProperties;->compareAddresses(Landroid/net/LinkProperties;)Landroid/net/LinkProperties$CompareResult; -Landroid/net/LinkProperties;->compareAllInterfaceNames(Landroid/net/LinkProperties;)Landroid/net/LinkProperties$CompareResult; -Landroid/net/LinkProperties;->compareAllRoutes(Landroid/net/LinkProperties;)Landroid/net/LinkProperties$CompareResult; -Landroid/net/LinkProperties;->compareDnses(Landroid/net/LinkProperties;)Landroid/net/LinkProperties$CompareResult; -Landroid/net/LinkProperties;->compareValidatedPrivateDnses(Landroid/net/LinkProperties;)Landroid/net/LinkProperties$CompareResult; -Landroid/net/LinkProperties;->ensureDirectlyConnectedRoutes()V -Landroid/net/LinkProperties;->findLinkAddressIndex(Landroid/net/LinkAddress;)I -Landroid/net/LinkProperties;->getValidatedPrivateDnsServers()Ljava/util/List; -Landroid/net/LinkProperties;->hasIPv4AddressOnInterface(Ljava/lang/String;)Z -Landroid/net/LinkProperties;->isIdenticalMtu(Landroid/net/LinkProperties;)Z -Landroid/net/LinkProperties;->isIdenticalPrivateDns(Landroid/net/LinkProperties;)Z -Landroid/net/LinkProperties;->isIdenticalTcpBufferSizes(Landroid/net/LinkProperties;)Z -Landroid/net/LinkProperties;->isIdenticalValidatedPrivateDnses(Landroid/net/LinkProperties;)Z -Landroid/net/LinkProperties;->isIPv4Provisioned()Z -Landroid/net/LinkProperties;->isValidMtu(IZ)Z -Landroid/net/LinkProperties;->MAX_MTU:I -Landroid/net/LinkProperties;->mDnses:Ljava/util/ArrayList; -Landroid/net/LinkProperties;->mDomains:Ljava/lang/String; -Landroid/net/LinkProperties;->mHttpProxy:Landroid/net/ProxyInfo; -Landroid/net/LinkProperties;->MIN_MTU:I -Landroid/net/LinkProperties;->MIN_MTU_V6:I -Landroid/net/LinkProperties;->mLinkAddresses:Ljava/util/ArrayList; -Landroid/net/LinkProperties;->mMtu:I -Landroid/net/LinkProperties;->mPrivateDnsServerName:Ljava/lang/String; -Landroid/net/LinkProperties;->mRoutes:Ljava/util/ArrayList; -Landroid/net/LinkProperties;->mStackedLinks:Ljava/util/Hashtable; -Landroid/net/LinkProperties;->mTcpBufferSizes:Ljava/lang/String; -Landroid/net/LinkProperties;->mUsePrivateDns:Z -Landroid/net/LinkProperties;->mValidatedPrivateDnses:Ljava/util/ArrayList; -Landroid/net/LinkProperties;->removeLinkAddress(Landroid/net/LinkAddress;)Z -Landroid/net/LinkProperties;->removeStackedLink(Ljava/lang/String;)Z -Landroid/net/LinkProperties;->removeValidatedPrivateDnsServer(Ljava/net/InetAddress;)Z -Landroid/net/LinkProperties;->routeWithInterface(Landroid/net/RouteInfo;)Landroid/net/RouteInfo; -Landroid/net/LinkProperties;->setPrivateDnsServerName(Ljava/lang/String;)V -Landroid/net/LinkProperties;->setUsePrivateDns(Z)V -Landroid/net/LinkProperties;->setValidatedPrivateDnsServers(Ljava/util/Collection;)V Landroid/net/LinkQualityInfo;-><init>()V Landroid/net/LinkQualityInfo;->CREATOR:Landroid/os/Parcelable$Creator; Landroid/net/LinkQualityInfo;->getDataSampleDuration()I @@ -36671,29 +36183,6 @@ Landroid/net/LocalSocketImpl;->supportsUrgentData()Z Landroid/net/LocalSocketImpl;->writeba_native([BIILjava/io/FileDescriptor;)V Landroid/net/LocalSocketImpl;->writeMonitor:Ljava/lang/Object; Landroid/net/LocalSocketImpl;->write_native(ILjava/io/FileDescriptor;)V -Landroid/net/MacAddress;-><init>(J)V -Landroid/net/MacAddress;->BASE_GOOGLE_MAC:Landroid/net/MacAddress; -Landroid/net/MacAddress;->byteAddrFromLongAddr(J)[B -Landroid/net/MacAddress;->byteAddrFromStringAddr(Ljava/lang/String;)[B -Landroid/net/MacAddress;->createRandomUnicastAddress()Landroid/net/MacAddress; -Landroid/net/MacAddress;->createRandomUnicastAddress(Landroid/net/MacAddress;Ljava/util/Random;)Landroid/net/MacAddress; -Landroid/net/MacAddress;->createRandomUnicastAddressWithGoogleBase()Landroid/net/MacAddress; -Landroid/net/MacAddress;->ETHER_ADDR_BROADCAST:[B -Landroid/net/MacAddress;->ETHER_ADDR_LEN:I -Landroid/net/MacAddress;->isMacAddress([B)Z -Landroid/net/MacAddress;->isMulticastAddress()Z -Landroid/net/MacAddress;->LOCALLY_ASSIGNED_MASK:J -Landroid/net/MacAddress;->longAddrFromByteAddr([B)J -Landroid/net/MacAddress;->longAddrFromStringAddr(Ljava/lang/String;)J -Landroid/net/MacAddress;->macAddressType([B)I -Landroid/net/MacAddress;->mAddr:J -Landroid/net/MacAddress;->MULTICAST_MASK:J -Landroid/net/MacAddress;->NIC_MASK:J -Landroid/net/MacAddress;->OUI_MASK:J -Landroid/net/MacAddress;->stringAddrFromByteAddr([B)Ljava/lang/String; -Landroid/net/MacAddress;->stringAddrFromLongAddr(J)Ljava/lang/String; -Landroid/net/MacAddress;->TYPE_UNKNOWN:I -Landroid/net/MacAddress;->VALID_LONG_MASK:J Landroid/net/MailTo;-><init>()V Landroid/net/MailTo;->BODY:Ljava/lang/String; Landroid/net/MailTo;->CC:Ljava/lang/String; @@ -36952,666 +36441,6 @@ Landroid/net/MobileLinkQualityInfo;->mLteRssnr:I Landroid/net/MobileLinkQualityInfo;->mLteSignalStrength:I Landroid/net/MobileLinkQualityInfo;->mMobileNetworkType:I Landroid/net/MobileLinkQualityInfo;->mRssi:I -Landroid/net/Network$NetworkBoundSocketFactory;->connectToHost(Ljava/lang/String;ILjava/net/SocketAddress;)Ljava/net/Socket; -Landroid/net/Network$NetworkBoundSocketFactory;->mNetId:I -Landroid/net/Network;-><init>(Landroid/net/Network;)V -Landroid/net/Network;->getNetIdForResolv()I -Landroid/net/Network;->HANDLE_MAGIC:J -Landroid/net/Network;->HANDLE_MAGIC_SIZE:I -Landroid/net/Network;->httpKeepAlive:Z -Landroid/net/Network;->httpKeepAliveDurationMs:J -Landroid/net/Network;->httpMaxConnections:I -Landroid/net/Network;->maybeInitUrlConnectionFactory()V -Landroid/net/Network;->mLock:Ljava/lang/Object; -Landroid/net/Network;->mNetworkBoundSocketFactory:Landroid/net/Network$NetworkBoundSocketFactory; -Landroid/net/Network;->mPrivateDnsBypass:Z -Landroid/net/Network;->mUrlConnectionFactory:Lcom/android/okhttp/internalandroidapi/HttpURLConnectionFactory; -Landroid/net/Network;->setPrivateDnsBypass(Z)V -Landroid/net/Network;->writeToProto(Landroid/util/proto/ProtoOutputStream;J)V -Landroid/net/NetworkAgent;-><init>(Landroid/os/Looper;Landroid/content/Context;Ljava/lang/String;Landroid/net/NetworkInfo;Landroid/net/NetworkCapabilities;Landroid/net/LinkProperties;I)V -Landroid/net/NetworkAgent;-><init>(Landroid/os/Looper;Landroid/content/Context;Ljava/lang/String;Landroid/net/NetworkInfo;Landroid/net/NetworkCapabilities;Landroid/net/LinkProperties;ILandroid/net/NetworkMisc;)V -Landroid/net/NetworkAgent;->BASE:I -Landroid/net/NetworkAgent;->BW_REFRESH_MIN_WIN_MS:J -Landroid/net/NetworkAgent;->CMD_PREVENT_AUTOMATIC_RECONNECT:I -Landroid/net/NetworkAgent;->CMD_REPORT_NETWORK_STATUS:I -Landroid/net/NetworkAgent;->CMD_REQUEST_BANDWIDTH_UPDATE:I -Landroid/net/NetworkAgent;->CMD_SAVE_ACCEPT_UNVALIDATED:I -Landroid/net/NetworkAgent;->CMD_SET_SIGNAL_STRENGTH_THRESHOLDS:I -Landroid/net/NetworkAgent;->CMD_START_PACKET_KEEPALIVE:I -Landroid/net/NetworkAgent;->CMD_STOP_PACKET_KEEPALIVE:I -Landroid/net/NetworkAgent;->CMD_SUSPECT_BAD:I -Landroid/net/NetworkAgent;->DBG:Z -Landroid/net/NetworkAgent;->EVENT_NETWORK_CAPABILITIES_CHANGED:I -Landroid/net/NetworkAgent;->EVENT_NETWORK_INFO_CHANGED:I -Landroid/net/NetworkAgent;->EVENT_NETWORK_PROPERTIES_CHANGED:I -Landroid/net/NetworkAgent;->EVENT_NETWORK_SCORE_CHANGED:I -Landroid/net/NetworkAgent;->EVENT_PACKET_KEEPALIVE:I -Landroid/net/NetworkAgent;->EVENT_SET_EXPLICITLY_SELECTED:I -Landroid/net/NetworkAgent;->explicitlySelected(Z)V -Landroid/net/NetworkAgent;->INVALID_NETWORK:I -Landroid/net/NetworkAgent;->log(Ljava/lang/String;)V -Landroid/net/NetworkAgent;->LOG_TAG:Ljava/lang/String; -Landroid/net/NetworkAgent;->mAsyncChannel:Lcom/android/internal/util/AsyncChannel; -Landroid/net/NetworkAgent;->mContext:Landroid/content/Context; -Landroid/net/NetworkAgent;->mLastBwRefreshTime:J -Landroid/net/NetworkAgent;->mPollLcePending:Ljava/util/concurrent/atomic/AtomicBoolean; -Landroid/net/NetworkAgent;->mPollLceScheduled:Z -Landroid/net/NetworkAgent;->mPreConnectedQueue:Ljava/util/ArrayList; -Landroid/net/NetworkAgent;->netId:I -Landroid/net/NetworkAgent;->networkStatus(ILjava/lang/String;)V -Landroid/net/NetworkAgent;->onPacketKeepaliveEvent(II)V -Landroid/net/NetworkAgent;->pollLceData()V -Landroid/net/NetworkAgent;->preventAutomaticReconnect()V -Landroid/net/NetworkAgent;->queueOrSendMessage(III)V -Landroid/net/NetworkAgent;->queueOrSendMessage(IIILjava/lang/Object;)V -Landroid/net/NetworkAgent;->queueOrSendMessage(ILjava/lang/Object;)V -Landroid/net/NetworkAgent;->queueOrSendMessage(Landroid/os/Message;)V -Landroid/net/NetworkAgent;->REDIRECT_URL_KEY:Ljava/lang/String; -Landroid/net/NetworkAgent;->saveAcceptUnvalidated(Z)V -Landroid/net/NetworkAgent;->sendLinkProperties(Landroid/net/LinkProperties;)V -Landroid/net/NetworkAgent;->sendNetworkCapabilities(Landroid/net/NetworkCapabilities;)V -Landroid/net/NetworkAgent;->sendNetworkScore(I)V -Landroid/net/NetworkAgent;->setSignalStrengthThresholds([I)V -Landroid/net/NetworkAgent;->startPacketKeepalive(Landroid/os/Message;)V -Landroid/net/NetworkAgent;->stopPacketKeepalive(Landroid/os/Message;)V -Landroid/net/NetworkAgent;->unwanted()V -Landroid/net/NetworkAgent;->VALID_NETWORK:I -Landroid/net/NetworkAgent;->VDBG:Z -Landroid/net/NetworkAgent;->WIFI_BASE_SCORE:I -Landroid/net/NetworkBadging;-><init>()V -Landroid/net/NetworkBadging;->getBadgedWifiSignalResource(I)I -Landroid/net/NetworkBadging;->getWifiSignalResource(I)I -Landroid/net/NetworkCapabilities$NameOf;->nameOf(I)Ljava/lang/String; -Landroid/net/NetworkCapabilities;->addUnwantedCapability(I)V -Landroid/net/NetworkCapabilities;->appendStringRepresentationOfBitMaskToStringBuilder(Ljava/lang/StringBuilder;JLandroid/net/NetworkCapabilities$NameOf;Ljava/lang/String;)V -Landroid/net/NetworkCapabilities;->appliesToUid(I)Z -Landroid/net/NetworkCapabilities;->appliesToUidRange(Landroid/net/UidRange;)Z -Landroid/net/NetworkCapabilities;->capabilityNameOf(I)Ljava/lang/String; -Landroid/net/NetworkCapabilities;->capabilityNamesOf([I)Ljava/lang/String; -Landroid/net/NetworkCapabilities;->checkValidCapability(I)V -Landroid/net/NetworkCapabilities;->checkValidTransportType(I)V -Landroid/net/NetworkCapabilities;->clearAll()V -Landroid/net/NetworkCapabilities;->combineCapabilities(Landroid/net/NetworkCapabilities;)V -Landroid/net/NetworkCapabilities;->combineLinkBandwidths(Landroid/net/NetworkCapabilities;)V -Landroid/net/NetworkCapabilities;->combineNetCapabilities(Landroid/net/NetworkCapabilities;)V -Landroid/net/NetworkCapabilities;->combineSignalStrength(Landroid/net/NetworkCapabilities;)V -Landroid/net/NetworkCapabilities;->combineSpecifiers(Landroid/net/NetworkCapabilities;)V -Landroid/net/NetworkCapabilities;->combineSSIDs(Landroid/net/NetworkCapabilities;)V -Landroid/net/NetworkCapabilities;->combineTransportTypes(Landroid/net/NetworkCapabilities;)V -Landroid/net/NetworkCapabilities;->combineUids(Landroid/net/NetworkCapabilities;)V -Landroid/net/NetworkCapabilities;->DEFAULT_CAPABILITIES:J -Landroid/net/NetworkCapabilities;->describeFirstNonRequestableCapability()Ljava/lang/String; -Landroid/net/NetworkCapabilities;->describeImmutableDifferences(Landroid/net/NetworkCapabilities;)Ljava/lang/String; -Landroid/net/NetworkCapabilities;->equalRequestableCapabilities(Landroid/net/NetworkCapabilities;)Z -Landroid/net/NetworkCapabilities;->equalsLinkBandwidths(Landroid/net/NetworkCapabilities;)Z -Landroid/net/NetworkCapabilities;->equalsNetCapabilities(Landroid/net/NetworkCapabilities;)Z -Landroid/net/NetworkCapabilities;->equalsNetCapabilitiesRequestable(Landroid/net/NetworkCapabilities;)Z -Landroid/net/NetworkCapabilities;->equalsSignalStrength(Landroid/net/NetworkCapabilities;)Z -Landroid/net/NetworkCapabilities;->equalsSpecifier(Landroid/net/NetworkCapabilities;)Z -Landroid/net/NetworkCapabilities;->equalsSSID(Landroid/net/NetworkCapabilities;)Z -Landroid/net/NetworkCapabilities;->equalsTransportTypes(Landroid/net/NetworkCapabilities;)Z -Landroid/net/NetworkCapabilities;->equalsUids(Landroid/net/NetworkCapabilities;)Z -Landroid/net/NetworkCapabilities;->FORCE_RESTRICTED_CAPABILITIES:J -Landroid/net/NetworkCapabilities;->getSSID()Ljava/lang/String; -Landroid/net/NetworkCapabilities;->getUids()Ljava/util/Set; -Landroid/net/NetworkCapabilities;->getUnwantedCapabilities()[I -Landroid/net/NetworkCapabilities;->hasUnwantedCapability(I)Z -Landroid/net/NetworkCapabilities;->INVALID_UID:I -Landroid/net/NetworkCapabilities;->isValidCapability(I)Z -Landroid/net/NetworkCapabilities;->isValidTransport(I)Z -Landroid/net/NetworkCapabilities;->LINK_BANDWIDTH_UNSPECIFIED:I -Landroid/net/NetworkCapabilities;->maxBandwidth(II)I -Landroid/net/NetworkCapabilities;->MAX_NET_CAPABILITY:I -Landroid/net/NetworkCapabilities;->MAX_TRANSPORT:I -Landroid/net/NetworkCapabilities;->maybeMarkCapabilitiesRestricted()V -Landroid/net/NetworkCapabilities;->mEstablishingVpnAppUid:I -Landroid/net/NetworkCapabilities;->minBandwidth(II)I -Landroid/net/NetworkCapabilities;->MIN_NET_CAPABILITY:I -Landroid/net/NetworkCapabilities;->MIN_TRANSPORT:I -Landroid/net/NetworkCapabilities;->mLinkDownBandwidthKbps:I -Landroid/net/NetworkCapabilities;->mLinkUpBandwidthKbps:I -Landroid/net/NetworkCapabilities;->mNetworkSpecifier:Landroid/net/NetworkSpecifier; -Landroid/net/NetworkCapabilities;->mSSID:Ljava/lang/String; -Landroid/net/NetworkCapabilities;->mTransportTypes:J -Landroid/net/NetworkCapabilities;->mUids:Landroid/util/ArraySet; -Landroid/net/NetworkCapabilities;->mUnwantedNetworkCapabilities:J -Landroid/net/NetworkCapabilities;->MUTABLE_CAPABILITIES:J -Landroid/net/NetworkCapabilities;->NON_REQUESTABLE_CAPABILITIES:J -Landroid/net/NetworkCapabilities;->removeTransportType(I)Landroid/net/NetworkCapabilities; -Landroid/net/NetworkCapabilities;->RESTRICTED_CAPABILITIES:J -Landroid/net/NetworkCapabilities;->satisfiedByImmutableNetworkCapabilities(Landroid/net/NetworkCapabilities;)Z -Landroid/net/NetworkCapabilities;->satisfiedByLinkBandwidths(Landroid/net/NetworkCapabilities;)Z -Landroid/net/NetworkCapabilities;->satisfiedByNetCapabilities(Landroid/net/NetworkCapabilities;Z)Z -Landroid/net/NetworkCapabilities;->satisfiedByNetworkCapabilities(Landroid/net/NetworkCapabilities;)Z -Landroid/net/NetworkCapabilities;->satisfiedByNetworkCapabilities(Landroid/net/NetworkCapabilities;Z)Z -Landroid/net/NetworkCapabilities;->satisfiedBySignalStrength(Landroid/net/NetworkCapabilities;)Z -Landroid/net/NetworkCapabilities;->satisfiedBySpecifier(Landroid/net/NetworkCapabilities;)Z -Landroid/net/NetworkCapabilities;->satisfiedBySSID(Landroid/net/NetworkCapabilities;)Z -Landroid/net/NetworkCapabilities;->satisfiedByTransportTypes(Landroid/net/NetworkCapabilities;)Z -Landroid/net/NetworkCapabilities;->satisfiedByUids(Landroid/net/NetworkCapabilities;)Z -Landroid/net/NetworkCapabilities;->set(Landroid/net/NetworkCapabilities;)V -Landroid/net/NetworkCapabilities;->setCapabilities([I)V -Landroid/net/NetworkCapabilities;->setCapabilities([I[I)V -Landroid/net/NetworkCapabilities;->setCapability(IZ)Landroid/net/NetworkCapabilities; -Landroid/net/NetworkCapabilities;->setEstablishingVpnAppUid(I)V -Landroid/net/NetworkCapabilities;->setLinkDownstreamBandwidthKbps(I)Landroid/net/NetworkCapabilities; -Landroid/net/NetworkCapabilities;->setLinkUpstreamBandwidthKbps(I)Landroid/net/NetworkCapabilities; -Landroid/net/NetworkCapabilities;->setNetworkSpecifier(Landroid/net/NetworkSpecifier;)Landroid/net/NetworkCapabilities; -Landroid/net/NetworkCapabilities;->setSingleUid(I)Landroid/net/NetworkCapabilities; -Landroid/net/NetworkCapabilities;->setSSID(Ljava/lang/String;)Landroid/net/NetworkCapabilities; -Landroid/net/NetworkCapabilities;->setTransportType(IZ)Landroid/net/NetworkCapabilities; -Landroid/net/NetworkCapabilities;->setTransportTypes([I)V -Landroid/net/NetworkCapabilities;->setUids(Ljava/util/Set;)Landroid/net/NetworkCapabilities; -Landroid/net/NetworkCapabilities;->SIGNAL_STRENGTH_UNSPECIFIED:I -Landroid/net/NetworkCapabilities;->TAG:Ljava/lang/String; -Landroid/net/NetworkCapabilities;->transportNameOf(I)Ljava/lang/String; -Landroid/net/NetworkCapabilities;->TRANSPORT_NAMES:[Ljava/lang/String; -Landroid/net/NetworkCapabilities;->UNRESTRICTED_CAPABILITIES:J -Landroid/net/NetworkCapabilities;->writeToProto(Landroid/util/proto/ProtoOutputStream;J)V -Landroid/net/NetworkCapabilitiesProto;-><init>()V -Landroid/net/NetworkCapabilitiesProto;->CAN_REPORT_SIGNAL_STRENGTH:J -Landroid/net/NetworkCapabilitiesProto;->CAPABILITIES:J -Landroid/net/NetworkCapabilitiesProto;->LINK_DOWN_BANDWIDTH_KBPS:J -Landroid/net/NetworkCapabilitiesProto;->LINK_UP_BANDWIDTH_KBPS:J -Landroid/net/NetworkCapabilitiesProto;->NETWORK_SPECIFIER:J -Landroid/net/NetworkCapabilitiesProto;->NET_CAPABILITY_CAPTIVE_PORTAL:I -Landroid/net/NetworkCapabilitiesProto;->NET_CAPABILITY_CBS:I -Landroid/net/NetworkCapabilitiesProto;->NET_CAPABILITY_DUN:I -Landroid/net/NetworkCapabilitiesProto;->NET_CAPABILITY_EIMS:I -Landroid/net/NetworkCapabilitiesProto;->NET_CAPABILITY_FOREGROUND:I -Landroid/net/NetworkCapabilitiesProto;->NET_CAPABILITY_FOTA:I -Landroid/net/NetworkCapabilitiesProto;->NET_CAPABILITY_IA:I -Landroid/net/NetworkCapabilitiesProto;->NET_CAPABILITY_IMS:I -Landroid/net/NetworkCapabilitiesProto;->NET_CAPABILITY_INTERNET:I -Landroid/net/NetworkCapabilitiesProto;->NET_CAPABILITY_MMS:I -Landroid/net/NetworkCapabilitiesProto;->NET_CAPABILITY_NOT_METERED:I -Landroid/net/NetworkCapabilitiesProto;->NET_CAPABILITY_NOT_RESTRICTED:I -Landroid/net/NetworkCapabilitiesProto;->NET_CAPABILITY_NOT_ROAMING:I -Landroid/net/NetworkCapabilitiesProto;->NET_CAPABILITY_NOT_VPN:I -Landroid/net/NetworkCapabilitiesProto;->NET_CAPABILITY_RCS:I -Landroid/net/NetworkCapabilitiesProto;->NET_CAPABILITY_SUPL:I -Landroid/net/NetworkCapabilitiesProto;->NET_CAPABILITY_TRUSTED:I -Landroid/net/NetworkCapabilitiesProto;->NET_CAPABILITY_VALIDATED:I -Landroid/net/NetworkCapabilitiesProto;->NET_CAPABILITY_WIFI_P2P:I -Landroid/net/NetworkCapabilitiesProto;->NET_CAPABILITY_XCAP:I -Landroid/net/NetworkCapabilitiesProto;->SIGNAL_STRENGTH:J -Landroid/net/NetworkCapabilitiesProto;->TRANSPORTS:J -Landroid/net/NetworkCapabilitiesProto;->TRANSPORT_BLUETOOTH:I -Landroid/net/NetworkCapabilitiesProto;->TRANSPORT_CELLULAR:I -Landroid/net/NetworkCapabilitiesProto;->TRANSPORT_ETHERNET:I -Landroid/net/NetworkCapabilitiesProto;->TRANSPORT_LOWPAN:I -Landroid/net/NetworkCapabilitiesProto;->TRANSPORT_VPN:I -Landroid/net/NetworkCapabilitiesProto;->TRANSPORT_WIFI:I -Landroid/net/NetworkCapabilitiesProto;->TRANSPORT_WIFI_AWARE:I -Landroid/net/NetworkConfig;-><init>(Ljava/lang/String;)V -Landroid/net/NetworkConfig;->dependencyMet:Z -Landroid/net/NetworkConfig;->isDefault()Z -Landroid/net/NetworkConfig;->name:Ljava/lang/String; -Landroid/net/NetworkConfig;->priority:I -Landroid/net/NetworkConfig;->radio:I -Landroid/net/NetworkConfig;->restoreTime:I -Landroid/net/NetworkConfig;->type:I -Landroid/net/NetworkFactory$NetworkRequestInfo;->request:Landroid/net/NetworkRequest; -Landroid/net/NetworkFactory$NetworkRequestInfo;->requested:Z -Landroid/net/NetworkFactory$NetworkRequestInfo;->score:I -Landroid/net/NetworkFactory;->acceptRequest(Landroid/net/NetworkRequest;I)Z -Landroid/net/NetworkFactory;->addNetworkRequest(Landroid/net/NetworkRequest;I)V -Landroid/net/NetworkFactory;->BASE:I -Landroid/net/NetworkFactory;->CMD_CANCEL_REQUEST:I -Landroid/net/NetworkFactory;->CMD_REQUEST_NETWORK:I -Landroid/net/NetworkFactory;->CMD_SET_FILTER:I -Landroid/net/NetworkFactory;->CMD_SET_SCORE:I -Landroid/net/NetworkFactory;->DBG:Z -Landroid/net/NetworkFactory;->evalRequest(Landroid/net/NetworkFactory$NetworkRequestInfo;)V -Landroid/net/NetworkFactory;->evalRequests()V -Landroid/net/NetworkFactory;->getRequestCount()I -Landroid/net/NetworkFactory;->handleAddRequest(Landroid/net/NetworkRequest;I)V -Landroid/net/NetworkFactory;->handleRemoveRequest(Landroid/net/NetworkRequest;)V -Landroid/net/NetworkFactory;->handleSetFilter(Landroid/net/NetworkCapabilities;)V -Landroid/net/NetworkFactory;->handleSetScore(I)V -Landroid/net/NetworkFactory;->log(Ljava/lang/String;)V -Landroid/net/NetworkFactory;->LOG_TAG:Ljava/lang/String; -Landroid/net/NetworkFactory;->mCapabilityFilter:Landroid/net/NetworkCapabilities; -Landroid/net/NetworkFactory;->mContext:Landroid/content/Context; -Landroid/net/NetworkFactory;->mMessenger:Landroid/os/Messenger; -Landroid/net/NetworkFactory;->mNetworkRequests:Landroid/util/SparseArray; -Landroid/net/NetworkFactory;->mRefCount:I -Landroid/net/NetworkFactory;->mScore:I -Landroid/net/NetworkFactory;->needNetworkFor(Landroid/net/NetworkRequest;I)V -Landroid/net/NetworkFactory;->reevaluateAllRequests()V -Landroid/net/NetworkFactory;->register()V -Landroid/net/NetworkFactory;->releaseNetworkFor(Landroid/net/NetworkRequest;)V -Landroid/net/NetworkFactory;->removeNetworkRequest(Landroid/net/NetworkRequest;)V -Landroid/net/NetworkFactory;->setCapabilityFilter(Landroid/net/NetworkCapabilities;)V -Landroid/net/NetworkFactory;->startNetwork()V -Landroid/net/NetworkFactory;->stopNetwork()V -Landroid/net/NetworkFactory;->unregister()V -Landroid/net/NetworkFactory;->VDBG:Z -Landroid/net/NetworkIdentity;-><init>(IILjava/lang/String;Ljava/lang/String;ZZZ)V -Landroid/net/NetworkIdentity;->buildNetworkIdentity(Landroid/content/Context;Landroid/net/NetworkState;Z)Landroid/net/NetworkIdentity; -Landroid/net/NetworkIdentity;->COMBINE_SUBTYPE_ENABLED:Z -Landroid/net/NetworkIdentity;->compareTo(Landroid/net/NetworkIdentity;)I -Landroid/net/NetworkIdentity;->getDefaultNetwork()Z -Landroid/net/NetworkIdentity;->getMetered()Z -Landroid/net/NetworkIdentity;->getNetworkId()Ljava/lang/String; -Landroid/net/NetworkIdentity;->getRoaming()Z -Landroid/net/NetworkIdentity;->getSubscriberId()Ljava/lang/String; -Landroid/net/NetworkIdentity;->getSubType()I -Landroid/net/NetworkIdentity;->getType()I -Landroid/net/NetworkIdentity;->mDefaultNetwork:Z -Landroid/net/NetworkIdentity;->mMetered:Z -Landroid/net/NetworkIdentity;->mNetworkId:Ljava/lang/String; -Landroid/net/NetworkIdentity;->mRoaming:Z -Landroid/net/NetworkIdentity;->mSubscriberId:Ljava/lang/String; -Landroid/net/NetworkIdentity;->mSubType:I -Landroid/net/NetworkIdentity;->mType:I -Landroid/net/NetworkIdentity;->scrubSubscriberId(Ljava/lang/String;)Ljava/lang/String; -Landroid/net/NetworkIdentity;->scrubSubscriberId([Ljava/lang/String;)[Ljava/lang/String; -Landroid/net/NetworkIdentity;->SUBTYPE_COMBINED:I -Landroid/net/NetworkIdentity;->TAG:Ljava/lang/String; -Landroid/net/NetworkIdentity;->writeToProto(Landroid/util/proto/ProtoOutputStream;J)V -Landroid/net/NetworkInfo;->mDetailedState:Landroid/net/NetworkInfo$DetailedState; -Landroid/net/NetworkInfo;->mExtraInfo:Ljava/lang/String; -Landroid/net/NetworkInfo;->mIsAvailable:Z -Landroid/net/NetworkInfo;->mIsFailover:Z -Landroid/net/NetworkInfo;->mIsRoaming:Z -Landroid/net/NetworkInfo;->mNetworkType:I -Landroid/net/NetworkInfo;->mReason:Ljava/lang/String; -Landroid/net/NetworkInfo;->mState:Landroid/net/NetworkInfo$State; -Landroid/net/NetworkInfo;->mSubtype:I -Landroid/net/NetworkInfo;->mSubtypeName:Ljava/lang/String; -Landroid/net/NetworkInfo;->mTypeName:Ljava/lang/String; -Landroid/net/NetworkInfo;->setExtraInfo(Ljava/lang/String;)V -Landroid/net/NetworkInfo;->setType(I)V -Landroid/net/NetworkInfo;->stateMap:Ljava/util/EnumMap; -Landroid/net/NetworkKey;-><init>(Landroid/os/Parcel;)V -Landroid/net/NetworkKey;->createFromScanResult(Landroid/net/wifi/ScanResult;)Landroid/net/NetworkKey; -Landroid/net/NetworkKey;->createFromWifiInfo(Landroid/net/wifi/WifiInfo;)Landroid/net/NetworkKey; -Landroid/net/NetworkKey;->TAG:Ljava/lang/String; -Landroid/net/NetworkMisc;-><init>()V -Landroid/net/NetworkMisc;-><init>(Landroid/net/NetworkMisc;)V -Landroid/net/NetworkMisc;->acceptUnvalidated:Z -Landroid/net/NetworkMisc;->allowBypass:Z -Landroid/net/NetworkMisc;->CREATOR:Landroid/os/Parcelable$Creator; -Landroid/net/NetworkMisc;->explicitlySelected:Z -Landroid/net/NetworkMisc;->provisioningNotificationDisabled:Z -Landroid/net/NetworkMisc;->subscriberId:Ljava/lang/String; -Landroid/net/NetworkPolicy;-><init>(Landroid/net/NetworkTemplate;ILjava/lang/String;JJZ)V -Landroid/net/NetworkPolicy;-><init>(Landroid/net/NetworkTemplate;Landroid/util/RecurrenceRule;JJJJJZZ)V -Landroid/net/NetworkPolicy;-><init>(Landroid/net/NetworkTemplate;Landroid/util/RecurrenceRule;JJJJZZ)V -Landroid/net/NetworkPolicy;-><init>(Landroid/os/Parcel;)V -Landroid/net/NetworkPolicy;->buildRule(ILjava/time/ZoneId;)Landroid/util/RecurrenceRule; -Landroid/net/NetworkPolicy;->cycleIterator()Ljava/util/Iterator; -Landroid/net/NetworkPolicy;->cycleRule:Landroid/util/RecurrenceRule; -Landroid/net/NetworkPolicy;->CYCLE_NONE:I -Landroid/net/NetworkPolicy;->DEFAULT_MTU:J -Landroid/net/NetworkPolicy;->getBytesForBackup()[B -Landroid/net/NetworkPolicy;->getNetworkPolicyFromBackup(Ljava/io/DataInputStream;)Landroid/net/NetworkPolicy; -Landroid/net/NetworkPolicy;->hasCycle()Z -Landroid/net/NetworkPolicy;->lastLimitSnooze:J -Landroid/net/NetworkPolicy;->lastRapidSnooze:J -Landroid/net/NetworkPolicy;->lastWarningSnooze:J -Landroid/net/NetworkPolicy;->LIMIT_DISABLED:J -Landroid/net/NetworkPolicy;->SNOOZE_NEVER:J -Landroid/net/NetworkPolicy;->VERSION_INIT:I -Landroid/net/NetworkPolicy;->VERSION_RAPID:I -Landroid/net/NetworkPolicy;->VERSION_RULE:I -Landroid/net/NetworkPolicy;->WARNING_DISABLED:J -Landroid/net/NetworkPolicyManager$Listener;-><init>()V -Landroid/net/NetworkPolicyManager$Listener;->onMeteredIfacesChanged([Ljava/lang/String;)V -Landroid/net/NetworkPolicyManager$Listener;->onRestrictBackgroundChanged(Z)V -Landroid/net/NetworkPolicyManager$Listener;->onSubscriptionOverride(III)V -Landroid/net/NetworkPolicyManager$Listener;->onUidPoliciesChanged(II)V -Landroid/net/NetworkPolicyManager$Listener;->onUidRulesChanged(II)V -Landroid/net/NetworkPolicyManager;-><init>(Landroid/content/Context;Landroid/net/INetworkPolicyManager;)V -Landroid/net/NetworkPolicyManager;->addUidPolicy(II)V -Landroid/net/NetworkPolicyManager;->ALLOW_PLATFORM_APP_POLICY:Z -Landroid/net/NetworkPolicyManager;->cycleIterator(Landroid/net/NetworkPolicy;)Ljava/util/Iterator; -Landroid/net/NetworkPolicyManager;->EXTRA_NETWORK_TEMPLATE:Ljava/lang/String; -Landroid/net/NetworkPolicyManager;->factoryReset(Ljava/lang/String;)V -Landroid/net/NetworkPolicyManager;->FIREWALL_CHAIN_DOZABLE:I -Landroid/net/NetworkPolicyManager;->FIREWALL_CHAIN_NAME_DOZABLE:Ljava/lang/String; -Landroid/net/NetworkPolicyManager;->FIREWALL_CHAIN_NAME_NONE:Ljava/lang/String; -Landroid/net/NetworkPolicyManager;->FIREWALL_CHAIN_NAME_POWERSAVE:Ljava/lang/String; -Landroid/net/NetworkPolicyManager;->FIREWALL_CHAIN_NAME_STANDBY:Ljava/lang/String; -Landroid/net/NetworkPolicyManager;->FIREWALL_CHAIN_NONE:I -Landroid/net/NetworkPolicyManager;->FIREWALL_CHAIN_POWERSAVE:I -Landroid/net/NetworkPolicyManager;->FIREWALL_CHAIN_STANDBY:I -Landroid/net/NetworkPolicyManager;->FIREWALL_RULE_ALLOW:I -Landroid/net/NetworkPolicyManager;->FIREWALL_RULE_DEFAULT:I -Landroid/net/NetworkPolicyManager;->FIREWALL_RULE_DENY:I -Landroid/net/NetworkPolicyManager;->FIREWALL_TYPE_BLACKLIST:I -Landroid/net/NetworkPolicyManager;->FIREWALL_TYPE_WHITELIST:I -Landroid/net/NetworkPolicyManager;->FOREGROUND_THRESHOLD_STATE:I -Landroid/net/NetworkPolicyManager;->isProcStateAllowedWhileIdleOrPowerSaveMode(I)Z -Landroid/net/NetworkPolicyManager;->isProcStateAllowedWhileOnRestrictBackground(I)Z -Landroid/net/NetworkPolicyManager;->isUidValidForPolicy(Landroid/content/Context;I)Z -Landroid/net/NetworkPolicyManager;->MASK_ALL_NETWORKS:I -Landroid/net/NetworkPolicyManager;->MASK_METERED_NETWORKS:I -Landroid/net/NetworkPolicyManager;->mContext:Landroid/content/Context; -Landroid/net/NetworkPolicyManager;->OVERRIDE_CONGESTED:I -Landroid/net/NetworkPolicyManager;->OVERRIDE_UNMETERED:I -Landroid/net/NetworkPolicyManager;->POLICY_ALLOW_METERED_BACKGROUND:I -Landroid/net/NetworkPolicyManager;->POLICY_NONE:I -Landroid/net/NetworkPolicyManager;->POLICY_REJECT_METERED_BACKGROUND:I -Landroid/net/NetworkPolicyManager;->removeUidPolicy(II)V -Landroid/net/NetworkPolicyManager;->resolveNetworkId(Landroid/net/wifi/WifiConfiguration;)Ljava/lang/String; -Landroid/net/NetworkPolicyManager;->resolveNetworkId(Ljava/lang/String;)Ljava/lang/String; -Landroid/net/NetworkPolicyManager;->RULE_ALLOW_ALL:I -Landroid/net/NetworkPolicyManager;->RULE_ALLOW_METERED:I -Landroid/net/NetworkPolicyManager;->RULE_NONE:I -Landroid/net/NetworkPolicyManager;->RULE_REJECT_ALL:I -Landroid/net/NetworkPolicyManager;->RULE_REJECT_METERED:I -Landroid/net/NetworkPolicyManager;->RULE_TEMPORARY_ALLOW_METERED:I -Landroid/net/NetworkPolicyManager;->setNetworkPolicies([Landroid/net/NetworkPolicy;)V -Landroid/net/NetworkPolicyManager;->uidPoliciesToString(I)Ljava/lang/String; -Landroid/net/NetworkPolicyManager;->uidRulesToString(I)Ljava/lang/String; -Landroid/net/NetworkProto;-><init>()V -Landroid/net/NetworkProto;->NET_ID:J -Landroid/net/NetworkQuotaInfo;-><init>()V -Landroid/net/NetworkQuotaInfo;-><init>(Landroid/os/Parcel;)V -Landroid/net/NetworkQuotaInfo;->NO_LIMIT:J -Landroid/net/NetworkRecommendationProvider$ServiceWrapper;->enforceCallingPermission()V -Landroid/net/NetworkRecommendationProvider$ServiceWrapper;->execute(Ljava/lang/Runnable;)V -Landroid/net/NetworkRecommendationProvider$ServiceWrapper;->mContext:Landroid/content/Context; -Landroid/net/NetworkRecommendationProvider$ServiceWrapper;->mExecutor:Ljava/util/concurrent/Executor; -Landroid/net/NetworkRecommendationProvider$ServiceWrapper;->mHandler:Landroid/os/Handler; -Landroid/net/NetworkRecommendationProvider$ServiceWrapper;->requestScores([Landroid/net/NetworkKey;)V -Landroid/net/NetworkRecommendationProvider;->mService:Landroid/os/IBinder; -Landroid/net/NetworkRecommendationProvider;->TAG:Ljava/lang/String; -Landroid/net/NetworkRecommendationProvider;->VERBOSE:Z -Landroid/net/NetworkRequest$Builder;->addUnwantedCapability(I)Landroid/net/NetworkRequest$Builder; -Landroid/net/NetworkRequest$Builder;->mNetworkCapabilities:Landroid/net/NetworkCapabilities; -Landroid/net/NetworkRequest$Builder;->setCapabilities(Landroid/net/NetworkCapabilities;)Landroid/net/NetworkRequest$Builder; -Landroid/net/NetworkRequest$Builder;->setLinkDownstreamBandwidthKbps(I)Landroid/net/NetworkRequest$Builder; -Landroid/net/NetworkRequest$Builder;->setLinkUpstreamBandwidthKbps(I)Landroid/net/NetworkRequest$Builder; -Landroid/net/NetworkRequest$Builder;->setUids(Ljava/util/Set;)Landroid/net/NetworkRequest$Builder; -Landroid/net/NetworkRequest$Type;->BACKGROUND_REQUEST:Landroid/net/NetworkRequest$Type; -Landroid/net/NetworkRequest$Type;->LISTEN:Landroid/net/NetworkRequest$Type; -Landroid/net/NetworkRequest$Type;->NONE:Landroid/net/NetworkRequest$Type; -Landroid/net/NetworkRequest$Type;->REQUEST:Landroid/net/NetworkRequest$Type; -Landroid/net/NetworkRequest$Type;->TRACK_DEFAULT:Landroid/net/NetworkRequest$Type; -Landroid/net/NetworkRequest$Type;->valueOf(Ljava/lang/String;)Landroid/net/NetworkRequest$Type; -Landroid/net/NetworkRequest$Type;->values()[Landroid/net/NetworkRequest$Type; -Landroid/net/NetworkRequest;-><init>(Landroid/net/NetworkCapabilities;IILandroid/net/NetworkRequest$Type;)V -Landroid/net/NetworkRequest;-><init>(Landroid/net/NetworkRequest;)V -Landroid/net/NetworkRequest;->hasUnwantedCapability(I)Z -Landroid/net/NetworkRequest;->isBackgroundRequest()Z -Landroid/net/NetworkRequest;->isForegroundRequest()Z -Landroid/net/NetworkRequest;->isListen()Z -Landroid/net/NetworkRequest;->isRequest()Z -Landroid/net/NetworkRequest;->type:Landroid/net/NetworkRequest$Type; -Landroid/net/NetworkRequest;->typeToProtoEnum(Landroid/net/NetworkRequest$Type;)I -Landroid/net/NetworkRequest;->writeToProto(Landroid/util/proto/ProtoOutputStream;J)V -Landroid/net/NetworkRequestProto;-><init>()V -Landroid/net/NetworkRequestProto;->LEGACY_TYPE:J -Landroid/net/NetworkRequestProto;->NETWORK_CAPABILITIES:J -Landroid/net/NetworkRequestProto;->REQUEST_ID:J -Landroid/net/NetworkRequestProto;->TYPE:J -Landroid/net/NetworkRequestProto;->TYPE_BACKGROUND_REQUEST:I -Landroid/net/NetworkRequestProto;->TYPE_LISTEN:I -Landroid/net/NetworkRequestProto;->TYPE_NONE:I -Landroid/net/NetworkRequestProto;->TYPE_REQUEST:I -Landroid/net/NetworkRequestProto;->TYPE_TRACK_DEFAULT:I -Landroid/net/NetworkRequestProto;->TYPE_UNKNOWN:I -Landroid/net/NetworkScoreManager;-><init>(Landroid/content/Context;)V -Landroid/net/NetworkScoreManager;->CACHE_FILTER_CURRENT_NETWORK:I -Landroid/net/NetworkScoreManager;->CACHE_FILTER_NONE:I -Landroid/net/NetworkScoreManager;->CACHE_FILTER_SCAN_RESULTS:I -Landroid/net/NetworkScoreManager;->getActiveScorer()Landroid/net/NetworkScorerAppData; -Landroid/net/NetworkScoreManager;->getAllValidScorers()Ljava/util/List; -Landroid/net/NetworkScoreManager;->isCallerActiveScorer(I)Z -Landroid/net/NetworkScoreManager;->mContext:Landroid/content/Context; -Landroid/net/NetworkScoreManager;->mService:Landroid/net/INetworkScoreService; -Landroid/net/NetworkScoreManager;->NETWORK_AVAILABLE_NOTIFICATION_CHANNEL_ID_META_DATA:Ljava/lang/String; -Landroid/net/NetworkScoreManager;->RECOMMENDATIONS_ENABLED_FORCED_OFF:I -Landroid/net/NetworkScoreManager;->RECOMMENDATIONS_ENABLED_OFF:I -Landroid/net/NetworkScoreManager;->RECOMMENDATIONS_ENABLED_ON:I -Landroid/net/NetworkScoreManager;->RECOMMENDATION_SERVICE_LABEL_META_DATA:Ljava/lang/String; -Landroid/net/NetworkScoreManager;->registerNetworkScoreCache(ILandroid/net/INetworkScoreCache;)V -Landroid/net/NetworkScoreManager;->registerNetworkScoreCache(ILandroid/net/INetworkScoreCache;I)V -Landroid/net/NetworkScoreManager;->requestScores([Landroid/net/NetworkKey;)Z -Landroid/net/NetworkScoreManager;->unregisterNetworkScoreCache(ILandroid/net/INetworkScoreCache;)V -Landroid/net/NetworkScoreManager;->USE_OPEN_WIFI_PACKAGE_META_DATA:Ljava/lang/String; -Landroid/net/NetworkScorerAppData;-><init>(ILandroid/content/ComponentName;Ljava/lang/String;Landroid/content/ComponentName;Ljava/lang/String;)V -Landroid/net/NetworkScorerAppData;-><init>(Landroid/os/Parcel;)V -Landroid/net/NetworkScorerAppData;->CREATOR:Landroid/os/Parcelable$Creator; -Landroid/net/NetworkScorerAppData;->getEnableUseOpenWifiActivity()Landroid/content/ComponentName; -Landroid/net/NetworkScorerAppData;->getNetworkAvailableNotificationChannelId()Ljava/lang/String; -Landroid/net/NetworkScorerAppData;->getRecommendationServiceComponent()Landroid/content/ComponentName; -Landroid/net/NetworkScorerAppData;->getRecommendationServiceLabel()Ljava/lang/String; -Landroid/net/NetworkScorerAppData;->getRecommendationServicePackageName()Ljava/lang/String; -Landroid/net/NetworkScorerAppData;->mEnableUseOpenWifiActivity:Landroid/content/ComponentName; -Landroid/net/NetworkScorerAppData;->mNetworkAvailableNotificationChannelId:Ljava/lang/String; -Landroid/net/NetworkScorerAppData;->mRecommendationService:Landroid/content/ComponentName; -Landroid/net/NetworkScorerAppData;->mRecommendationServiceLabel:Ljava/lang/String; -Landroid/net/NetworkScorerAppData;->packageUid:I -Landroid/net/NetworkSpecifier;-><init>()V -Landroid/net/NetworkSpecifier;->assertValidFromUid(I)V -Landroid/net/NetworkSpecifier;->satisfiedBy(Landroid/net/NetworkSpecifier;)Z -Landroid/net/NetworkState;-><init>(Landroid/net/NetworkInfo;Landroid/net/LinkProperties;Landroid/net/NetworkCapabilities;Landroid/net/Network;Ljava/lang/String;Ljava/lang/String;)V -Landroid/net/NetworkState;->EMPTY:Landroid/net/NetworkState; -Landroid/net/NetworkState;->linkProperties:Landroid/net/LinkProperties; -Landroid/net/NetworkState;->networkCapabilities:Landroid/net/NetworkCapabilities; -Landroid/net/NetworkState;->networkId:Ljava/lang/String; -Landroid/net/NetworkState;->networkInfo:Landroid/net/NetworkInfo; -Landroid/net/NetworkState;->SANITY_CHECK_ROAMING:Z -Landroid/net/NetworkState;->subscriberId:Ljava/lang/String; -Landroid/net/NetworkStats$Entry;-><init>(JJJJJ)V -Landroid/net/NetworkStats$Entry;-><init>(Ljava/lang/String;IIIIIIJJJJJ)V -Landroid/net/NetworkStats$Entry;-><init>(Ljava/lang/String;IIIJJJJJ)V -Landroid/net/NetworkStats$Entry;->add(Landroid/net/NetworkStats$Entry;)V -Landroid/net/NetworkStats$Entry;->defaultNetwork:I -Landroid/net/NetworkStats$Entry;->isEmpty()Z -Landroid/net/NetworkStats$Entry;->isNegative()Z -Landroid/net/NetworkStats$Entry;->metered:I -Landroid/net/NetworkStats$Entry;->operations:J -Landroid/net/NetworkStats$Entry;->roaming:I -Landroid/net/NetworkStats$NonMonotonicObserver;->foundNonMonotonic(Landroid/net/NetworkStats;ILandroid/net/NetworkStats;ILjava/lang/Object;)V -Landroid/net/NetworkStats$NonMonotonicObserver;->foundNonMonotonic(Landroid/net/NetworkStats;ILjava/lang/Object;)V -Landroid/net/NetworkStats;->addIfaceValues(Ljava/lang/String;JJJJ)Landroid/net/NetworkStats; -Landroid/net/NetworkStats;->addTrafficToApplications(ILjava/lang/String;Ljava/lang/String;Landroid/net/NetworkStats$Entry;Landroid/net/NetworkStats$Entry;)Landroid/net/NetworkStats$Entry; -Landroid/net/NetworkStats;->addValues(Landroid/net/NetworkStats$Entry;)Landroid/net/NetworkStats; -Landroid/net/NetworkStats;->addValues(Ljava/lang/String;IIIIIIJJJJJ)Landroid/net/NetworkStats; -Landroid/net/NetworkStats;->addValues(Ljava/lang/String;IIIJJJJJ)Landroid/net/NetworkStats; -Landroid/net/NetworkStats;->apply464xlatAdjustments(Landroid/net/NetworkStats;Landroid/net/NetworkStats;Ljava/util/Map;)V -Landroid/net/NetworkStats;->apply464xlatAdjustments(Ljava/util/Map;)V -Landroid/net/NetworkStats;->CLATD_INTERFACE_PREFIX:Ljava/lang/String; -Landroid/net/NetworkStats;->clear()V -Landroid/net/NetworkStats;->combineValues(Ljava/lang/String;IIIJJJJJ)Landroid/net/NetworkStats; -Landroid/net/NetworkStats;->combineValues(Ljava/lang/String;IIJJJJJ)Landroid/net/NetworkStats; -Landroid/net/NetworkStats;->deductTrafficFromVpnApp(ILjava/lang/String;Landroid/net/NetworkStats$Entry;)V -Landroid/net/NetworkStats;->defaultNetworkToString(I)Ljava/lang/String; -Landroid/net/NetworkStats;->DEFAULT_NETWORK_ALL:I -Landroid/net/NetworkStats;->DEFAULT_NETWORK_NO:I -Landroid/net/NetworkStats;->DEFAULT_NETWORK_YES:I -Landroid/net/NetworkStats;->dump(Ljava/lang/String;Ljava/io/PrintWriter;)V -Landroid/net/NetworkStats;->elapsedRealtime:J -Landroid/net/NetworkStats;->filter(I[Ljava/lang/String;I)V -Landroid/net/NetworkStats;->findIndex(Ljava/lang/String;IIIIII)I -Landroid/net/NetworkStats;->findIndexHinted(Ljava/lang/String;IIIIIII)I -Landroid/net/NetworkStats;->getElapsedRealtime()J -Landroid/net/NetworkStats;->getElapsedRealtimeAge()J -Landroid/net/NetworkStats;->getTotal(Landroid/net/NetworkStats$Entry;Ljava/util/HashSet;)Landroid/net/NetworkStats$Entry; -Landroid/net/NetworkStats;->getTotal(Landroid/net/NetworkStats$Entry;Ljava/util/HashSet;IZ)Landroid/net/NetworkStats$Entry; -Landroid/net/NetworkStats;->getTotalPackets()J -Landroid/net/NetworkStats;->getUniqueIfaces()[Ljava/lang/String; -Landroid/net/NetworkStats;->groupedByIface()Landroid/net/NetworkStats; -Landroid/net/NetworkStats;->groupedByUid()Landroid/net/NetworkStats; -Landroid/net/NetworkStats;->IFACE_ALL:Ljava/lang/String; -Landroid/net/NetworkStats;->INTERFACES_ALL:[Ljava/lang/String; -Landroid/net/NetworkStats;->internalSize()I -Landroid/net/NetworkStats;->IPV4V6_HEADER_DELTA:I -Landroid/net/NetworkStats;->meteredToString(I)Ljava/lang/String; -Landroid/net/NetworkStats;->METERED_ALL:I -Landroid/net/NetworkStats;->METERED_NO:I -Landroid/net/NetworkStats;->METERED_YES:I -Landroid/net/NetworkStats;->migrateTun(ILjava/lang/String;Ljava/lang/String;)Z -Landroid/net/NetworkStats;->roamingToString(I)Ljava/lang/String; -Landroid/net/NetworkStats;->ROAMING_ALL:I -Landroid/net/NetworkStats;->ROAMING_NO:I -Landroid/net/NetworkStats;->ROAMING_YES:I -Landroid/net/NetworkStats;->setElapsedRealtime(J)V -Landroid/net/NetworkStats;->setMatches(II)Z -Landroid/net/NetworkStats;->setToCheckinString(I)Ljava/lang/String; -Landroid/net/NetworkStats;->setToString(I)Ljava/lang/String; -Landroid/net/NetworkStats;->setValues(ILandroid/net/NetworkStats$Entry;)V -Landroid/net/NetworkStats;->SET_ALL:I -Landroid/net/NetworkStats;->SET_DBG_VPN_IN:I -Landroid/net/NetworkStats;->SET_DBG_VPN_OUT:I -Landroid/net/NetworkStats;->SET_DEBUG_START:I -Landroid/net/NetworkStats;->SET_DEFAULT:I -Landroid/net/NetworkStats;->SET_FOREGROUND:I -Landroid/net/NetworkStats;->spliceOperationsFrom(Landroid/net/NetworkStats;)V -Landroid/net/NetworkStats;->STATS_PER_IFACE:I -Landroid/net/NetworkStats;->STATS_PER_UID:I -Landroid/net/NetworkStats;->subtract(Landroid/net/NetworkStats;)Landroid/net/NetworkStats; -Landroid/net/NetworkStats;->subtract(Landroid/net/NetworkStats;Landroid/net/NetworkStats;Landroid/net/NetworkStats$NonMonotonicObserver;Ljava/lang/Object;)Landroid/net/NetworkStats; -Landroid/net/NetworkStats;->subtract(Landroid/net/NetworkStats;Landroid/net/NetworkStats;Landroid/net/NetworkStats$NonMonotonicObserver;Ljava/lang/Object;Landroid/net/NetworkStats;)Landroid/net/NetworkStats; -Landroid/net/NetworkStats;->TAG:Ljava/lang/String; -Landroid/net/NetworkStats;->tagToString(I)Ljava/lang/String; -Landroid/net/NetworkStats;->TAG_ALL:I -Landroid/net/NetworkStats;->TAG_NONE:I -Landroid/net/NetworkStats;->tunAdjustmentInit(ILjava/lang/String;Ljava/lang/String;Landroid/net/NetworkStats$Entry;Landroid/net/NetworkStats$Entry;)V -Landroid/net/NetworkStats;->tunGetPool(Landroid/net/NetworkStats$Entry;Landroid/net/NetworkStats$Entry;)Landroid/net/NetworkStats$Entry; -Landroid/net/NetworkStats;->tunSubtract(ILandroid/net/NetworkStats;Landroid/net/NetworkStats$Entry;)V -Landroid/net/NetworkStats;->UID_ALL:I -Landroid/net/NetworkStats;->withoutUids([I)Landroid/net/NetworkStats; -Landroid/net/NetworkStatsHistory$DataStreamUtils;-><init>()V -Landroid/net/NetworkStatsHistory$DataStreamUtils;->readFullLongArray(Ljava/io/DataInputStream;)[J -Landroid/net/NetworkStatsHistory$DataStreamUtils;->readVarLong(Ljava/io/DataInputStream;)J -Landroid/net/NetworkStatsHistory$DataStreamUtils;->readVarLongArray(Ljava/io/DataInputStream;)[J -Landroid/net/NetworkStatsHistory$DataStreamUtils;->writeVarLong(Ljava/io/DataOutputStream;J)V -Landroid/net/NetworkStatsHistory$DataStreamUtils;->writeVarLongArray(Ljava/io/DataOutputStream;[JI)V -Landroid/net/NetworkStatsHistory$Entry;-><init>()V -Landroid/net/NetworkStatsHistory$Entry;->activeTime:J -Landroid/net/NetworkStatsHistory$Entry;->operations:J -Landroid/net/NetworkStatsHistory$Entry;->UNKNOWN:J -Landroid/net/NetworkStatsHistory$ParcelUtils;-><init>()V -Landroid/net/NetworkStatsHistory$ParcelUtils;->readLongArray(Landroid/os/Parcel;)[J -Landroid/net/NetworkStatsHistory$ParcelUtils;->writeLongArray(Landroid/os/Parcel;[JI)V -Landroid/net/NetworkStatsHistory;-><init>(JI)V -Landroid/net/NetworkStatsHistory;-><init>(JII)V -Landroid/net/NetworkStatsHistory;-><init>(Landroid/net/NetworkStatsHistory;J)V -Landroid/net/NetworkStatsHistory;-><init>(Ljava/io/DataInputStream;)V -Landroid/net/NetworkStatsHistory;->activeTime:[J -Landroid/net/NetworkStatsHistory;->addLong([JIJ)V -Landroid/net/NetworkStatsHistory;->bucketCount:I -Landroid/net/NetworkStatsHistory;->bucketDuration:J -Landroid/net/NetworkStatsHistory;->bucketStart:[J -Landroid/net/NetworkStatsHistory;->clear()V -Landroid/net/NetworkStatsHistory;->dump(Lcom/android/internal/util/IndentingPrintWriter;Z)V -Landroid/net/NetworkStatsHistory;->dumpCheckin(Ljava/io/PrintWriter;)V -Landroid/net/NetworkStatsHistory;->ensureBuckets(JJ)V -Landroid/net/NetworkStatsHistory;->estimateResizeBuckets(J)I -Landroid/net/NetworkStatsHistory;->FIELD_ACTIVE_TIME:I -Landroid/net/NetworkStatsHistory;->FIELD_ALL:I -Landroid/net/NetworkStatsHistory;->FIELD_OPERATIONS:I -Landroid/net/NetworkStatsHistory;->FIELD_RX_BYTES:I -Landroid/net/NetworkStatsHistory;->FIELD_RX_PACKETS:I -Landroid/net/NetworkStatsHistory;->FIELD_TX_BYTES:I -Landroid/net/NetworkStatsHistory;->FIELD_TX_PACKETS:I -Landroid/net/NetworkStatsHistory;->generateRandom(JJJ)V -Landroid/net/NetworkStatsHistory;->generateRandom(JJJJJJJLjava/util/Random;)V -Landroid/net/NetworkStatsHistory;->getBucketDuration()J -Landroid/net/NetworkStatsHistory;->getIndexAfter(J)I -Landroid/net/NetworkStatsHistory;->getLong([JIJ)J -Landroid/net/NetworkStatsHistory;->getTotalBytes()J -Landroid/net/NetworkStatsHistory;->insertBucket(IJ)V -Landroid/net/NetworkStatsHistory;->intersects(JJ)Z -Landroid/net/NetworkStatsHistory;->operations:[J -Landroid/net/NetworkStatsHistory;->randomLong(Ljava/util/Random;JJ)J -Landroid/net/NetworkStatsHistory;->recordData(JJJJ)V -Landroid/net/NetworkStatsHistory;->recordData(JJLandroid/net/NetworkStats$Entry;)V -Landroid/net/NetworkStatsHistory;->recordHistory(Landroid/net/NetworkStatsHistory;JJ)V -Landroid/net/NetworkStatsHistory;->removeBucketsBefore(J)V -Landroid/net/NetworkStatsHistory;->rxBytes:[J -Landroid/net/NetworkStatsHistory;->rxPackets:[J -Landroid/net/NetworkStatsHistory;->setLong([JIJ)V -Landroid/net/NetworkStatsHistory;->setValues(ILandroid/net/NetworkStatsHistory$Entry;)V -Landroid/net/NetworkStatsHistory;->totalBytes:J -Landroid/net/NetworkStatsHistory;->txBytes:[J -Landroid/net/NetworkStatsHistory;->txPackets:[J -Landroid/net/NetworkStatsHistory;->VERSION_ADD_ACTIVE:I -Landroid/net/NetworkStatsHistory;->VERSION_ADD_PACKETS:I -Landroid/net/NetworkStatsHistory;->VERSION_INIT:I -Landroid/net/NetworkStatsHistory;->writeToProto(Landroid/util/proto/ProtoOutputStream;J)V -Landroid/net/NetworkStatsHistory;->writeToProto(Landroid/util/proto/ProtoOutputStream;J[JI)V -Landroid/net/NetworkStatsHistory;->writeToStream(Ljava/io/DataOutputStream;)V -Landroid/net/NetworkTemplate;-><init>(ILjava/lang/String;[Ljava/lang/String;Ljava/lang/String;)V -Landroid/net/NetworkTemplate;-><init>(ILjava/lang/String;[Ljava/lang/String;Ljava/lang/String;III)V -Landroid/net/NetworkTemplate;-><init>(Landroid/os/Parcel;)V -Landroid/net/NetworkTemplate;->BACKUP_VERSION:I -Landroid/net/NetworkTemplate;->buildTemplateBluetooth()Landroid/net/NetworkTemplate; -Landroid/net/NetworkTemplate;->buildTemplateProxy()Landroid/net/NetworkTemplate; -Landroid/net/NetworkTemplate;->buildTemplateWifi(Ljava/lang/String;)Landroid/net/NetworkTemplate; -Landroid/net/NetworkTemplate;->forceAllNetworkTypes()V -Landroid/net/NetworkTemplate;->getBytesForBackup()[B -Landroid/net/NetworkTemplate;->getMatchRuleName(I)Ljava/lang/String; -Landroid/net/NetworkTemplate;->getNetworkId()Ljava/lang/String; -Landroid/net/NetworkTemplate;->getNetworkTemplateFromBackup(Ljava/io/DataInputStream;)Landroid/net/NetworkTemplate; -Landroid/net/NetworkTemplate;->isKnownMatchRule(I)Z -Landroid/net/NetworkTemplate;->isMatchRuleMobile()Z -Landroid/net/NetworkTemplate;->isPersistable()Z -Landroid/net/NetworkTemplate;->matches(Landroid/net/NetworkIdentity;)Z -Landroid/net/NetworkTemplate;->matchesBluetooth(Landroid/net/NetworkIdentity;)Z -Landroid/net/NetworkTemplate;->matchesDefaultNetwork(Landroid/net/NetworkIdentity;)Z -Landroid/net/NetworkTemplate;->matchesEthernet(Landroid/net/NetworkIdentity;)Z -Landroid/net/NetworkTemplate;->matchesMetered(Landroid/net/NetworkIdentity;)Z -Landroid/net/NetworkTemplate;->matchesMobile(Landroid/net/NetworkIdentity;)Z -Landroid/net/NetworkTemplate;->matchesMobileWildcard(Landroid/net/NetworkIdentity;)Z -Landroid/net/NetworkTemplate;->matchesProxy(Landroid/net/NetworkIdentity;)Z -Landroid/net/NetworkTemplate;->matchesRoaming(Landroid/net/NetworkIdentity;)Z -Landroid/net/NetworkTemplate;->matchesSubscriberId(Ljava/lang/String;)Z -Landroid/net/NetworkTemplate;->matchesWifi(Landroid/net/NetworkIdentity;)Z -Landroid/net/NetworkTemplate;->matchesWifiWildcard(Landroid/net/NetworkIdentity;)Z -Landroid/net/NetworkTemplate;->MATCH_BLUETOOTH:I -Landroid/net/NetworkTemplate;->MATCH_ETHERNET:I -Landroid/net/NetworkTemplate;->MATCH_MOBILE:I -Landroid/net/NetworkTemplate;->MATCH_MOBILE_WILDCARD:I -Landroid/net/NetworkTemplate;->MATCH_PROXY:I -Landroid/net/NetworkTemplate;->MATCH_WIFI:I -Landroid/net/NetworkTemplate;->MATCH_WIFI_WILDCARD:I -Landroid/net/NetworkTemplate;->mDefaultNetwork:I -Landroid/net/NetworkTemplate;->mMatchRule:I -Landroid/net/NetworkTemplate;->mMatchSubscriberIds:[Ljava/lang/String; -Landroid/net/NetworkTemplate;->mMetered:I -Landroid/net/NetworkTemplate;->mNetworkId:Ljava/lang/String; -Landroid/net/NetworkTemplate;->mRoaming:I -Landroid/net/NetworkTemplate;->mSubscriberId:Ljava/lang/String; -Landroid/net/NetworkTemplate;->sForceAllNetworkTypes:Z -Landroid/net/NetworkTemplate;->TAG:Ljava/lang/String; -Landroid/net/NetworkUtils;-><init>()V -Landroid/net/NetworkUtils;->addressTypeMatches(Ljava/net/InetAddress;Ljava/net/InetAddress;)Z -Landroid/net/NetworkUtils;->bindProcessToNetwork(I)Z -Landroid/net/NetworkUtils;->bindProcessToNetworkForHostResolution(I)Z -Landroid/net/NetworkUtils;->bindSocketToNetwork(II)I -Landroid/net/NetworkUtils;->deduplicatePrefixSet(Ljava/util/TreeSet;)Ljava/util/TreeSet; -Landroid/net/NetworkUtils;->getBoundNetworkForProcess()I -Landroid/net/NetworkUtils;->getNetworkPart(Ljava/net/InetAddress;I)Ljava/net/InetAddress; -Landroid/net/NetworkUtils;->hexToInet6Address(Ljava/lang/String;)Ljava/net/InetAddress; -Landroid/net/NetworkUtils;->inetAddressToInt(Ljava/net/Inet4Address;)I -Landroid/net/NetworkUtils;->makeStrings(Ljava/util/Collection;)[Ljava/lang/String; -Landroid/net/NetworkUtils;->maskRawAddress([BI)V -Landroid/net/NetworkUtils;->netmaskIntToPrefixLength(I)I -Landroid/net/NetworkUtils;->parcelInetAddress(Landroid/os/Parcel;Ljava/net/InetAddress;I)V -Landroid/net/NetworkUtils;->parseIpAndMask(Ljava/lang/String;)Landroid/util/Pair; -Landroid/net/NetworkUtils;->protectFromVpn(I)Z -Landroid/net/NetworkUtils;->queryUserAccess(II)Z -Landroid/net/NetworkUtils;->routedIPv4AddressCount(Ljava/util/TreeSet;)J -Landroid/net/NetworkUtils;->routedIPv6AddressCount(Ljava/util/TreeSet;)Ljava/math/BigInteger; -Landroid/net/NetworkUtils;->setupRaSocket(Ljava/io/FileDescriptor;I)V -Landroid/net/NetworkUtils;->TAG:Ljava/lang/String; -Landroid/net/NetworkUtils;->unparcelInetAddress(Landroid/os/Parcel;)Ljava/net/InetAddress; -Landroid/net/NetworkWatchlistManager;-><init>(Landroid/content/Context;)V -Landroid/net/NetworkWatchlistManager;-><init>(Landroid/content/Context;Lcom/android/internal/net/INetworkWatchlistManager;)V -Landroid/net/NetworkWatchlistManager;->getWatchlistConfigHash()[B -Landroid/net/NetworkWatchlistManager;->mContext:Landroid/content/Context; -Landroid/net/NetworkWatchlistManager;->mNetworkWatchlistManager:Lcom/android/internal/net/INetworkWatchlistManager; -Landroid/net/NetworkWatchlistManager;->reloadWatchlist()V -Landroid/net/NetworkWatchlistManager;->reportWatchlistIfNecessary()V -Landroid/net/NetworkWatchlistManager;->SHARED_MEMORY_TAG:Ljava/lang/String; -Landroid/net/NetworkWatchlistManager;->TAG:Ljava/lang/String; Landroid/net/nsd/DnsSdTxtRecord;-><init>()V Landroid/net/nsd/DnsSdTxtRecord;-><init>(Landroid/net/nsd/DnsSdTxtRecord;)V Landroid/net/nsd/DnsSdTxtRecord;-><init>([B)V @@ -37727,43 +36556,6 @@ Landroid/net/Proxy;->sDefaultProxySelector:Ljava/net/ProxySelector; Landroid/net/Proxy;->setHttpProxySystemProperty(Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Landroid/net/Uri;)V Landroid/net/Proxy;->TAG:Ljava/lang/String; Landroid/net/Proxy;->validate(Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;)I -Landroid/net/ProxyInfo;-><init>(Landroid/net/ProxyInfo;)V -Landroid/net/ProxyInfo;-><init>(Landroid/net/Uri;)V -Landroid/net/ProxyInfo;-><init>(Landroid/net/Uri;I)V -Landroid/net/ProxyInfo;-><init>(Ljava/lang/String;)V -Landroid/net/ProxyInfo;-><init>(Ljava/lang/String;ILjava/lang/String;[Ljava/lang/String;)V -Landroid/net/ProxyInfo;->getExclusionListAsString()Ljava/lang/String; -Landroid/net/ProxyInfo;->getSocketAddress()Ljava/net/InetSocketAddress; -Landroid/net/ProxyInfo;->isValid()Z -Landroid/net/ProxyInfo;->LOCAL_EXCL_LIST:Ljava/lang/String; -Landroid/net/ProxyInfo;->LOCAL_HOST:Ljava/lang/String; -Landroid/net/ProxyInfo;->LOCAL_PORT:I -Landroid/net/ProxyInfo;->makeProxy()Ljava/net/Proxy; -Landroid/net/ProxyInfo;->mExclusionList:Ljava/lang/String; -Landroid/net/ProxyInfo;->mHost:Ljava/lang/String; -Landroid/net/ProxyInfo;->mPacFileUrl:Landroid/net/Uri; -Landroid/net/ProxyInfo;->mParsedExclusionList:[Ljava/lang/String; -Landroid/net/ProxyInfo;->mPort:I -Landroid/net/ProxyInfo;->setExclusionList(Ljava/lang/String;)V -Landroid/net/RouteInfo;-><init>(Landroid/net/IpPrefix;)V -Landroid/net/RouteInfo;-><init>(Landroid/net/IpPrefix;I)V -Landroid/net/RouteInfo;-><init>(Landroid/net/IpPrefix;Ljava/net/InetAddress;)V -Landroid/net/RouteInfo;-><init>(Landroid/net/IpPrefix;Ljava/net/InetAddress;Ljava/lang/String;I)V -Landroid/net/RouteInfo;-><init>(Landroid/net/LinkAddress;)V -Landroid/net/RouteInfo;->getDestinationLinkAddress()Landroid/net/LinkAddress; -Landroid/net/RouteInfo;->getType()I -Landroid/net/RouteInfo;->isHostRoute()Z -Landroid/net/RouteInfo;->isIPv4Default()Z -Landroid/net/RouteInfo;->isIPv6Default()Z -Landroid/net/RouteInfo;->makeHostRoute(Ljava/net/InetAddress;Ljava/lang/String;)Landroid/net/RouteInfo; -Landroid/net/RouteInfo;->makeHostRoute(Ljava/net/InetAddress;Ljava/net/InetAddress;Ljava/lang/String;)Landroid/net/RouteInfo; -Landroid/net/RouteInfo;->mDestination:Landroid/net/IpPrefix; -Landroid/net/RouteInfo;->mHasGateway:Z -Landroid/net/RouteInfo;->mInterface:Ljava/lang/String; -Landroid/net/RouteInfo;->mType:I -Landroid/net/RouteInfo;->RTN_THROW:I -Landroid/net/RouteInfo;->RTN_UNICAST:I -Landroid/net/RouteInfo;->RTN_UNREACHABLE:I Landroid/net/RssiCurve;-><init>(Landroid/os/Parcel;)V Landroid/net/RssiCurve;->DEFAULT_ACTIVE_NETWORK_RSSI_BOOST:I Landroid/net/rtp/AudioCodec;-><init>(ILjava/lang/String;Ljava/lang/String;)V @@ -38034,11 +36826,6 @@ Landroid/net/SntpClient;->writeTimeStamp([BIJ)V Landroid/net/SSLSessionCache;-><init>(Ljava/lang/Object;)V Landroid/net/SSLSessionCache;->install(Landroid/net/SSLSessionCache;Ljavax/net/ssl/SSLContext;)V Landroid/net/SSLSessionCache;->TAG:Ljava/lang/String; -Landroid/net/StaticIpConfiguration;-><init>(Landroid/net/StaticIpConfiguration;)V -Landroid/net/StaticIpConfiguration;->clear()V -Landroid/net/StaticIpConfiguration;->CREATOR:Landroid/os/Parcelable$Creator; -Landroid/net/StaticIpConfiguration;->readFromParcel(Landroid/net/StaticIpConfiguration;Landroid/os/Parcel;)V -Landroid/net/StaticIpConfiguration;->toLinkProperties(Ljava/lang/String;)Landroid/net/LinkProperties; Landroid/net/StringNetworkSpecifier;-><init>(Ljava/lang/String;)V Landroid/net/StringNetworkSpecifier;->CREATOR:Landroid/os/Parcelable$Creator; Landroid/net/StringNetworkSpecifier;->satisfiedBy(Landroid/net/NetworkSpecifier;)Z @@ -38077,15 +36864,6 @@ Landroid/net/TrafficStats;->TYPE_TX_BYTES:I Landroid/net/TrafficStats;->TYPE_TX_PACKETS:I Landroid/net/TrafficStats;->UID_REMOVED:I Landroid/net/TrafficStats;->UID_TETHERING:I -Landroid/net/UidRange;-><init>(II)V -Landroid/net/UidRange;->contains(I)Z -Landroid/net/UidRange;->containsRange(Landroid/net/UidRange;)Z -Landroid/net/UidRange;->count()I -Landroid/net/UidRange;->createForUser(I)Landroid/net/UidRange; -Landroid/net/UidRange;->CREATOR:Landroid/os/Parcelable$Creator; -Landroid/net/UidRange;->getStartUser()I -Landroid/net/UidRange;->start:I -Landroid/net/UidRange;->stop:I Landroid/net/Uri$AbstractHierarchicalUri;-><init>()V Landroid/net/Uri$AbstractHierarchicalUri;->getUserInfoPart()Landroid/net/Uri$Part; Landroid/net/Uri$AbstractHierarchicalUri;->host:Ljava/lang/String; diff --git a/boot/hiddenapi/hiddenapi-unsupported.txt b/boot/hiddenapi/hiddenapi-unsupported.txt index 0265df5e93f5..002d42dbf1dc 100644 --- a/boot/hiddenapi/hiddenapi-unsupported.txt +++ b/boot/hiddenapi/hiddenapi-unsupported.txt @@ -167,16 +167,6 @@ Landroid/media/IMediaScannerListener$Stub;-><init>()V Landroid/media/IMediaScannerService$Stub;->asInterface(Landroid/os/IBinder;)Landroid/media/IMediaScannerService; Landroid/media/session/ISessionManager$Stub;->asInterface(Landroid/os/IBinder;)Landroid/media/session/ISessionManager; Landroid/media/tv/ITvRemoteProvider$Stub;-><init>()V -Landroid/net/IConnectivityManager$Stub$Proxy;-><init>(Landroid/os/IBinder;)V -Landroid/net/IConnectivityManager$Stub$Proxy;->getActiveLinkProperties()Landroid/net/LinkProperties; -Landroid/net/IConnectivityManager$Stub$Proxy;->getActiveNetworkInfo()Landroid/net/NetworkInfo; -Landroid/net/IConnectivityManager$Stub$Proxy;->getAllNetworkInfo()[Landroid/net/NetworkInfo; -Landroid/net/IConnectivityManager$Stub$Proxy;->getAllNetworks()[Landroid/net/Network; -Landroid/net/IConnectivityManager$Stub$Proxy;->getTetherableIfaces()[Ljava/lang/String; -Landroid/net/IConnectivityManager$Stub$Proxy;->getTetherableUsbRegexs()[Ljava/lang/String; -Landroid/net/IConnectivityManager$Stub$Proxy;->getTetheredIfaces()[Ljava/lang/String; -Landroid/net/IConnectivityManager$Stub$Proxy;->mRemote:Landroid/os/IBinder; -Landroid/net/IConnectivityManager$Stub;->asInterface(Landroid/os/IBinder;)Landroid/net/IConnectivityManager; Landroid/net/INetworkManagementEventObserver$Stub;-><init>()V Landroid/net/INetworkPolicyManager$Stub;->asInterface(Landroid/os/IBinder;)Landroid/net/INetworkPolicyManager; Landroid/net/INetworkScoreService$Stub;->asInterface(Landroid/os/IBinder;)Landroid/net/INetworkScoreService; diff --git a/core/java/android/content/Intent.java b/core/java/android/content/Intent.java index 688483a5c969..9e35a32638a8 100644 --- a/core/java/android/content/Intent.java +++ b/core/java/android/content/Intent.java @@ -5736,12 +5736,20 @@ public class Intent implements Parcelable, Cloneable { public static final String EXTRA_REPLACING = "android.intent.extra.REPLACING"; /** - * Used as an int extra field in {@link android.app.AlarmManager} intents + * Used as an int extra field in {@link android.app.AlarmManager} pending intents * to tell the application being invoked how many pending alarms are being - * delievered with the intent. For one-shot alarms this will always be 1. + * delivered with the intent. For one-shot alarms this will always be 1. * For recurring alarms, this might be greater than 1 if the device was * asleep or powered off at the time an earlier alarm would have been * delivered. + * + * <p>Note: You must supply a <b>mutable</b> {@link android.app.PendingIntent} to + * {@code AlarmManager} while setting your alarms to be able to read this value on receiving + * them. <em>Mutability of pending intents must be explicitly specified by apps targeting + * {@link Build.VERSION_CODES#S} or higher</em>. + * + * @see android.app.PendingIntent#FLAG_MUTABLE + * */ public static final String EXTRA_ALARM_COUNT = "android.intent.extra.ALARM_COUNT"; diff --git a/core/java/android/content/pm/PackageItemInfo.java b/core/java/android/content/pm/PackageItemInfo.java index 65ce1e7ef079..dd2080b60b37 100644 --- a/core/java/android/content/pm/PackageItemInfo.java +++ b/core/java/android/content/pm/PackageItemInfo.java @@ -61,7 +61,7 @@ public class PackageItemInfo { public static final int MAX_SAFE_LABEL_LENGTH = 1000; /** @hide */ - public static final float DEFAULT_MAX_LABEL_SIZE_PX = 500f; + public static final float DEFAULT_MAX_LABEL_SIZE_PX = 1000f; /** * Remove {@link Character#isWhitespace(int) whitespace} and non-breaking spaces from the edges diff --git a/core/java/android/content/pm/TEST_MAPPING b/core/java/android/content/pm/TEST_MAPPING index 1eb4504bf591..8bc3734e060d 100644 --- a/core/java/android/content/pm/TEST_MAPPING +++ b/core/java/android/content/pm/TEST_MAPPING @@ -49,9 +49,6 @@ "include-filter": "android.appsecurity.cts.AppSecurityTests#testPermissionDiffCert" } ] - }, - { - "name": "CtsPackageManagerBootTestCases" } ] } diff --git a/core/java/android/telephony/PhoneStateListener.java b/core/java/android/telephony/PhoneStateListener.java index a1ffe345d457..d39b56ddabed 100644 --- a/core/java/android/telephony/PhoneStateListener.java +++ b/core/java/android/telephony/PhoneStateListener.java @@ -621,7 +621,11 @@ public class PhoneStateListener { * The instance of {@link ServiceState} passed as an argument here will have various levels of * location information stripped from it depending on the location permissions that your app * holds. Only apps holding the {@link Manifest.permission#ACCESS_FINE_LOCATION} permission will - * receive all the information in {@link ServiceState}. + * receive all the information in {@link ServiceState}, otherwise the cellIdentity will be null + * if apps only holding the {@link Manifest.permission#ACCESS_COARSE_LOCATION} permission. + * Network operator name in long/short alphanumeric format and numeric id will be null if apps + * holding neither {@link android.Manifest.permission#ACCESS_FINE_LOCATION} nor + * {@link android.Manifest.permission#ACCESS_COARSE_LOCATION}. * * @see ServiceState#STATE_EMERGENCY_ONLY * @see ServiceState#STATE_IN_SERVICE diff --git a/core/java/android/telephony/TelephonyCallback.java b/core/java/android/telephony/TelephonyCallback.java index 1a25c8b4e671..dd4de0a81392 100644 --- a/core/java/android/telephony/TelephonyCallback.java +++ b/core/java/android/telephony/TelephonyCallback.java @@ -663,7 +663,12 @@ public class TelephonyCallback { * levels of location information stripped from it depending on the location permissions * that your app holds. * Only apps holding the {@link Manifest.permission#ACCESS_FINE_LOCATION} permission will - * receive all the information in {@link ServiceState}. + * receive all the information in {@link ServiceState}, otherwise the cellIdentity + * will be null if apps only holding the {@link Manifest.permission#ACCESS_COARSE_LOCATION} + * permission. + * Network operator name in long/short alphanumeric format and numeric id will be null if + * apps holding neither {@link android.Manifest.permission#ACCESS_FINE_LOCATION} nor + * {@link android.Manifest.permission#ACCESS_COARSE_LOCATION}. * * @see ServiceState#STATE_EMERGENCY_ONLY * @see ServiceState#STATE_IN_SERVICE diff --git a/core/java/android/view/contentcapture/MainContentCaptureSession.java b/core/java/android/view/contentcapture/MainContentCaptureSession.java index e0a7bf23f488..4cf553207b6e 100644 --- a/core/java/android/view/contentcapture/MainContentCaptureSession.java +++ b/core/java/android/view/contentcapture/MainContentCaptureSession.java @@ -773,7 +773,7 @@ public final class MainContentCaptureSession extends ContentCaptureSession { void notifyContextUpdated(int sessionId, @Nullable ContentCaptureContext context) { mHandler.post(() -> sendEvent(new ContentCaptureEvent(sessionId, TYPE_CONTEXT_UPDATED) - .setClientContext(context))); + .setClientContext(context), FORCE_FLUSH)); } @Override diff --git a/core/java/android/widget/AnalogClock.java b/core/java/android/widget/AnalogClock.java index d5966269a753..9c1285064afb 100644 --- a/core/java/android/widget/AnalogClock.java +++ b/core/java/android/widget/AnalogClock.java @@ -111,7 +111,9 @@ public class AnalogClock extends View { mSecondsHandFps = AppGlobals.getIntCoreSetting( WidgetFlags.KEY_ANALOG_CLOCK_SECONDS_HAND_FPS, - WidgetFlags.ANALOG_CLOCK_SECONDS_HAND_FPS_DEFAULT); + context.getResources() + .getInteger(com.android.internal.R.integer + .config_defaultAnalogClockSecondsHandFps)); final TypedArray a = context.obtainStyledAttributes( attrs, com.android.internal.R.styleable.AnalogClock, defStyleAttr, defStyleRes); @@ -720,7 +722,7 @@ public class AnalogClock extends View { canvas.restore(); final Drawable secondHand = mSecondHand; - if (secondHand != null) { + if (secondHand != null && mSecondsHandFps > 0) { canvas.save(); canvas.rotate(mSeconds / 60.0f * 360.0f, x, y); @@ -752,7 +754,10 @@ public class AnalogClock extends View { // n positions between two given numbers, where n is the number of ticks per second. This // ensures the second hand advances by a consistent distance despite our handler callbacks // occurring at inconsistent frequencies. - mSeconds = Math.round(rawSeconds * mSecondsHandFps) / (float) mSecondsHandFps; + mSeconds = + mSecondsHandFps <= 0 + ? rawSeconds + : Math.round(rawSeconds * mSecondsHandFps) / (float) mSecondsHandFps; mMinutes = localTime.getMinute() + mSeconds / 60.0f; mHour = localTime.getHour() + mMinutes / 60.0f; mChanged = true; @@ -789,7 +794,7 @@ public class AnalogClock extends View { LocalTime localTime = zonedDateTime.toLocalTime(); long millisUntilNextTick; - if (mSecondHand == null) { + if (mSecondHand == null || mSecondsHandFps <= 0) { // If there's no second hand, then tick at the start of the next minute. // // This must be done with ZonedDateTime as opposed to LocalDateTime to ensure proper diff --git a/core/java/android/widget/WidgetFlags.java b/core/java/android/widget/WidgetFlags.java index 097126807f71..fb40ee5ec843 100644 --- a/core/java/android/widget/WidgetFlags.java +++ b/core/java/android/widget/WidgetFlags.java @@ -207,9 +207,6 @@ public final class WidgetFlags { public static final String KEY_ANALOG_CLOCK_SECONDS_HAND_FPS = "widget__analog_clock_seconds_hand_fps"; - /** Default value for the flag {@link #ANALOG_CLOCK_SECONDS_HAND_FPS}. */ - public static final int ANALOG_CLOCK_SECONDS_HAND_FPS_DEFAULT = 1; - private WidgetFlags() { } } diff --git a/core/res/AndroidManifest.xml b/core/res/AndroidManifest.xml index 64b8a1a81e69..14d200d00d56 100644 --- a/core/res/AndroidManifest.xml +++ b/core/res/AndroidManifest.xml @@ -1663,11 +1663,6 @@ <permission android:name="android.permission.INSTALL_LOCATION_TIME_ZONE_PROVIDER_SERVICE" android:protectionLevel="signature|privileged" /> - <!-- The system server uses this permission to install a default secondary location time zone - provider. - --> - <uses-permission android:name="android.permission.INSTALL_LOCATION_TIME_ZONE_PROVIDER_SERVICE"/> - <!-- @SystemApi @hide Allows an application to bind to a android.service.TimeZoneProviderService for the purpose of detecting the device's time zone. This prevents arbitrary clients connecting to the time zone provider service. The system server checks that the provider's @@ -5809,10 +5804,6 @@ android:label="@string/sensor_notification_service"/> <!-- Attribution for Twilight service. --> <attribution android:tag="TwilightService" android:label="@string/twilight_service"/> - <!-- Attribution for the Offline LocationTimeZoneProvider, used to detect time zone using - on-device data --> - <attribution android:tag="OfflineLocationTimeZoneProviderService" - android:label="@string/offline_location_time_zone_detection_service_attribution"/> <!-- Attribution for Gnss Time Update service. --> <attribution android:tag="GnssTimeUpdateService" android:label="@string/gnss_time_update_service"/> @@ -6292,19 +6283,6 @@ </intent-filter> </service> - <!-- AOSP configures a default secondary LocationTimeZoneProvider that uses an on-device - data set from the com.android.geotz APEX. --> - <service android:name="com.android.timezone.location.provider.OfflineLocationTimeZoneProviderService" - android:enabled="@bool/config_enableSecondaryLocationTimeZoneProvider" - android:permission="android.permission.BIND_TIME_ZONE_PROVIDER_SERVICE" - android:exported="false"> - <intent-filter> - <action android:name="android.service.timezone.SecondaryLocationTimeZoneProviderService" /> - </intent-filter> - <meta-data android:name="serviceVersion" android:value="1" /> - <meta-data android:name="serviceIsMultiuser" android:value="true" /> - </service> - <provider android:name="com.android.server.textclassifier.IconsContentProvider" android:authorities="com.android.textclassifier.icons" diff --git a/core/res/res/values/config.xml b/core/res/res/values/config.xml index 646fbd3978fd..8dfbdccf5706 100644 --- a/core/res/res/values/config.xml +++ b/core/res/res/values/config.xml @@ -5038,4 +5038,8 @@ <!-- The amount of dimming to apply to wallpapers with mid range luminance. 0 displays the wallpaper at full brightness. 1 displays the wallpaper as fully black. --> <item name="config_wallpaperDimAmount" format="float" type="dimen">0.05</item> + + <!-- The default number of times per second that the seconds hand on AnalogClock ticks. If set + to 0, the seconds hand will be disabled. --> + <integer name="config_defaultAnalogClockSecondsHandFps">1</integer> </resources> diff --git a/core/res/res/values/strings.xml b/core/res/res/values/strings.xml index d1a5cc49be9f..a99a22009e3b 100644 --- a/core/res/res/values/strings.xml +++ b/core/res/res/values/strings.xml @@ -444,12 +444,6 @@ <string name="sensor_notification_service">Sensor Notification Service</string> <!-- Attribution for Twilight service. [CHAR LIMIT=NONE]--> <string name="twilight_service">Twilight Service</string> - <!-- Attribution for the Offline LocationTimeZoneProvider service, i.e. the service capable of - performing time zone detection using time zone geospatial information held on the device. - This text is shown in UIs related to an application name to help users and developers to - understand which sub-unit of an application is requesting permissions and using power. - [CHAR LIMIT=NONE]--> - <string name="offline_location_time_zone_detection_service_attribution">Time Zone Detector (No connectivity)</string> <!-- Attribution for Gnss Time Update service. [CHAR LIMIT=NONE]--> <string name="gnss_time_update_service">GNSS Time Update Service</string> @@ -3964,6 +3958,8 @@ <string name="deny">Deny</string> <string name="permission_request_notification_title">Permission requested</string> <string name="permission_request_notification_with_subtitle">Permission requested\nfor account <xliff:g id="account" example="foo@gmail.com">%s</xliff:g>.</string> + <!-- Title and subtitle for notification shown when app request account access (two lines) [CHAR LIMIT=NONE] --> + <string name="permission_request_notification_for_app_with_subtitle">Permission requested by <xliff:g id="app" example="Gmail">%1$s</xliff:g>\nfor account <xliff:g id="account" example="foo@gmail.com">%2$s</xliff:g>.</string> <!-- Message to show when an intent automatically switches users into the personal profile. --> <string name="forward_intent_to_owner">You\'re using this app outside of your work profile</string> diff --git a/core/res/res/values/symbols.xml b/core/res/res/values/symbols.xml index c05adfd4ba2e..b76a7804461e 100644 --- a/core/res/res/values/symbols.xml +++ b/core/res/res/values/symbols.xml @@ -490,6 +490,7 @@ <java-symbol type="integer" name="config_smartSelectionInitializedTimeoutMillis" /> <java-symbol type="integer" name="config_smartSelectionInitializingTimeoutMillis" /> <java-symbol type="bool" name="config_hibernationDeletesOatArtifactsEnabled"/> + <java-symbol type="integer" name="config_defaultAnalogClockSecondsHandFps"/> <java-symbol type="color" name="tab_indicator_text_v4" /> @@ -560,6 +561,7 @@ <java-symbol type="string" name="notification_title" /> <java-symbol type="string" name="other_networks_no_internet" /> <java-symbol type="string" name="permission_request_notification_with_subtitle" /> + <java-symbol type="string" name="permission_request_notification_for_app_with_subtitle" /> <java-symbol type="string" name="prepend_shortcut_label" /> <java-symbol type="string" name="private_dns_broken_detailed" /> <java-symbol type="string" name="paste_as_plain_text" /> diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipBoundsAlgorithm.java b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipBoundsAlgorithm.java index 046c32071358..a4b866aa3f5e 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipBoundsAlgorithm.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipBoundsAlgorithm.java @@ -19,6 +19,7 @@ package com.android.wm.shell.pip; import static android.util.TypedValue.COMPLEX_UNIT_DIP; import android.annotation.NonNull; +import android.annotation.Nullable; import android.app.PictureInPictureParams; import android.content.Context; import android.content.pm.ActivityInfo; @@ -454,6 +455,56 @@ public class PipBoundsAlgorithm { } /** + * @return the normal bounds adjusted so that they fit the menu actions. + */ + public Rect adjustNormalBoundsToFitMenu(@NonNull Rect normalBounds, + @Nullable Size minMenuSize) { + if (minMenuSize == null) { + return normalBounds; + } + if (normalBounds.width() >= minMenuSize.getWidth() + && normalBounds.height() >= minMenuSize.getHeight()) { + // The normal bounds can fit the menu as is, no need to adjust the bounds. + return normalBounds; + } + final Rect adjustedNormalBounds = new Rect(); + final boolean needsWidthAdj = minMenuSize.getWidth() > normalBounds.width(); + final boolean needsHeightAdj = minMenuSize.getHeight() > normalBounds.height(); + final int adjWidth; + final int adjHeight; + if (needsWidthAdj && needsHeightAdj) { + // Both the width and the height are too small - find the edge that needs the larger + // adjustment and scale that edge. The other edge will scale beyond the minMenuSize + // when the aspect ratio is applied. + final float widthScaleFactor = + ((float) (minMenuSize.getWidth())) / ((float) (normalBounds.width())); + final float heightScaleFactor = + ((float) (minMenuSize.getHeight())) / ((float) (normalBounds.height())); + if (widthScaleFactor > heightScaleFactor) { + adjWidth = minMenuSize.getWidth(); + adjHeight = Math.round(adjWidth / mPipBoundsState.getAspectRatio()); + } else { + adjHeight = minMenuSize.getHeight(); + adjWidth = Math.round(adjHeight * mPipBoundsState.getAspectRatio()); + } + } else if (needsWidthAdj) { + // Width is too small - use the min menu size width instead. + adjWidth = minMenuSize.getWidth(); + adjHeight = Math.round(adjWidth / mPipBoundsState.getAspectRatio()); + } else { + // Height is too small - use the min menu size height instead. + adjHeight = minMenuSize.getHeight(); + adjWidth = Math.round(adjHeight * mPipBoundsState.getAspectRatio()); + } + adjustedNormalBounds.set(0, 0, adjWidth, adjHeight); + // Make sure the bounds conform to the aspect ratio and min edge size. + transformBoundsToAspectRatio(adjustedNormalBounds, + mPipBoundsState.getAspectRatio(), true /* useCurrentMinEdgeSize */, + true /* useCurrentSize */); + return adjustedNormalBounds; + } + + /** * Dumps internal states. */ public void dump(PrintWriter pw, String prefix) { diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipTaskOrganizer.java b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipTaskOrganizer.java index 324a6e27a242..f367cd608f37 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipTaskOrganizer.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipTaskOrganizer.java @@ -722,6 +722,14 @@ public class PipTaskOrganizer implements ShellTaskOrganizer.TaskListener, if (info.displayId != Display.DEFAULT_DISPLAY && mOnDisplayIdChangeCallback != null) { mOnDisplayIdChangeCallback.accept(Display.DEFAULT_DISPLAY); } + + final PipAnimationController.PipTransitionAnimator animator = + mPipAnimationController.getCurrentAnimator(); + if (animator != null) { + animator.removeAllUpdateListeners(); + animator.removeAllListeners(); + animator.cancel(); + } } @Override diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipTouchHandler.java b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipTouchHandler.java index b1086c575f49..0bcd1a363eb6 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipTouchHandler.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipTouchHandler.java @@ -726,12 +726,17 @@ public class PipTouchHandler { } private void animateToNormalSize(Runnable callback) { + // Save the current bounds as the user-resize bounds. mPipResizeGestureHandler.setUserResizeBounds(mPipBoundsState.getBounds()); - final Rect normalBounds = new Rect(mPipBoundsState.getNormalBounds()); + + final Size minMenuSize = mMenuController.getEstimatedMinMenuSize(); + final Rect normalBounds = mPipBoundsState.getNormalBounds(); + final Rect destBounds = mPipBoundsAlgorithm.adjustNormalBoundsToFitMenu(normalBounds, + minMenuSize); Rect restoredMovementBounds = new Rect(); - mPipBoundsAlgorithm.getMovementBounds(normalBounds, + mPipBoundsAlgorithm.getMovementBounds(destBounds, mInsetBounds, restoredMovementBounds, mIsImeShowing ? mImeHeight : 0); - mSavedSnapFraction = mMotionHelper.animateToExpandedState(normalBounds, + mSavedSnapFraction = mMotionHelper.animateToExpandedState(destBounds, mPipBoundsState.getMovementBounds(), restoredMovementBounds, callback); } diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/pip/PipBoundsAlgorithmTest.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/pip/PipBoundsAlgorithmTest.java index a0c6d1138698..90f898aa09da 100644 --- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/pip/PipBoundsAlgorithmTest.java +++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/pip/PipBoundsAlgorithmTest.java @@ -402,6 +402,64 @@ public class PipBoundsAlgorithmTest extends ShellTestCase { assertBoundsInclusionWithMargin("useDefaultBounds", defaultBounds, actualBounds); } + @Test + public void adjustNormalBoundsToFitMenu_alreadyFits() { + final Rect normalBounds = new Rect(0, 0, 400, 711); + final Size minMenuSize = new Size(396, 292); + mPipBoundsState.setAspectRatio( + ((float) normalBounds.width()) / ((float) normalBounds.height())); + + final Rect bounds = + mPipBoundsAlgorithm.adjustNormalBoundsToFitMenu(normalBounds, minMenuSize); + + assertEquals(normalBounds, bounds); + } + + @Test + public void adjustNormalBoundsToFitMenu_widthTooSmall() { + final Rect normalBounds = new Rect(0, 0, 297, 528); + final Size minMenuSize = new Size(396, 292); + mPipBoundsState.setAspectRatio( + ((float) normalBounds.width()) / ((float) normalBounds.height())); + + final Rect bounds = + mPipBoundsAlgorithm.adjustNormalBoundsToFitMenu(normalBounds, minMenuSize); + + assertEquals(minMenuSize.getWidth(), bounds.width()); + assertEquals(minMenuSize.getWidth() / mPipBoundsState.getAspectRatio(), + bounds.height(), 0.3f); + } + + @Test + public void adjustNormalBoundsToFitMenu_heightTooSmall() { + final Rect normalBounds = new Rect(0, 0, 400, 280); + final Size minMenuSize = new Size(396, 292); + mPipBoundsState.setAspectRatio( + ((float) normalBounds.width()) / ((float) normalBounds.height())); + + final Rect bounds = + mPipBoundsAlgorithm.adjustNormalBoundsToFitMenu(normalBounds, minMenuSize); + + assertEquals(minMenuSize.getHeight(), bounds.height()); + assertEquals(minMenuSize.getHeight() * mPipBoundsState.getAspectRatio(), + bounds.width(), 0.3f); + } + + @Test + public void adjustNormalBoundsToFitMenu_widthAndHeightTooSmall() { + final Rect normalBounds = new Rect(0, 0, 350, 280); + final Size minMenuSize = new Size(396, 292); + mPipBoundsState.setAspectRatio( + ((float) normalBounds.width()) / ((float) normalBounds.height())); + + final Rect bounds = + mPipBoundsAlgorithm.adjustNormalBoundsToFitMenu(normalBounds, minMenuSize); + + assertEquals(minMenuSize.getWidth(), bounds.width()); + assertEquals(minMenuSize.getWidth() / mPipBoundsState.getAspectRatio(), + bounds.height(), 0.3f); + } + private void overrideDefaultAspectRatio(float aspectRatio) { final TestableResources res = mContext.getOrCreateTestableResources(); res.addOverride( diff --git a/libs/hwui/WebViewFunctorManager.cpp b/libs/hwui/WebViewFunctorManager.cpp index 92e20c477669..974c8635b3df 100644 --- a/libs/hwui/WebViewFunctorManager.cpp +++ b/libs/hwui/WebViewFunctorManager.cpp @@ -126,7 +126,14 @@ void WebViewFunctor::drawGl(const DrawGlInfo& drawInfo) { renderthread::CanvasContext::getActiveContext(); if (activeContext != nullptr) { ASurfaceControl* rootSurfaceControl = activeContext->getSurfaceControl(); - if (rootSurfaceControl) overlayParams.overlaysMode = OverlaysMode::Enabled; + if (rootSurfaceControl) { + overlayParams.overlaysMode = OverlaysMode::Enabled; + int32_t rgid = activeContext->getSurfaceControlGenerationId(); + if (mParentSurfaceControlGenerationId != rgid) { + reparentSurfaceControl(rootSurfaceControl); + mParentSurfaceControlGenerationId = rgid; + } + } } } @@ -195,6 +202,7 @@ ASurfaceControl* WebViewFunctor::getSurfaceControl() { LOG_ALWAYS_FATAL_IF(rootSurfaceControl == nullptr, "Null root surface control!"); auto funcs = renderthread::RenderThread::getInstance().getASurfaceControlFunctions(); + mParentSurfaceControlGenerationId = activeContext->getSurfaceControlGenerationId(); mSurfaceControl = funcs.createFunc(rootSurfaceControl, "Webview Overlay SurfaceControl"); ASurfaceTransaction* transaction = funcs.transactionCreateFunc(); activeContext->prepareSurfaceControlForWebview(); @@ -218,6 +226,17 @@ void WebViewFunctor::mergeTransaction(ASurfaceTransaction* transaction) { } } +void WebViewFunctor::reparentSurfaceControl(ASurfaceControl* parent) { + ATRACE_NAME("WebViewFunctor::reparentSurfaceControl"); + if (mSurfaceControl == nullptr) return; + + auto funcs = renderthread::RenderThread::getInstance().getASurfaceControlFunctions(); + ASurfaceTransaction* transaction = funcs.transactionCreateFunc(); + funcs.transactionReparentFunc(transaction, mSurfaceControl, parent); + mergeTransaction(transaction); + funcs.transactionDeleteFunc(transaction); +} + WebViewFunctorManager& WebViewFunctorManager::instance() { static WebViewFunctorManager sInstance; return sInstance; diff --git a/libs/hwui/WebViewFunctorManager.h b/libs/hwui/WebViewFunctorManager.h index a84cda550567..048d1fbe1c7c 100644 --- a/libs/hwui/WebViewFunctorManager.h +++ b/libs/hwui/WebViewFunctorManager.h @@ -85,12 +85,16 @@ public: } private: + void reparentSurfaceControl(ASurfaceControl* parent); + +private: WebViewFunctorCallbacks mCallbacks; void* const mData; int mFunctor; RenderMode mMode; bool mHasContext = false; bool mCreatedHandle = false; + int32_t mParentSurfaceControlGenerationId = 0; ASurfaceControl* mSurfaceControl = nullptr; }; diff --git a/libs/hwui/renderthread/CanvasContext.cpp b/libs/hwui/renderthread/CanvasContext.cpp index 0c9711ba8025..81cee6103d22 100644 --- a/libs/hwui/renderthread/CanvasContext.cpp +++ b/libs/hwui/renderthread/CanvasContext.cpp @@ -201,6 +201,7 @@ void CanvasContext::setSurfaceControl(ASurfaceControl* surfaceControl) { funcs.releaseFunc(mSurfaceControl); } mSurfaceControl = surfaceControl; + mSurfaceControlGenerationId++; mExpectSurfaceStats = surfaceControl != nullptr; if (mSurfaceControl != nullptr) { funcs.acquireFunc(mSurfaceControl); diff --git a/libs/hwui/renderthread/CanvasContext.h b/libs/hwui/renderthread/CanvasContext.h index 3279ccb8e597..85af3e4fb0b6 100644 --- a/libs/hwui/renderthread/CanvasContext.h +++ b/libs/hwui/renderthread/CanvasContext.h @@ -110,6 +110,7 @@ public: GrDirectContext* getGrContext() const { return mRenderThread.getGrContext(); } ASurfaceControl* getSurfaceControl() const { return mSurfaceControl; } + int32_t getSurfaceControlGenerationId() const { return mSurfaceControlGenerationId; } // Won't take effect until next EGLSurface creation void setSwapBehavior(SwapBehavior swapBehavior); @@ -253,6 +254,9 @@ private: // The SurfaceControl reference is passed from ViewRootImpl, can be set to // NULL to remove the reference ASurfaceControl* mSurfaceControl = nullptr; + // id to track surface control changes and WebViewFunctor uses it to determine + // whether reparenting is needed + int32_t mSurfaceControlGenerationId = 0; // stopped indicates the CanvasContext will reject actual redraw operations, // and defer repaint until it is un-stopped bool mStopped = false; diff --git a/libs/hwui/renderthread/RenderThread.cpp b/libs/hwui/renderthread/RenderThread.cpp index 524407d2b9b0..f83c0a4926f9 100644 --- a/libs/hwui/renderthread/RenderThread.cpp +++ b/libs/hwui/renderthread/RenderThread.cpp @@ -98,6 +98,10 @@ ASurfaceControlFunctions::ASurfaceControlFunctions() { LOG_ALWAYS_FATAL_IF(transactionApplyFunc == nullptr, "Failed to find required symbol ASurfaceTransaction_apply!"); + transactionReparentFunc = (AST_reparent)dlsym(handle_, "ASurfaceTransaction_reparent"); + LOG_ALWAYS_FATAL_IF(transactionReparentFunc == nullptr, + "Failed to find required symbol transactionReparentFunc!"); + transactionSetVisibilityFunc = (AST_setVisibility)dlsym(handle_, "ASurfaceTransaction_setVisibility"); LOG_ALWAYS_FATAL_IF(transactionSetVisibilityFunc == nullptr, diff --git a/libs/hwui/renderthread/RenderThread.h b/libs/hwui/renderthread/RenderThread.h index c5e3746587b8..05d225b856db 100644 --- a/libs/hwui/renderthread/RenderThread.h +++ b/libs/hwui/renderthread/RenderThread.h @@ -94,6 +94,9 @@ typedef uint64_t (*ASCStats_getFrameNumber)(ASurfaceControlStats* stats); typedef ASurfaceTransaction* (*AST_create)(); typedef void (*AST_delete)(ASurfaceTransaction* transaction); typedef void (*AST_apply)(ASurfaceTransaction* transaction); +typedef void (*AST_reparent)(ASurfaceTransaction* aSurfaceTransaction, + ASurfaceControl* aSurfaceControl, + ASurfaceControl* newParentASurfaceControl); typedef void (*AST_setVisibility)(ASurfaceTransaction* transaction, ASurfaceControl* surface_control, int8_t visibility); typedef void (*AST_setZOrder)(ASurfaceTransaction* transaction, ASurfaceControl* surface_control, @@ -113,6 +116,7 @@ struct ASurfaceControlFunctions { AST_create transactionCreateFunc; AST_delete transactionDeleteFunc; AST_apply transactionApplyFunc; + AST_reparent transactionReparentFunc; AST_setVisibility transactionSetVisibilityFunc; AST_setZOrder transactionSetZOrderFunc; }; diff --git a/media/java/android/media/AudioFormat.java b/media/java/android/media/AudioFormat.java index c8412f214cfa..1644ec892c7e 100644 --- a/media/java/android/media/AudioFormat.java +++ b/media/java/android/media/AudioFormat.java @@ -310,6 +310,10 @@ public final class AudioFormat implements Parcelable { public static final int ENCODING_LEGACY_SHORT_ARRAY_THRESHOLD = ENCODING_OPUS; /** Audio data format: PCM 24 bit per sample packed as 3 bytes. + * + * The bytes are in little-endian order, so the least significant byte + * comes first in the byte array. + * * Not guaranteed to be supported by devices, may be emulated if not supported. */ public static final int ENCODING_PCM_24BIT_PACKED = 21; /** Audio data format: PCM 32 bit per sample. diff --git a/media/java/android/media/MediaRouter2Manager.java b/media/java/android/media/MediaRouter2Manager.java index 915cb1272040..628f7eef84f9 100644 --- a/media/java/android/media/MediaRouter2Manager.java +++ b/media/java/android/media/MediaRouter2Manager.java @@ -221,10 +221,24 @@ public final class MediaRouter2Manager { Objects.requireNonNull(packageName, "packageName must not be null"); List<RoutingSessionInfo> sessions = getRoutingSessions(packageName); - return getAvailableRoutesForRoutingSession(sessions.get(sessions.size() - 1)); + return getAvailableRoutes(sessions.get(sessions.size() - 1)); } /** + * Gets routes that can be transferable seamlessly for an application. + * + * @param packageName the package name of the application + */ + @NonNull + public List<MediaRoute2Info> getTransferableRoutes(@NonNull String packageName) { + Objects.requireNonNull(packageName, "packageName must not be null"); + + List<RoutingSessionInfo> sessions = getRoutingSessions(packageName); + return getTransferableRoutes(sessions.get(sessions.size() - 1)); + } + + + /** * Gets available routes for the given routing session. * The returned routes can be passed to * {@link #transfer(RoutingSessionInfo, MediaRoute2Info)} for transferring the routing session. @@ -232,8 +246,7 @@ public final class MediaRouter2Manager { * @param sessionInfo the routing session that would be transferred */ @NonNull - public List<MediaRoute2Info> getAvailableRoutesForRoutingSession( - @NonNull RoutingSessionInfo sessionInfo) { + public List<MediaRoute2Info> getAvailableRoutes(@NonNull RoutingSessionInfo sessionInfo) { Objects.requireNonNull(sessionInfo, "sessionInfo must not be null"); List<MediaRoute2Info> routes = new ArrayList<>(); @@ -256,6 +269,45 @@ public final class MediaRouter2Manager { } /** + * Gets routes that can be transferable seamlessly for the given routing session. + * The returned routes can be passed to + * {@link #transfer(RoutingSessionInfo, MediaRoute2Info)} for transferring the routing session. + * <p> + * This includes routes that are {@link RoutingSessionInfo#getTransferableRoutes() transferable} + * by provider itself and routes that are different playback type (e.g. local/remote) + * from the given routing session. + * + * @param sessionInfo the routing session that would be transferred + */ + @NonNull + public List<MediaRoute2Info> getTransferableRoutes(@NonNull RoutingSessionInfo sessionInfo) { + Objects.requireNonNull(sessionInfo, "sessionInfo must not be null"); + + List<MediaRoute2Info> routes = new ArrayList<>(); + + String packageName = sessionInfo.getClientPackageName(); + List<String> preferredFeatures = mPreferredFeaturesMap.get(packageName); + if (preferredFeatures == null) { + preferredFeatures = Collections.emptyList(); + } + synchronized (mRoutesLock) { + for (MediaRoute2Info route : mRoutes.values()) { + if (sessionInfo.getSelectedRoutes().contains(route.getId()) + || sessionInfo.getTransferableRoutes().contains(route.getId())) { + routes.add(route); + continue; + } + // Add Phone -> Cast and Cast -> Phone + if (route.hasAnyFeatures(preferredFeatures) + && (sessionInfo.isSystemSession() ^ route.isSystemRoute())) { + routes.add(route); + } + } + } + return routes; + } + + /** * Returns the preferred features of the specified package name. */ @NonNull diff --git a/packages/SettingsLib/IllustrationPreference/src/com/android/settingslib/widget/IllustrationPreference.java b/packages/SettingsLib/IllustrationPreference/src/com/android/settingslib/widget/IllustrationPreference.java index e91dd94c715d..f04b0e338959 100644 --- a/packages/SettingsLib/IllustrationPreference/src/com/android/settingslib/widget/IllustrationPreference.java +++ b/packages/SettingsLib/IllustrationPreference/src/com/android/settingslib/widget/IllustrationPreference.java @@ -18,18 +18,29 @@ package com.android.settingslib.widget; import android.content.Context; import android.content.res.TypedArray; +import android.graphics.drawable.Animatable; +import android.graphics.drawable.Animatable2; +import android.graphics.drawable.AnimationDrawable; +import android.graphics.drawable.Drawable; +import android.net.Uri; import android.util.AttributeSet; import android.util.Log; import android.view.View; +import android.view.ViewGroup; import android.view.ViewGroup.LayoutParams; import android.widget.FrameLayout; import android.widget.ImageView; -import androidx.annotation.VisibleForTesting; +import androidx.annotation.RawRes; import androidx.preference.Preference; import androidx.preference.PreferenceViewHolder; +import androidx.vectordrawable.graphics.drawable.Animatable2Compat; import com.airbnb.lottie.LottieAnimationView; +import com.airbnb.lottie.LottieDrawable; + +import java.io.FileNotFoundException; +import java.io.InputStream; /** * IllustrationPreference is a preference that can play lottie format animation @@ -40,11 +51,32 @@ public class IllustrationPreference extends Preference { private static final boolean IS_ENABLED_LOTTIE_ADAPTIVE_COLOR = false; - private int mAnimationId; + private int mImageResId; private boolean mIsAutoScale; - private LottieAnimationView mIllustrationView; + private Uri mImageUri; + private Drawable mImageDrawable; private View mMiddleGroundView; - private FrameLayout mMiddleGroundLayout; + + private final Animatable2.AnimationCallback mAnimationCallback = + new Animatable2.AnimationCallback() { + @Override + public void onAnimationEnd(Drawable drawable) { + ((Animatable) drawable).start(); + } + }; + + private final Animatable2Compat.AnimationCallback mAnimationCallbackCompat = + new Animatable2Compat.AnimationCallback() { + @Override + public void onAnimationEnd(Drawable drawable) { + ((Animatable) drawable).start(); + } + }; + + public IllustrationPreference(Context context) { + super(context); + init(context, /* attrs= */ null); + } public IllustrationPreference(Context context, AttributeSet attrs) { super(context, attrs); @@ -65,10 +97,11 @@ public class IllustrationPreference extends Preference { @Override public void onBindViewHolder(PreferenceViewHolder holder) { super.onBindViewHolder(holder); - if (mAnimationId == 0) { - Log.w(TAG, "Invalid illustration resource id."); - return; - } + + final FrameLayout middleGroundLayout = + (FrameLayout) holder.findViewById(R.id.middleground_layout); + final LottieAnimationView illustrationView = + (LottieAnimationView) holder.findViewById(R.id.lottie_view); // To solve the problem of non-compliant illustrations, we set the frame height // to 300dp and set the length of the short side of the screen to @@ -81,73 +114,208 @@ public class IllustrationPreference extends Preference { lp.width = screenWidth < screenHeight ? screenWidth : screenHeight; illustrationFrame.setLayoutParams(lp); - mMiddleGroundLayout = (FrameLayout) holder.findViewById(R.id.middleground_layout); - mIllustrationView = (LottieAnimationView) holder.findViewById(R.id.lottie_view); - mIllustrationView.setAnimation(mAnimationId); - mIllustrationView.loop(true); - mIllustrationView.playAnimation(); + handleImageWithAnimation(illustrationView); + if (mIsAutoScale) { - enableAnimationAutoScale(mIsAutoScale); - } - if (mMiddleGroundView != null) { - enableMiddleGroundView(); + illustrationView.setScaleType(mIsAutoScale + ? ImageView.ScaleType.CENTER_CROP + : ImageView.ScaleType.CENTER_INSIDE); } + + handleMiddleGroundView(middleGroundLayout); + if (IS_ENABLED_LOTTIE_ADAPTIVE_COLOR) { - ColorUtils.applyDynamicColors(getContext(), mIllustrationView); + ColorUtils.applyDynamicColors(getContext(), illustrationView); } } - @VisibleForTesting - boolean isAnimating() { - return mIllustrationView.isAnimating(); - } - /** - * Set the middle ground view to preference. The user + * Sets the middle ground view to preference. The user * can overlay a view on top of the animation. */ public void setMiddleGroundView(View view) { - mMiddleGroundView = view; - if (mMiddleGroundLayout == null) { - return; + if (view != mMiddleGroundView) { + mMiddleGroundView = view; + notifyChanged(); } - enableMiddleGroundView(); } /** - * Remove the middle ground view of preference. + * Removes the middle ground view of preference. */ public void removeMiddleGroundView() { - if (mMiddleGroundLayout == null) { - return; - } - mMiddleGroundLayout.removeAllViews(); - mMiddleGroundLayout.setVisibility(View.GONE); + mMiddleGroundView = null; + notifyChanged(); } /** * Enables the auto scale feature of animation view. */ public void enableAnimationAutoScale(boolean enable) { - mIsAutoScale = enable; - if (mIllustrationView == null) { - return; + if (enable != mIsAutoScale) { + mIsAutoScale = enable; + notifyChanged(); } - mIllustrationView.setScaleType( - mIsAutoScale ? ImageView.ScaleType.CENTER_CROP : ImageView.ScaleType.CENTER_INSIDE); } /** - * Set the lottie illustration resource id. + * Sets the lottie illustration resource id. */ public void setLottieAnimationResId(int resId) { - mAnimationId = resId; + if (resId != mImageResId) { + resetImageResourceCache(); + mImageResId = resId; + notifyChanged(); + } + } + + /** + * Sets image drawable to display image in {@link LottieAnimationView} + * + * @param imageDrawable the drawable of an image + */ + public void setImageDrawable(Drawable imageDrawable) { + if (imageDrawable != mImageDrawable) { + resetImageResourceCache(); + mImageDrawable = imageDrawable; + notifyChanged(); + } + } + + /** + * Sets image uri to display image in {@link LottieAnimationView} + * + * @param imageUri the Uri of an image + */ + public void setImageUri(Uri imageUri) { + if (imageUri != mImageUri) { + resetImageResourceCache(); + mImageUri = imageUri; + notifyChanged(); + } + } + + private void resetImageResourceCache() { + mImageDrawable = null; + mImageUri = null; + mImageResId = 0; + } + + private void handleMiddleGroundView(ViewGroup middleGroundLayout) { + middleGroundLayout.removeAllViews(); + + if (mMiddleGroundView != null) { + middleGroundLayout.addView(mMiddleGroundView); + middleGroundLayout.setVisibility(View.VISIBLE); + } else { + middleGroundLayout.setVisibility(View.GONE); + } + } + + private void handleImageWithAnimation(LottieAnimationView illustrationView) { + if (mImageDrawable != null) { + resetAnimations(illustrationView); + illustrationView.setImageDrawable(mImageDrawable); + final Drawable drawable = illustrationView.getDrawable(); + if (drawable != null) { + startAnimation(drawable); + } + } + + if (mImageUri != null) { + resetAnimations(illustrationView); + illustrationView.setImageURI(mImageUri); + final Drawable drawable = illustrationView.getDrawable(); + if (drawable != null) { + startAnimation(drawable); + } else { + // The lottie image from the raw folder also returns null because the ImageView + // couldn't handle it now. + startLottieAnimationWith(illustrationView, mImageUri); + } + } + + if (mImageResId > 0) { + resetAnimations(illustrationView); + illustrationView.setImageResource(mImageResId); + final Drawable drawable = illustrationView.getDrawable(); + if (drawable != null) { + startAnimation(drawable); + } else { + // The lottie image from the raw folder also returns null because the ImageView + // couldn't handle it now. + startLottieAnimationWith(illustrationView, mImageResId); + } + } + } + + private void startAnimation(Drawable drawable) { + if (!(drawable instanceof Animatable)) { + return; + } + + if (drawable instanceof Animatable2) { + ((Animatable2) drawable).registerAnimationCallback(mAnimationCallback); + } else if (drawable instanceof Animatable2Compat) { + ((Animatable2Compat) drawable).registerAnimationCallback(mAnimationCallbackCompat); + } else if (drawable instanceof AnimationDrawable) { + ((AnimationDrawable) drawable).setOneShot(false); + } + + ((Animatable) drawable).start(); } - private void enableMiddleGroundView() { - mMiddleGroundLayout.removeAllViews(); - mMiddleGroundLayout.addView(mMiddleGroundView); - mMiddleGroundLayout.setVisibility(View.VISIBLE); + private static void startLottieAnimationWith(LottieAnimationView illustrationView, + Uri imageUri) { + try { + final InputStream inputStream = + getInputStreamFromUri(illustrationView.getContext(), imageUri); + illustrationView.setAnimation(inputStream, /* cacheKey= */ null); + illustrationView.setRepeatCount(LottieDrawable.INFINITE); + illustrationView.playAnimation(); + } catch (IllegalStateException e) { + Log.w(TAG, "Invalid illustration image uri: " + imageUri, e); + } + } + + private static void startLottieAnimationWith(LottieAnimationView illustrationView, + @RawRes int rawRes) { + try { + illustrationView.setAnimation(rawRes); + illustrationView.setRepeatCount(LottieDrawable.INFINITE); + illustrationView.playAnimation(); + } catch (IllegalStateException e) { + Log.w(TAG, "Invalid illustration resource id: " + rawRes, e); + } + } + + private static void resetAnimations(LottieAnimationView illustrationView) { + resetAnimation(illustrationView.getDrawable()); + + illustrationView.cancelAnimation(); + } + + private static void resetAnimation(Drawable drawable) { + if (!(drawable instanceof Animatable)) { + return; + } + + if (drawable instanceof Animatable2) { + ((Animatable2) drawable).clearAnimationCallbacks(); + } else if (drawable instanceof Animatable2Compat) { + ((Animatable2Compat) drawable).clearAnimationCallbacks(); + } + + ((Animatable) drawable).stop(); + } + + private static InputStream getInputStreamFromUri(Context context, Uri uri) { + try { + return context.getContentResolver().openInputStream(uri); + } catch (FileNotFoundException e) { + Log.w(TAG, "Cannot find content uri: " + uri, e); + return null; + } } private void init(Context context, AttributeSet attrs) { @@ -157,7 +325,7 @@ public class IllustrationPreference extends Preference { if (attrs != null) { final TypedArray a = context.obtainStyledAttributes(attrs, R.styleable.LottieAnimationView, 0 /*defStyleAttr*/, 0 /*defStyleRes*/); - mAnimationId = a.getResourceId(R.styleable.LottieAnimationView_lottie_rawRes, 0); + mImageResId = a.getResourceId(R.styleable.LottieAnimationView_lottie_rawRes, 0); a.recycle(); } } diff --git a/packages/SettingsLib/MainSwitchPreference/res/layout-v31/settingslib_main_switch_bar.xml b/packages/SettingsLib/MainSwitchPreference/res/layout-v31/settingslib_main_switch_bar.xml index 2518a6d06255..6e5911cbf0a0 100644 --- a/packages/SettingsLib/MainSwitchPreference/res/layout-v31/settingslib_main_switch_bar.xml +++ b/packages/SettingsLib/MainSwitchPreference/res/layout-v31/settingslib_main_switch_bar.xml @@ -36,9 +36,9 @@ android:layout_height="wrap_content" android:layout_width="0dp" android:layout_weight="1" - android:layout_marginEnd="16dp" + android:layout_marginEnd="@dimen/settingslib_switch_title_margin" + android:layout_marginVertical="@dimen/settingslib_switch_title_margin" android:layout_gravity="center_vertical" - android:maxLines="2" android:ellipsize="end" android:textAppearance="?android:attr/textAppearanceListItem" style="@style/MainSwitchText.Settingslib" /> diff --git a/packages/SettingsLib/MainSwitchPreference/res/values/dimens.xml b/packages/SettingsLib/MainSwitchPreference/res/values/dimens.xml index a386adbafe2a..16b8af6a2dab 100644 --- a/packages/SettingsLib/MainSwitchPreference/res/values/dimens.xml +++ b/packages/SettingsLib/MainSwitchPreference/res/values/dimens.xml @@ -41,6 +41,9 @@ <!-- Radius of switch bar --> <dimen name="settingslib_switch_bar_radius">28dp</dimen> + <!-- Size of title margin --> + <dimen name="settingslib_switch_title_margin">16dp</dimen> + <!-- SwitchBar sub settings margin start / end --> <dimen name="settingslib_switchbar_subsettings_margin_start">72dp</dimen> <dimen name="settingslib_switchbar_subsettings_margin_end">16dp</dimen> diff --git a/packages/SettingsLib/src/com/android/settingslib/media/InfoMediaManager.java b/packages/SettingsLib/src/com/android/settingslib/media/InfoMediaManager.java index 6b1e282ecdd8..79446e430d4f 100644 --- a/packages/SettingsLib/src/com/android/settingslib/media/InfoMediaManager.java +++ b/packages/SettingsLib/src/com/android/settingslib/media/InfoMediaManager.java @@ -372,7 +372,7 @@ public class InfoMediaManager extends MediaManager { Log.w(TAG, "shouldDisableMediaOutput() package name is null or empty!"); return false; } - final List<MediaRoute2Info> infos = mRouterManager.getAvailableRoutes(packageName); + final List<MediaRoute2Info> infos = mRouterManager.getTransferableRoutes(packageName); if (infos.size() == 1) { final MediaRoute2Info info = infos.get(0); final int deviceType = info.getType(); @@ -456,7 +456,7 @@ public class InfoMediaManager extends MediaManager { } private void buildAvailableRoutes() { - for (MediaRoute2Info route : mRouterManager.getAvailableRoutes(mPackageName)) { + for (MediaRoute2Info route : mRouterManager.getTransferableRoutes(mPackageName)) { if (DEBUG) { Log.d(TAG, "buildAvailableRoutes() route : " + route.getName() + ", volume : " + route.getVolume() + ", type : " + route.getType()); diff --git a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/widget/IllustrationPreferenceTest.java b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/widget/IllustrationPreferenceTest.java index 89b0fe72ca16..ea9be04527be 100644 --- a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/widget/IllustrationPreferenceTest.java +++ b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/widget/IllustrationPreferenceTest.java @@ -18,12 +18,25 @@ package com.android.settingslib.widget; import static com.google.common.truth.Truth.assertThat; +import static org.mockito.Mockito.doReturn; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.spy; +import static org.mockito.Mockito.verify; + import android.content.Context; +import android.graphics.drawable.AnimatedImageDrawable; +import android.graphics.drawable.AnimatedVectorDrawable; +import android.graphics.drawable.AnimationDrawable; +import android.net.Uri; import android.util.AttributeSet; import android.view.View; +import android.view.ViewGroup; import android.widget.FrameLayout; import android.widget.ImageView; +import androidx.preference.PreferenceViewHolder; +import androidx.test.core.app.ApplicationProvider; + import com.airbnb.lottie.LottieAnimationView; import org.junit.Before; @@ -33,47 +46,88 @@ import org.mockito.Mock; import org.mockito.MockitoAnnotations; import org.robolectric.Robolectric; import org.robolectric.RobolectricTestRunner; -import org.robolectric.RuntimeEnvironment; -import org.robolectric.util.ReflectionHelpers; @RunWith(RobolectricTestRunner.class) public class IllustrationPreferenceTest { @Mock - LottieAnimationView mAnimationView; - - private Context mContext; + private ViewGroup mRootView; + private Uri mImageUri; + private LottieAnimationView mAnimationView; private IllustrationPreference mPreference; + private PreferenceViewHolder mViewHolder; + private FrameLayout mMiddleGroundLayout; + private final Context mContext = ApplicationProvider.getApplicationContext(); @Before public void setUp() { MockitoAnnotations.initMocks(this); - mContext = RuntimeEnvironment.application; + + mImageUri = new Uri.Builder().build(); + mAnimationView = spy(new LottieAnimationView(mContext)); + mMiddleGroundLayout = new FrameLayout(mContext); + final FrameLayout illustrationFrame = new FrameLayout(mContext); + illustrationFrame.setLayoutParams( + new FrameLayout.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, + ViewGroup.LayoutParams.WRAP_CONTENT)); + doReturn(mMiddleGroundLayout).when(mRootView).findViewById(R.id.middleground_layout); + doReturn(mAnimationView).when(mRootView).findViewById(R.id.lottie_view); + doReturn(illustrationFrame).when(mRootView).findViewById(R.id.illustration_frame); + mViewHolder = spy(PreferenceViewHolder.createInstanceForTests(mRootView)); + final AttributeSet attributeSet = Robolectric.buildAttributeSet().build(); mPreference = new IllustrationPreference(mContext, attributeSet); - ReflectionHelpers.setField(mPreference, "mIllustrationView", mAnimationView); } @Test public void setMiddleGroundView_middleGroundView_shouldVisible() { final View view = new View(mContext); - final FrameLayout layout = new FrameLayout(mContext); - layout.setVisibility(View.GONE); - ReflectionHelpers.setField(mPreference, "mMiddleGroundView", view); - ReflectionHelpers.setField(mPreference, "mMiddleGroundLayout", layout); + mMiddleGroundLayout.setVisibility(View.GONE); mPreference.setMiddleGroundView(view); + mPreference.onBindViewHolder(mViewHolder); - assertThat(layout.getVisibility()).isEqualTo(View.VISIBLE); + assertThat(mMiddleGroundLayout.getVisibility()).isEqualTo(View.VISIBLE); } @Test public void enableAnimationAutoScale_shouldChangeScaleType() { - final LottieAnimationView animationView = new LottieAnimationView(mContext); - ReflectionHelpers.setField(mPreference, "mIllustrationView", animationView); - mPreference.enableAnimationAutoScale(true); + mPreference.onBindViewHolder(mViewHolder); + + assertThat(mAnimationView.getScaleType()).isEqualTo(ImageView.ScaleType.CENTER_CROP); + } + + @Test + public void playAnimationWithUri_animatedImageDrawable_success() { + final AnimatedImageDrawable drawable = mock(AnimatedImageDrawable.class); + doReturn(drawable).when(mAnimationView).getDrawable(); + + mPreference.setImageUri(mImageUri); + mPreference.onBindViewHolder(mViewHolder); + + verify(drawable).start(); + } + + @Test + public void playAnimationWithUri_animatedVectorDrawable_success() { + final AnimatedVectorDrawable drawable = mock(AnimatedVectorDrawable.class); + doReturn(drawable).when(mAnimationView).getDrawable(); + + mPreference.setImageUri(mImageUri); + mPreference.onBindViewHolder(mViewHolder); + + verify(drawable).start(); + } + + @Test + public void playAnimationWithUri_animationDrawable_success() { + final AnimationDrawable drawable = mock(AnimationDrawable.class); + doReturn(drawable).when(mAnimationView).getDrawable(); + + mPreference.setImageUri(mImageUri); + mPreference.onBindViewHolder(mViewHolder); - assertThat(animationView.getScaleType()).isEqualTo(ImageView.ScaleType.CENTER_CROP); + verify(drawable).start(); } } diff --git a/packages/SettingsProvider/src/com/android/providers/settings/SettingsProvider.java b/packages/SettingsProvider/src/com/android/providers/settings/SettingsProvider.java index dd9a6eee7327..1e3ee22fee2f 100644 --- a/packages/SettingsProvider/src/com/android/providers/settings/SettingsProvider.java +++ b/packages/SettingsProvider/src/com/android/providers/settings/SettingsProvider.java @@ -1145,6 +1145,7 @@ public class SettingsProvider extends ContentProvider { } enforceWritePermission(Manifest.permission.WRITE_DEVICE_CONFIG); + final String callingPackage = resolveCallingPackage(); synchronized (mLock) { if (isSyncDisabledConfigLocked()) { @@ -1152,7 +1153,7 @@ public class SettingsProvider extends ContentProvider { } final int key = makeKey(SETTINGS_TYPE_CONFIG, UserHandle.USER_SYSTEM); boolean success = mSettingsRegistry.setConfigSettingsLocked(key, prefix, keyValues, - resolveCallingPackage()); + callingPackage); return success ? SET_ALL_RESULT_SUCCESS : SET_ALL_RESULT_FAILURE; } } @@ -1258,6 +1259,7 @@ public class SettingsProvider extends ContentProvider { private boolean mutateConfigSetting(String name, String value, String prefix, boolean makeDefault, int operation, int mode) { enforceWritePermission(Manifest.permission.WRITE_DEVICE_CONFIG); + final String callingPackage = resolveCallingPackage(); // Perform the mutation. synchronized (mLock) { @@ -1265,7 +1267,7 @@ public class SettingsProvider extends ContentProvider { case MUTATION_OPERATION_INSERT: { return mSettingsRegistry.insertSettingLocked(SETTINGS_TYPE_CONFIG, UserHandle.USER_SYSTEM, name, value, null, makeDefault, true, - resolveCallingPackage(), false, null, + callingPackage, false, null, /* overrideableByRestore */ false); } @@ -1276,7 +1278,7 @@ public class SettingsProvider extends ContentProvider { case MUTATION_OPERATION_RESET: { mSettingsRegistry.resetSettingsLocked(SETTINGS_TYPE_CONFIG, - UserHandle.USER_SYSTEM, resolveCallingPackage(), mode, null, prefix); + UserHandle.USER_SYSTEM, callingPackage, mode, null, prefix); } return true; } } @@ -1434,13 +1436,15 @@ public class SettingsProvider extends ContentProvider { return false; } + final String callingPackage = getCallingPackage(); + // Perform the mutation. synchronized (mLock) { switch (operation) { case MUTATION_OPERATION_INSERT: { return mSettingsRegistry.insertSettingLocked(SETTINGS_TYPE_GLOBAL, UserHandle.USER_SYSTEM, name, value, tag, makeDefault, - getCallingPackage(), forceNotify, + callingPackage, forceNotify, CRITICAL_GLOBAL_SETTINGS, overrideableByRestore); } @@ -1452,12 +1456,12 @@ public class SettingsProvider extends ContentProvider { case MUTATION_OPERATION_UPDATE: { return mSettingsRegistry.updateSettingLocked(SETTINGS_TYPE_GLOBAL, UserHandle.USER_SYSTEM, name, value, tag, makeDefault, - getCallingPackage(), forceNotify, CRITICAL_GLOBAL_SETTINGS); + callingPackage, forceNotify, CRITICAL_GLOBAL_SETTINGS); } case MUTATION_OPERATION_RESET: { mSettingsRegistry.resetSettingsLocked(SETTINGS_TYPE_GLOBAL, - UserHandle.USER_SYSTEM, getCallingPackage(), mode, tag); + UserHandle.USER_SYSTEM, callingPackage, mode, tag); } return true; } } @@ -1466,11 +1470,12 @@ public class SettingsProvider extends ContentProvider { } private PackageInfo getCallingPackageInfo(int userId) { + final String callingPackage = getCallingPackage(); try { - return mPackageManager.getPackageInfo(getCallingPackage(), + return mPackageManager.getPackageInfo(callingPackage, PackageManager.GET_SIGNATURES, userId); } catch (RemoteException e) { - throw new IllegalStateException("Package " + getCallingPackage() + " doesn't exist"); + throw new IllegalStateException("Package " + callingPackage + " doesn't exist"); } } @@ -1720,13 +1725,15 @@ public class SettingsProvider extends ContentProvider { return false; } + final String callingPackage = getCallingPackage(); + // Mutate the value. synchronized (mLock) { switch (operation) { case MUTATION_OPERATION_INSERT: { return mSettingsRegistry.insertSettingLocked(SETTINGS_TYPE_SECURE, owningUserId, name, value, tag, makeDefault, - getCallingPackage(), forceNotify, CRITICAL_SECURE_SETTINGS, + callingPackage, forceNotify, CRITICAL_SECURE_SETTINGS, overrideableByRestore); } @@ -1738,12 +1745,12 @@ public class SettingsProvider extends ContentProvider { case MUTATION_OPERATION_UPDATE: { return mSettingsRegistry.updateSettingLocked(SETTINGS_TYPE_SECURE, owningUserId, name, value, tag, makeDefault, - getCallingPackage(), forceNotify, CRITICAL_SECURE_SETTINGS); + callingPackage, forceNotify, CRITICAL_SECURE_SETTINGS); } case MUTATION_OPERATION_RESET: { mSettingsRegistry.resetSettingsLocked(SETTINGS_TYPE_SECURE, - UserHandle.USER_SYSTEM, getCallingPackage(), mode, tag); + UserHandle.USER_SYSTEM, callingPackage, mode, tag); } return true; } } @@ -1840,11 +1847,12 @@ public class SettingsProvider extends ContentProvider { private boolean mutateSystemSetting(String name, String value, int runAsUserId, int operation, boolean overrideableByRestore) { + final String callingPackage = getCallingPackage(); if (!hasWriteSecureSettingsPermission()) { // If the caller doesn't hold WRITE_SECURE_SETTINGS, we verify whether this // operation is allowed for the calling package through appops. if (!Settings.checkAndNoteWriteSettingsOperation(getContext(), - Binder.getCallingUid(), getCallingPackage(), getCallingAttributionTag(), + Binder.getCallingUid(), callingPackage, getCallingAttributionTag(), true)) { return false; } @@ -1889,7 +1897,7 @@ public class SettingsProvider extends ContentProvider { case MUTATION_OPERATION_INSERT: { validateSystemSettingValue(name, value); return mSettingsRegistry.insertSettingLocked(SETTINGS_TYPE_SYSTEM, - owningUserId, name, value, null, false, getCallingPackage(), + owningUserId, name, value, null, false, callingPackage, false, null, overrideableByRestore); } @@ -1901,7 +1909,7 @@ public class SettingsProvider extends ContentProvider { case MUTATION_OPERATION_UPDATE: { validateSystemSettingValue(name, value); return mSettingsRegistry.updateSettingLocked(SETTINGS_TYPE_SYSTEM, - owningUserId, name, value, null, false, getCallingPackage(), + owningUserId, name, value, null, false, callingPackage, false, null); } } @@ -2169,14 +2177,15 @@ public class SettingsProvider extends ContentProvider { // user is a system permission and the app must be uninstalled in B and then installed as // an Instant App that situation is not realistic or supported. ApplicationInfo ai = null; + final String callingPackage = getCallingPackage(); try { - ai = mPackageManager.getApplicationInfo(getCallingPackage(), 0 + ai = mPackageManager.getApplicationInfo(callingPackage, 0 , UserHandle.getCallingUserId()); } catch (RemoteException ignored) { } if (ai == null) { throw new IllegalStateException("Failed to lookup info for package " - + getCallingPackage()); + + callingPackage); } return ai; } diff --git a/packages/Shell/AndroidManifest.xml b/packages/Shell/AndroidManifest.xml index c7b50c22b8c1..bc1d420eaa3d 100644 --- a/packages/Shell/AndroidManifest.xml +++ b/packages/Shell/AndroidManifest.xml @@ -104,7 +104,6 @@ <uses-permission android:name="android.permission.PERSISTENT_ACTIVITY" /> <uses-permission android:name="android.permission.GET_PACKAGE_SIZE" /> <uses-permission android:name="android.permission.CHANGE_NETWORK_STATE" /> - <uses-permission android:name="android.permission.SCHEDULE_EXACT_ALARM" /> <uses-permission android:name="android.permission.REQUEST_DELETE_PACKAGES" /> <uses-permission android:name="android.permission.REQUEST_OBSERVE_COMPANION_DEVICE_PRESENCE" /> <uses-permission android:name="android.permission.REQUEST_IGNORE_BATTERY_OPTIMIZATIONS" /> diff --git a/packages/SystemUI/AndroidManifest.xml b/packages/SystemUI/AndroidManifest.xml index 604310a9e905..8963ddaece35 100644 --- a/packages/SystemUI/AndroidManifest.xml +++ b/packages/SystemUI/AndroidManifest.xml @@ -281,6 +281,8 @@ <!-- Permission for Smartspace. --> <uses-permission android:name="android.permission.MANAGE_SMARTSPACE" /> + <uses-permission android:name="android.permission.READ_PEOPLE_DATA" /> + <protected-broadcast android:name="com.android.settingslib.action.REGISTER_SLICE_RECEIVER" /> <protected-broadcast android:name="com.android.settingslib.action.UNREGISTER_SLICE_RECEIVER" /> <protected-broadcast android:name="com.android.settings.flashlight.action.FLASHLIGHT_CHANGED" /> @@ -604,7 +606,8 @@ </activity> <activity android:name=".people.widget.LaunchConversationActivity" - android:windowDisablePreview="true" /> + android:windowDisablePreview="true" + android:theme="@android:style/Theme.Translucent.NoTitleBar.Fullscreen" /> <!-- People Space Widget --> <receiver @@ -630,6 +633,9 @@ android:permission="android.permission.GET_PEOPLE_TILE_PREVIEW"> </provider> + <service android:name=".people.PeopleBackupFollowUpJob" + android:permission="android.permission.BIND_JOB_SERVICE"/> + <!-- a gallery of delicious treats --> <service android:name=".DessertCaseDream" diff --git a/packages/SystemUI/res/drawable/global_actions_popup_bg.xml b/packages/SystemUI/res/drawable/global_actions_popup_bg.xml new file mode 100644 index 000000000000..fc781a065527 --- /dev/null +++ b/packages/SystemUI/res/drawable/global_actions_popup_bg.xml @@ -0,0 +1,26 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- + ~ Copyright (C) 2021 The Android Open Source Project + ~ + ~ Licensed under the Apache License, Version 2.0 (the "License"); + ~ you may not use this file except in compliance with the License. + ~ You may obtain a copy of the License at + ~ + ~ http://www.apache.org/licenses/LICENSE-2.0 + ~ + ~ Unless required by applicable law or agreed to in writing, software + ~ distributed under the License is distributed on an "AS IS" BASIS, + ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + ~ See the License for the specific language governing permissions and + ~ limitations under the License + --> +<shape xmlns:android="http://schemas.android.com/apk/res/android" + android:shape="rectangle"> + <solid android:color="?android:attr/colorBackgroundFloating" /> + <corners + android:bottomLeftRadius="?android:attr/dialogCornerRadius" + android:topLeftRadius="?android:attr/dialogCornerRadius" + android:bottomRightRadius="?android:attr/dialogCornerRadius" + android:topRightRadius="?android:attr/dialogCornerRadius" + /> +</shape> diff --git a/packages/SystemUI/res/drawable/screenshot_save_background.xml b/packages/SystemUI/res/drawable/screenshot_button_background.xml index b61b28ed1d9e..3c39fe2ecb06 100644 --- a/packages/SystemUI/res/drawable/screenshot_save_background.xml +++ b/packages/SystemUI/res/drawable/screenshot_button_background.xml @@ -14,7 +14,7 @@ ~ See the License for the specific language governing permissions and ~ limitations under the License. --> -<!-- Long screenshot save button background --> +<!-- Long screenshot save/cancel button background --> <ripple xmlns:android="http://schemas.android.com/apk/res/android" xmlns:androidprv="http://schemas.android.com/apk/prv/res/android" android:color="?android:textColorPrimary"> diff --git a/packages/SystemUI/res/layout/global_screenshot_static.xml b/packages/SystemUI/res/layout/global_screenshot_static.xml index ba46edced03c..665d4a04b00b 100644 --- a/packages/SystemUI/res/layout/global_screenshot_static.xml +++ b/packages/SystemUI/res/layout/global_screenshot_static.xml @@ -127,6 +127,7 @@ android:layout_width="wrap_content" android:layout_height="wrap_content" android:scaleType="matrix" + android:visibility="gone" app:layout_constraintStart_toStartOf="@id/global_screenshot_preview" app:layout_constraintTop_toTopOf="@id/global_screenshot_preview" android:elevation="@dimen/screenshot_preview_elevation"/> diff --git a/packages/SystemUI/res/layout/long_screenshot.xml b/packages/SystemUI/res/layout/long_screenshot.xml index 50f38b6fa67f..8a2c8f09948d 100644 --- a/packages/SystemUI/res/layout/long_screenshot.xml +++ b/packages/SystemUI/res/layout/long_screenshot.xml @@ -32,12 +32,27 @@ android:text="@string/save" android:layout_marginStart="8dp" android:layout_marginTop="4dp" - android:background="@drawable/screenshot_save_background" + android:background="@drawable/screenshot_button_background" android:textColor="?android:textColorSecondary" app:layout_constraintStart_toStartOf="parent" app:layout_constraintTop_toTopOf="parent" app:layout_constraintBottom_toTopOf="@id/preview" /> + <Button + android:id="@+id/cancel" + style="@android:style/Widget.DeviceDefault.Button.Colored" + android:layout_width="wrap_content" + android:layout_height="40dp" + android:text="@android:string/cancel" + android:layout_marginStart="6dp" + android:layout_marginTop="4dp" + android:background="@drawable/screenshot_button_background" + android:textColor="?android:textColorSecondary" + app:layout_constraintStart_toEndOf="@id/save" + app:layout_constraintTop_toTopOf="parent" + app:layout_constraintBottom_toTopOf="@id/preview" + /> + <ImageButton android:id="@+id/share" style="@android:style/Widget.Material.Button.Borderless" @@ -98,8 +113,9 @@ app:layout_constraintStart_toStartOf="parent" app:layout_constraintBottom_toBottomOf="parent" app:handleThickness="@dimen/screenshot_crop_handle_thickness" - app:handleColor="?androidprv:attr/colorAccentPrimary" - app:scrimColor="@color/screenshot_crop_scrim" + app:handleColor="?android:attr/colorAccent" + app:scrimColor="?android:colorBackgroundFloating" + app:scrimAlpha="128" app:containerBackgroundColor="?android:colorBackgroundFloating" tools:background="?android:colorBackground" tools:minHeight="100dp" @@ -114,8 +130,9 @@ app:layout_constraintTop_toTopOf="@id/preview" app:layout_constraintLeft_toLeftOf="parent" app:handleThickness="@dimen/screenshot_crop_handle_thickness" - app:handleColor="?androidprv:attr/colorAccentSecondary" - app:scrimColor="@color/screenshot_crop_scrim" + app:handleColor="?android:attr/colorAccent" + app:scrimColor="?android:colorBackgroundFloating" + app:scrimAlpha="128" app:borderThickness="4dp" app:borderColor="#fff" /> diff --git a/packages/SystemUI/res/values/attrs.xml b/packages/SystemUI/res/values/attrs.xml index d2ed6017b205..b5337d363e12 100644 --- a/packages/SystemUI/res/values/attrs.xml +++ b/packages/SystemUI/res/values/attrs.xml @@ -143,6 +143,8 @@ <attr name="handleThickness" format="dimension" /> <attr name="handleColor" format="color" /> <attr name="scrimColor" format="color" /> + <!-- Int [0,255] for the alpha to be applied to scrimColor --> + <attr name="scrimAlpha" format="integer" /> <attr name="containerBackgroundColor" format="color" /> <attr name="isVertical" format="boolean" /> @@ -179,6 +181,7 @@ <attr name="handleThickness" /> <attr name="handleColor" /> <attr name="scrimColor" /> + <attr name="scrimAlpha" /> <attr name="containerBackgroundColor" /> </declare-styleable> @@ -186,6 +189,7 @@ <attr name="handleThickness" /> <attr name="handleColor" /> <attr name="scrimColor" /> + <attr name="scrimAlpha" /> <attr name="borderThickness" format="dimension" /> <attr name="borderColor" format="color" /> </declare-styleable> diff --git a/packages/SystemUI/res/values/colors.xml b/packages/SystemUI/res/values/colors.xml index 020a100fa1b5..2260d2175268 100644 --- a/packages/SystemUI/res/values/colors.xml +++ b/packages/SystemUI/res/values/colors.xml @@ -199,9 +199,6 @@ <color name="global_screenshot_button_ripple">#1f000000</color> <color name="global_screenshot_background_protection_start">#40000000</color> <!-- 25% black --> - <!-- Long screenshot UI --> - <color name="screenshot_crop_scrim">#6444</color> - <!-- GM2 colors --> <color name="GM2_grey_50">#F8F9FA</color> <color name="GM2_grey_100">#F1F3F4</color> diff --git a/packages/SystemUI/res/values/strings.xml b/packages/SystemUI/res/values/strings.xml index bc1c67c78fef..e705803d32fd 100644 --- a/packages/SystemUI/res/values/strings.xml +++ b/packages/SystemUI/res/values/strings.xml @@ -1661,6 +1661,8 @@ <string name="wallet_action_button_label_unlock">Unlock to pay</string> <!-- Secondary label of the quick access wallet tile if no card. [CHAR LIMIT=NONE] --> <string name="wallet_secondary_label_no_card">Not set up</string> + <!-- Secondary label of the quick access wallet tile if wallet is still updating. [CHAR LIMIT=NONE] --> + <string name="wallet_secondary_label_updating">Updating</string> <!-- Secondary label of the quick access wallet tile if device locked. [CHAR LIMIT=NONE] --> <string name="wallet_secondary_label_device_locked">Unlock to use</string> <!-- Message shown when an unknown failure occurred when fetching cards. [CHAR LIMIT=NONE] --> diff --git a/packages/SystemUI/src/com/android/keyguard/AnimatableClockController.java b/packages/SystemUI/src/com/android/keyguard/AnimatableClockController.java index 08076c17dc3a..4b3af34b1df1 100644 --- a/packages/SystemUI/src/com/android/keyguard/AnimatableClockController.java +++ b/packages/SystemUI/src/com/android/keyguard/AnimatableClockController.java @@ -94,7 +94,9 @@ public class AnimatableClockController extends ViewController<AnimatableClockVie @Override public void onBatteryLevelChanged(int level, boolean pluggedIn, boolean charging) { if (mKeyguardShowing && !mIsCharging && charging) { - mView.animateCharge(mIsDozing); + mView.animateCharge(() -> { + return mStatusBarStateController.isDozing(); + }); } mIsCharging = charging; } diff --git a/packages/SystemUI/src/com/android/keyguard/AnimatableClockView.java b/packages/SystemUI/src/com/android/keyguard/AnimatableClockView.java index 63867c0f7308..58b3865facbc 100644 --- a/packages/SystemUI/src/com/android/keyguard/AnimatableClockView.java +++ b/packages/SystemUI/src/com/android/keyguard/AnimatableClockView.java @@ -196,20 +196,20 @@ public class AnimatableClockView extends TextView { null /* onAnimationEnd */); } - void animateCharge(boolean isDozing) { + void animateCharge(DozeStateGetter dozeStateGetter) { if (mTextAnimator == null || mTextAnimator.isRunning()) { // Skip charge animation if dozing animation is already playing. return; } Runnable startAnimPhase2 = () -> setTextStyle( - isDozing ? mDozingWeight : mLockScreenWeight/* weight */, + dozeStateGetter.isDozing() ? mDozingWeight : mLockScreenWeight/* weight */, -1, null, true /* animate */, CHARGE_ANIM_DURATION_PHASE_1, 0 /* delay */, null /* onAnimationEnd */); - setTextStyle(isDozing ? mLockScreenWeight : mDozingWeight/* weight */, + setTextStyle(dozeStateGetter.isDozing() ? mLockScreenWeight : mDozingWeight/* weight */, -1, null, true /* animate */, @@ -279,4 +279,8 @@ public class AnimatableClockView extends TextView { context.getResources().getConfiguration().locale); return dtpg.getBestPattern(skeleton); } + + interface DozeStateGetter { + boolean isDozing(); + } } diff --git a/packages/SystemUI/src/com/android/systemui/backup/BackupHelper.kt b/packages/SystemUI/src/com/android/systemui/backup/BackupHelper.kt index fe31a7b75c06..c9e67715decb 100644 --- a/packages/SystemUI/src/com/android/systemui/backup/BackupHelper.kt +++ b/packages/SystemUI/src/com/android/systemui/backup/BackupHelper.kt @@ -29,6 +29,7 @@ import android.os.UserHandle import android.util.Log import com.android.systemui.controls.controller.AuxiliaryPersistenceWrapper import com.android.systemui.controls.controller.ControlsFavoritePersistenceWrapper +import com.android.systemui.people.widget.PeopleBackupHelper /** * Helper for backing up elements in SystemUI @@ -45,18 +46,29 @@ class BackupHelper : BackupAgentHelper() { private const val TAG = "BackupHelper" internal const val CONTROLS = ControlsFavoritePersistenceWrapper.FILE_NAME private const val NO_OVERWRITE_FILES_BACKUP_KEY = "systemui.files_no_overwrite" + private const val PEOPLE_TILES_BACKUP_KEY = "systemui.people.shared_preferences" val controlsDataLock = Any() const val ACTION_RESTORE_FINISHED = "com.android.systemui.backup.RESTORE_FINISHED" private const val PERMISSION_SELF = "com.android.systemui.permission.SELF" } - override fun onCreate() { + override fun onCreate(userHandle: UserHandle, operationType: Int) { super.onCreate() // The map in mapOf is guaranteed to be order preserving val controlsMap = mapOf(CONTROLS to getPPControlsFile(this)) NoOverwriteFileBackupHelper(controlsDataLock, this, controlsMap).also { addHelper(NO_OVERWRITE_FILES_BACKUP_KEY, it) } + + // Conversations widgets backup only works for system user, because widgets' information is + // stored in system user's SharedPreferences files and we can't open those from other users. + if (!userHandle.isSystem) { + return + } + + val keys = PeopleBackupHelper.getFilesToBackup() + addHelper(PEOPLE_TILES_BACKUP_KEY, PeopleBackupHelper( + this, userHandle, keys.toTypedArray())) } override fun onRestoreFinished() { diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsController.java b/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsController.java index 2d04d8da0cdc..e5bd89332967 100644 --- a/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsController.java +++ b/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsController.java @@ -596,8 +596,10 @@ public class UdfpsController implements DozeReceiver { } private void updateOverlay() { + mExecution.assertIsMainThread(); + if (mServerRequest != null) { - showUdfpsOverlay(mServerRequest.mRequestReason); + showUdfpsOverlay(mServerRequest); } else { hideUdfpsOverlay(); } @@ -658,36 +660,37 @@ public class UdfpsController implements DozeReceiver { updateOverlay(); } - private void showUdfpsOverlay(int reason) { - mFgExecutor.execute(() -> { - if (mView == null) { - try { - Log.v(TAG, "showUdfpsOverlay | adding window reason=" + reason); - mView = (UdfpsView) mInflater.inflate(R.layout.udfps_view, null, false); - mView.setSensorProperties(mSensorProps); - mView.setHbmProvider(mHbmProvider); - UdfpsAnimationViewController animation = inflateUdfpsAnimation(reason); - animation.init(); - mView.setAnimationViewController(animation); - - // This view overlaps the sensor area, so prevent it from being selectable - // during a11y. - if (reason == IUdfpsOverlayController.REASON_ENROLL_FIND_SENSOR - || reason == IUdfpsOverlayController.REASON_ENROLL_ENROLLING) { - mView.setImportantForAccessibility(View.IMPORTANT_FOR_ACCESSIBILITY_NO); - } + private void showUdfpsOverlay(@NonNull ServerRequest request) { + mExecution.assertIsMainThread(); - mWindowManager.addView(mView, computeLayoutParams(animation)); - mAccessibilityManager.addTouchExplorationStateChangeListener( - mTouchExplorationStateChangeListener); - updateTouchListener(); - } catch (RuntimeException e) { - Log.e(TAG, "showUdfpsOverlay | failed to add window", e); + final int reason = request.mRequestReason; + if (mView == null) { + try { + Log.v(TAG, "showUdfpsOverlay | adding window reason=" + reason); + mView = (UdfpsView) mInflater.inflate(R.layout.udfps_view, null, false); + mView.setSensorProperties(mSensorProps); + mView.setHbmProvider(mHbmProvider); + UdfpsAnimationViewController animation = inflateUdfpsAnimation(reason); + animation.init(); + mView.setAnimationViewController(animation); + + // This view overlaps the sensor area, so prevent it from being selectable + // during a11y. + if (reason == IUdfpsOverlayController.REASON_ENROLL_FIND_SENSOR + || reason == IUdfpsOverlayController.REASON_ENROLL_ENROLLING) { + mView.setImportantForAccessibility(View.IMPORTANT_FOR_ACCESSIBILITY_NO); } - } else { - Log.v(TAG, "showUdfpsOverlay | the overlay is already showing"); + + mWindowManager.addView(mView, computeLayoutParams(animation)); + mAccessibilityManager.addTouchExplorationStateChangeListener( + mTouchExplorationStateChangeListener); + updateTouchListener(); + } catch (RuntimeException e) { + Log.e(TAG, "showUdfpsOverlay | failed to add window", e); } - }); + } else { + Log.v(TAG, "showUdfpsOverlay | the overlay is already showing"); + } } private UdfpsAnimationViewController inflateUdfpsAnimation(int reason) { diff --git a/packages/SystemUI/src/com/android/systemui/globalactions/GlobalActionsPopupMenu.java b/packages/SystemUI/src/com/android/systemui/globalactions/GlobalActionsPopupMenu.java index ac4fc62bf1c9..d1a103e3a8fa 100644 --- a/packages/SystemUI/src/com/android/systemui/globalactions/GlobalActionsPopupMenu.java +++ b/packages/SystemUI/src/com/android/systemui/globalactions/GlobalActionsPopupMenu.java @@ -22,7 +22,6 @@ import android.content.res.Resources; import android.util.LayoutDirection; import android.view.View; import android.view.View.MeasureSpec; -import android.view.WindowManager; import android.widget.AdapterView; import android.widget.ListAdapter; import android.widget.ListPopupWindow; @@ -49,11 +48,9 @@ public class GlobalActionsPopupMenu extends ListPopupWindow { mContext = context; Resources res = mContext.getResources(); setBackgroundDrawable( - res.getDrawable(R.drawable.rounded_bg_full, context.getTheme())); + res.getDrawable(R.drawable.global_actions_popup_bg, context.getTheme())); mIsDropDownMode = isDropDownMode; - // required to show above the global actions dialog - setWindowLayoutType(WindowManager.LayoutParams.TYPE_VOLUME_OVERLAY); setInputMethodMode(INPUT_METHOD_NOT_NEEDED); setModal(true); diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewMediator.java b/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewMediator.java index c84d6a87246a..b5fd73984a79 100644 --- a/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewMediator.java +++ b/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewMediator.java @@ -2161,6 +2161,15 @@ public class KeyguardViewMediator extends SystemUI implements Dumpable, if (!mHiding && !mSurfaceBehindRemoteAnimationRequested && !mKeyguardStateController.isFlingingToDismissKeyguardDuringSwipeGesture()) { + if (finishedCallback != null) { + // There will not execute animation, send a finish callback to ensure the remote + // animation won't hanging there. + try { + finishedCallback.onAnimationFinished(); + } catch (RemoteException e) { + Slog.w(TAG, "Failed to call onAnimationFinished", e); + } + } setShowingLocked(mShowing, true /* force */); return; } diff --git a/packages/SystemUI/src/com/android/systemui/people/PeopleBackupFollowUpJob.java b/packages/SystemUI/src/com/android/systemui/people/PeopleBackupFollowUpJob.java new file mode 100644 index 000000000000..452484f599a8 --- /dev/null +++ b/packages/SystemUI/src/com/android/systemui/people/PeopleBackupFollowUpJob.java @@ -0,0 +1,229 @@ +/* + * Copyright (C) 2021 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.systemui.people; + +import static com.android.systemui.people.PeopleSpaceUtils.DEBUG; +import static com.android.systemui.people.PeopleSpaceUtils.EMPTY_STRING; +import static com.android.systemui.people.PeopleSpaceUtils.removeSharedPreferencesStorageForTile; +import static com.android.systemui.people.widget.PeopleBackupHelper.isReadyForRestore; + +import android.app.job.JobInfo; +import android.app.job.JobParameters; +import android.app.job.JobScheduler; +import android.app.job.JobService; +import android.app.people.IPeopleManager; +import android.content.ComponentName; +import android.content.Context; +import android.content.SharedPreferences; +import android.content.pm.PackageManager; +import android.os.PersistableBundle; +import android.os.ServiceManager; +import android.preference.PreferenceManager; +import android.util.Log; + +import androidx.annotation.VisibleForTesting; + +import com.android.systemui.people.widget.PeopleBackupHelper; +import com.android.systemui.people.widget.PeopleTileKey; + +import java.time.Duration; +import java.util.HashMap; +import java.util.Map; +import java.util.Set; + +/** + * Follow-up job that runs after a Conversations widgets restore operation. Check if shortcuts that + * were not available before are now available. If any shortcut doesn't become available after + * 1 day, we clean up its storage. + */ +public class PeopleBackupFollowUpJob extends JobService { + private static final String TAG = "PeopleBackupFollowUpJob"; + private static final String START_DATE = "start_date"; + + /** Follow-up job id. */ + public static final int JOB_ID = 74823873; + + private static final long JOB_PERIODIC_DURATION = Duration.ofHours(6).toMillis(); + private static final long CLEAN_UP_STORAGE_AFTER_DURATION = Duration.ofHours(24).toMillis(); + + /** SharedPreferences file name for follow-up specific storage.*/ + public static final String SHARED_FOLLOW_UP = "shared_follow_up"; + + private final Object mLock = new Object(); + private Context mContext; + private PackageManager mPackageManager; + private IPeopleManager mIPeopleManager; + private JobScheduler mJobScheduler; + + /** Schedules a PeopleBackupFollowUpJob every 2 hours. */ + public static void scheduleJob(Context context) { + JobScheduler jobScheduler = context.getSystemService(JobScheduler.class); + PersistableBundle bundle = new PersistableBundle(); + bundle.putLong(START_DATE, System.currentTimeMillis()); + JobInfo jobInfo = new JobInfo + .Builder(JOB_ID, new ComponentName(context, PeopleBackupFollowUpJob.class)) + .setPeriodic(JOB_PERIODIC_DURATION) + .setExtras(bundle) + .build(); + jobScheduler.schedule(jobInfo); + } + + @Override + public void onCreate() { + super.onCreate(); + mContext = getApplicationContext(); + mPackageManager = getApplicationContext().getPackageManager(); + mIPeopleManager = IPeopleManager.Stub.asInterface( + ServiceManager.getService(Context.PEOPLE_SERVICE)); + mJobScheduler = mContext.getSystemService(JobScheduler.class); + + } + + /** Sets necessary managers for testing. */ + @VisibleForTesting + public void setManagers(Context context, PackageManager packageManager, + IPeopleManager iPeopleManager, JobScheduler jobScheduler) { + mContext = context; + mPackageManager = packageManager; + mIPeopleManager = iPeopleManager; + mJobScheduler = jobScheduler; + } + + @Override + public boolean onStartJob(JobParameters params) { + if (DEBUG) Log.d(TAG, "Starting job."); + synchronized (mLock) { + SharedPreferences sp = PreferenceManager.getDefaultSharedPreferences(this); + SharedPreferences.Editor editor = sp.edit(); + SharedPreferences followUp = this.getSharedPreferences( + SHARED_FOLLOW_UP, Context.MODE_PRIVATE); + SharedPreferences.Editor followUpEditor = followUp.edit(); + + // Remove from SHARED_FOLLOW_UP storage all widgets that are now ready to be updated. + Map<String, Set<String>> remainingWidgets = + processFollowUpFile(followUp, followUpEditor); + + // Check if all widgets were restored or if enough time elapsed to cancel the job. + long start = params.getExtras().getLong(START_DATE); + long now = System.currentTimeMillis(); + if (shouldCancelJob(remainingWidgets, start, now)) { + cancelJobAndClearRemainingWidgets(remainingWidgets, followUpEditor, sp); + } + + editor.apply(); + followUpEditor.apply(); + } + + // Ensure all widgets modified from SHARED_FOLLOW_UP storage are now updated. + PeopleBackupHelper.updateWidgets(mContext); + return false; + } + + /** + * Iterates through follow-up file entries and checks which shortcuts are now available. + * Returns a map of shortcuts that should be checked at a later time. + */ + public Map<String, Set<String>> processFollowUpFile(SharedPreferences followUp, + SharedPreferences.Editor followUpEditor) { + Map<String, Set<String>> remainingWidgets = new HashMap<>(); + Map<String, ?> all = followUp.getAll(); + for (Map.Entry<String, ?> entry : all.entrySet()) { + String key = entry.getKey(); + + PeopleTileKey peopleTileKey = PeopleTileKey.fromString(key); + boolean restored = isReadyForRestore(mIPeopleManager, mPackageManager, peopleTileKey); + if (restored) { + if (DEBUG) Log.d(TAG, "Removing key from follow-up: " + key); + followUpEditor.remove(key); + continue; + } + + if (DEBUG) Log.d(TAG, "Key should not be restored yet, try later: " + key); + try { + remainingWidgets.put(entry.getKey(), (Set<String>) entry.getValue()); + } catch (Exception e) { + Log.e(TAG, "Malformed entry value: " + entry.getValue()); + } + } + return remainingWidgets; + } + + /** Returns whether all shortcuts were restored or if enough time elapsed to cancel the job. */ + public boolean shouldCancelJob(Map<String, Set<String>> remainingWidgets, + long start, long now) { + if (remainingWidgets.isEmpty()) { + if (DEBUG) Log.d(TAG, "All widget storage was successfully restored."); + return true; + } + + boolean oneDayHasPassed = (now - start) > CLEAN_UP_STORAGE_AFTER_DURATION; + if (oneDayHasPassed) { + if (DEBUG) { + Log.w(TAG, "One or more widgets were not properly restored, " + + "but cancelling job because it has been a day."); + } + return true; + } + if (DEBUG) Log.d(TAG, "There are still non-restored widgets, run job again."); + return false; + } + + /** Cancels job and removes storage of any shortcut that was not restored. */ + public void cancelJobAndClearRemainingWidgets(Map<String, Set<String>> remainingWidgets, + SharedPreferences.Editor followUpEditor, SharedPreferences sp) { + if (DEBUG) Log.d(TAG, "Cancelling follow up job."); + removeUnavailableShortcutsFromSharedStorage(remainingWidgets, sp); + followUpEditor.clear(); + mJobScheduler.cancel(JOB_ID); + } + + private void removeUnavailableShortcutsFromSharedStorage(Map<String, + Set<String>> remainingWidgets, SharedPreferences sp) { + for (Map.Entry<String, Set<String>> entry : remainingWidgets.entrySet()) { + PeopleTileKey peopleTileKey = PeopleTileKey.fromString(entry.getKey()); + if (!PeopleTileKey.isValid(peopleTileKey)) { + Log.e(TAG, "Malformed peopleTileKey in follow-up file: " + entry.getKey()); + continue; + } + Set<String> widgetIds; + try { + widgetIds = (Set<String>) entry.getValue(); + } catch (Exception e) { + Log.e(TAG, "Malformed widget ids in follow-up file: " + e); + continue; + } + for (String id : widgetIds) { + int widgetId; + try { + widgetId = Integer.parseInt(id); + } catch (NumberFormatException ex) { + Log.e(TAG, "Malformed widget id in follow-up file: " + ex); + continue; + } + + String contactUriString = sp.getString(String.valueOf(widgetId), EMPTY_STRING); + removeSharedPreferencesStorageForTile( + mContext, peopleTileKey, widgetId, contactUriString); + } + } + } + + @Override + public boolean onStopJob(JobParameters params) { + return false; + } +} diff --git a/packages/SystemUI/src/com/android/systemui/people/PeopleSpaceUtils.java b/packages/SystemUI/src/com/android/systemui/people/PeopleSpaceUtils.java index 917a060f1f1d..dcab86bac406 100644 --- a/packages/SystemUI/src/com/android/systemui/people/PeopleSpaceUtils.java +++ b/packages/SystemUI/src/com/android/systemui/people/PeopleSpaceUtils.java @@ -25,6 +25,7 @@ import static com.android.systemui.people.NotificationHelper.shouldMatchNotifica import android.annotation.Nullable; import android.app.Notification; +import android.app.backup.BackupManager; import android.app.people.ConversationChannel; import android.app.people.IPeopleManager; import android.app.people.PeopleSpaceTile; @@ -89,7 +90,7 @@ public class PeopleSpaceUtils { /** Returns stored widgets for the conversation specified. */ public static Set<String> getStoredWidgetIds(SharedPreferences sp, PeopleTileKey key) { - if (!key.isValid()) { + if (!PeopleTileKey.isValid(key)) { return new HashSet<>(); } return new HashSet<>(sp.getStringSet(key.toString(), new HashSet<>())); @@ -97,19 +98,16 @@ public class PeopleSpaceUtils { /** Sets all relevant storage for {@code appWidgetId} association to {@code tile}. */ public static void setSharedPreferencesStorageForTile(Context context, PeopleTileKey key, - int appWidgetId, Uri contactUri) { - if (!key.isValid()) { + int appWidgetId, Uri contactUri, BackupManager backupManager) { + if (!PeopleTileKey.isValid(key)) { Log.e(TAG, "Not storing for invalid key"); return; } // Write relevant persisted storage. SharedPreferences widgetSp = context.getSharedPreferences(String.valueOf(appWidgetId), Context.MODE_PRIVATE); - SharedPreferences.Editor widgetEditor = widgetSp.edit(); - widgetEditor.putString(PeopleSpaceUtils.PACKAGE_NAME, key.getPackageName()); - widgetEditor.putString(PeopleSpaceUtils.SHORTCUT_ID, key.getShortcutId()); - widgetEditor.putInt(PeopleSpaceUtils.USER_ID, key.getUserId()); - widgetEditor.apply(); + SharedPreferencesHelper.setPeopleTileKey(widgetSp, key); + SharedPreferences sp = PreferenceManager.getDefaultSharedPreferences(context); SharedPreferences.Editor editor = sp.edit(); String contactUriString = contactUri == null ? EMPTY_STRING : contactUri.toString(); @@ -117,14 +115,18 @@ public class PeopleSpaceUtils { // Don't overwrite existing widgets with the same key. addAppWidgetIdForKey(sp, editor, appWidgetId, key.toString()); - addAppWidgetIdForKey(sp, editor, appWidgetId, contactUriString); + if (!TextUtils.isEmpty(contactUriString)) { + addAppWidgetIdForKey(sp, editor, appWidgetId, contactUriString); + } editor.apply(); + backupManager.dataChanged(); } /** Removes stored data when tile is deleted. */ public static void removeSharedPreferencesStorageForTile(Context context, PeopleTileKey key, int widgetId, String contactUriString) { // Delete widgetId mapping to key. + if (DEBUG) Log.d(TAG, "Removing widget info from sharedPrefs"); SharedPreferences sp = PreferenceManager.getDefaultSharedPreferences(context); SharedPreferences.Editor editor = sp.edit(); editor.remove(String.valueOf(widgetId)); @@ -230,7 +232,7 @@ public class PeopleSpaceUtils { */ public static PeopleSpaceTile augmentTileFromNotification(Context context, PeopleSpaceTile tile, PeopleTileKey key, NotificationEntry notificationEntry, int messagesCount, - Optional<Integer> appWidgetId) { + Optional<Integer> appWidgetId, BackupManager backupManager) { if (notificationEntry == null || notificationEntry.getSbn().getNotification() == null) { if (DEBUG) Log.d(TAG, "Tile key: " + key.toString() + ". Notification is null"); return removeNotificationFields(tile); @@ -246,7 +248,7 @@ public class PeopleSpaceUtils { Uri contactUri = Uri.parse(uriFromNotification); // Update storage. setSharedPreferencesStorageForTile(context, new PeopleTileKey(tile), appWidgetId.get(), - contactUri); + contactUri, backupManager); // Update cached tile in-memory. updatedTile.setContactUri(contactUri); } diff --git a/packages/SystemUI/src/com/android/systemui/people/PeopleTileViewHelper.java b/packages/SystemUI/src/com/android/systemui/people/PeopleTileViewHelper.java index 844a8c6b6583..d363614830f9 100644 --- a/packages/SystemUI/src/com/android/systemui/people/PeopleTileViewHelper.java +++ b/packages/SystemUI/src/com/android/systemui/people/PeopleTileViewHelper.java @@ -330,11 +330,8 @@ public class PeopleTileViewHelper { R.layout.people_tile_suppressed_layout); } Drawable appIcon = mContext.getDrawable(R.drawable.ic_conversation_icon); - Bitmap appIconAsBitmap = convertDrawableToBitmap(appIcon); - FastBitmapDrawable drawable = new FastBitmapDrawable(appIconAsBitmap); - drawable.setIsDisabled(true); - Bitmap convertedBitmap = convertDrawableToBitmap(drawable); - views.setImageViewBitmap(R.id.icon, convertedBitmap); + Bitmap disabledBitmap = convertDrawableToDisabledBitmap(appIcon); + views.setImageViewBitmap(R.id.icon, disabledBitmap); return views; } @@ -504,6 +501,11 @@ public class PeopleTileViewHelper { } private RemoteViews setLaunchIntents(RemoteViews views) { + if (!PeopleTileKey.isValid(mKey) || mTile == null) { + if (DEBUG) Log.d(TAG, "Skipping launch intent, Null tile or invalid key: " + mKey); + return views; + } + try { Intent activityIntent = new Intent(mContext, LaunchConversationActivity.class); activityIntent.addFlags( @@ -706,7 +708,8 @@ public class PeopleTileViewHelper { views.setViewVisibility(R.id.predefined_icon, View.VISIBLE); views.setTextViewText(R.id.text_content, statusText); - if (status.getActivity() == ACTIVITY_BIRTHDAY) { + if (status.getActivity() == ACTIVITY_BIRTHDAY + || status.getActivity() == ACTIVITY_UPCOMING_BIRTHDAY) { setEmojiBackground(views, EMOJI_CAKE); } @@ -1067,7 +1070,8 @@ public class PeopleTileViewHelper { Icon icon = tile.getUserIcon(); if (icon == null) { - return null; + Drawable placeholder = context.getDrawable(R.drawable.ic_avatar_with_badge); + return convertDrawableToDisabledBitmap(placeholder); } PeopleStoryIconFactory storyIcon = new PeopleStoryIconFactory(context, context.getPackageManager(), @@ -1179,4 +1183,11 @@ public class PeopleTileViewHelper { mAvatarSize = avatarSize; } } + + private static Bitmap convertDrawableToDisabledBitmap(Drawable icon) { + Bitmap appIconAsBitmap = convertDrawableToBitmap(icon); + FastBitmapDrawable drawable = new FastBitmapDrawable(appIconAsBitmap); + drawable.setIsDisabled(true); + return convertDrawableToBitmap(drawable); + } } diff --git a/packages/SystemUI/src/com/android/systemui/people/SharedPreferencesHelper.java b/packages/SystemUI/src/com/android/systemui/people/SharedPreferencesHelper.java new file mode 100644 index 000000000000..aef08fb421d8 --- /dev/null +++ b/packages/SystemUI/src/com/android/systemui/people/SharedPreferencesHelper.java @@ -0,0 +1,59 @@ +/* + * Copyright (C) 2021 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.systemui.people; + +import static com.android.systemui.people.PeopleSpaceUtils.INVALID_USER_ID; +import static com.android.systemui.people.PeopleSpaceUtils.PACKAGE_NAME; +import static com.android.systemui.people.PeopleSpaceUtils.SHORTCUT_ID; +import static com.android.systemui.people.PeopleSpaceUtils.USER_ID; + +import android.content.SharedPreferences; + +import com.android.systemui.people.widget.PeopleTileKey; + +/** Helper class for Conversations widgets SharedPreferences storage. */ +public class SharedPreferencesHelper { + /** Clears all storage from {@code sp}. */ + public static void clear(SharedPreferences sp) { + SharedPreferences.Editor editor = sp.edit(); + editor.clear(); + editor.apply(); + } + + /** Sets {@code sp}'s storage to identify a {@link PeopleTileKey}. */ + public static void setPeopleTileKey(SharedPreferences sp, PeopleTileKey key) { + setPeopleTileKey(sp, key.getShortcutId(), key.getUserId(), key.getPackageName()); + } + + /** Sets {@code sp}'s storage to identify a {@link PeopleTileKey}. */ + public static void setPeopleTileKey(SharedPreferences sp, String shortcutId, int userId, + String packageName) { + SharedPreferences.Editor editor = sp.edit(); + editor.putString(SHORTCUT_ID, shortcutId); + editor.putInt(USER_ID, userId); + editor.putString(PACKAGE_NAME, packageName); + editor.apply(); + } + + /** Returns a {@link PeopleTileKey} based on storage from {@code sp}. */ + public static PeopleTileKey getPeopleTileKey(SharedPreferences sp) { + String shortcutId = sp.getString(SHORTCUT_ID, null); + String packageName = sp.getString(PACKAGE_NAME, null); + int userId = sp.getInt(USER_ID, INVALID_USER_ID); + return new PeopleTileKey(shortcutId, userId, packageName); + } +} diff --git a/packages/SystemUI/src/com/android/systemui/people/widget/LaunchConversationActivity.java b/packages/SystemUI/src/com/android/systemui/people/widget/LaunchConversationActivity.java index b031637e4016..79318d69837d 100644 --- a/packages/SystemUI/src/com/android/systemui/people/widget/LaunchConversationActivity.java +++ b/packages/SystemUI/src/com/android/systemui/people/widget/LaunchConversationActivity.java @@ -152,7 +152,7 @@ public class LaunchConversationActivity extends Activity { launcherApps.startShortcut( packageName, tileId, null, null, userHandle); } catch (Exception e) { - Log.e(TAG, "Exception:" + e); + Log.e(TAG, "Exception launching shortcut:" + e); } } else { if (DEBUG) Log.d(TAG, "Trying to launch conversation with null shortcutInfo."); diff --git a/packages/SystemUI/src/com/android/systemui/people/widget/PeopleBackupHelper.java b/packages/SystemUI/src/com/android/systemui/people/widget/PeopleBackupHelper.java new file mode 100644 index 000000000000..d8c96dd182b4 --- /dev/null +++ b/packages/SystemUI/src/com/android/systemui/people/widget/PeopleBackupHelper.java @@ -0,0 +1,508 @@ +/* + * Copyright (C) 2021 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.systemui.people.widget; + +import static com.android.systemui.people.PeopleBackupFollowUpJob.SHARED_FOLLOW_UP; +import static com.android.systemui.people.PeopleSpaceUtils.DEBUG; +import static com.android.systemui.people.PeopleSpaceUtils.INVALID_USER_ID; +import static com.android.systemui.people.PeopleSpaceUtils.USER_ID; + +import android.app.backup.BackupDataInputStream; +import android.app.backup.BackupDataOutput; +import android.app.backup.SharedPreferencesBackupHelper; +import android.app.people.IPeopleManager; +import android.appwidget.AppWidgetManager; +import android.content.ComponentName; +import android.content.ContentProvider; +import android.content.Context; +import android.content.Intent; +import android.content.SharedPreferences; +import android.content.pm.PackageInfo; +import android.content.pm.PackageManager; +import android.net.Uri; +import android.os.ParcelFileDescriptor; +import android.os.ServiceManager; +import android.os.UserHandle; +import android.preference.PreferenceManager; +import android.text.TextUtils; +import android.util.Log; + +import com.android.internal.annotations.VisibleForTesting; +import com.android.systemui.people.PeopleBackupFollowUpJob; +import com.android.systemui.people.SharedPreferencesHelper; + +import java.util.ArrayList; +import java.util.Collections; +import java.util.List; +import java.util.Map; +import java.util.Set; +import java.util.stream.Collectors; + +/** + * Helper class to backup and restore Conversations widgets storage. + * It is used by SystemUI's BackupHelper agent. + * TODO(b/192334798): Lock access to storage using PeopleSpaceWidgetManager's lock. + */ +public class PeopleBackupHelper extends SharedPreferencesBackupHelper { + private static final String TAG = "PeopleBackupHelper"; + + public static final String ADD_USER_ID_TO_URI = "add_user_id_to_uri_"; + public static final String SHARED_BACKUP = "shared_backup"; + + private final Context mContext; + private final UserHandle mUserHandle; + private final PackageManager mPackageManager; + private final IPeopleManager mIPeopleManager; + private final AppWidgetManager mAppWidgetManager; + + /** + * Types of entries stored in the default SharedPreferences file for Conversation widgets. + * Widget ID corresponds to a pair [widgetId, contactURI]. + * PeopleTileKey corresponds to a pair [PeopleTileKey, {widgetIds}]. + * Contact URI corresponds to a pair [Contact URI, {widgetIds}]. + */ + enum SharedFileEntryType { + UNKNOWN, + WIDGET_ID, + PEOPLE_TILE_KEY, + CONTACT_URI + } + + /** + * Returns the file names that should be backed up and restored by SharedPreferencesBackupHelper + * infrastructure. + */ + public static List<String> getFilesToBackup() { + return Collections.singletonList(SHARED_BACKUP); + } + + public PeopleBackupHelper(Context context, UserHandle userHandle, + String[] sharedPreferencesKey) { + super(context, sharedPreferencesKey); + mContext = context; + mUserHandle = userHandle; + mPackageManager = context.getPackageManager(); + mIPeopleManager = IPeopleManager.Stub.asInterface( + ServiceManager.getService(Context.PEOPLE_SERVICE)); + mAppWidgetManager = AppWidgetManager.getInstance(context); + } + + @VisibleForTesting + public PeopleBackupHelper(Context context, UserHandle userHandle, + String[] sharedPreferencesKey, PackageManager packageManager, + IPeopleManager peopleManager) { + super(context, sharedPreferencesKey); + mContext = context; + mUserHandle = userHandle; + mPackageManager = packageManager; + mIPeopleManager = peopleManager; + mAppWidgetManager = AppWidgetManager.getInstance(context); + } + + /** + * Reads values from default storage, backs them up appropriately to a specified backup file, + * and calls super's performBackup, which backs up the values of the backup file. + */ + @Override + public void performBackup(ParcelFileDescriptor oldState, BackupDataOutput data, + ParcelFileDescriptor newState) { + if (DEBUG) Log.d(TAG, "Backing up conversation widgets, writing to: " + SHARED_BACKUP); + // Open default value for readings values. + SharedPreferences sp = PreferenceManager.getDefaultSharedPreferences(mContext); + if (sp.getAll().isEmpty()) { + if (DEBUG) Log.d(TAG, "No information to be backed up, finishing."); + return; + } + + // Open backup file for writing. + SharedPreferences backupSp = mContext.getSharedPreferences( + SHARED_BACKUP, Context.MODE_PRIVATE); + SharedPreferences.Editor backupEditor = backupSp.edit(); + backupEditor.clear(); + + // Fetch Conversations widgets corresponding to this user. + List<String> existingWidgets = getExistingWidgetsForUser(mUserHandle.getIdentifier()); + if (existingWidgets.isEmpty()) { + if (DEBUG) Log.d(TAG, "No existing Conversations widgets, returning."); + return; + } + + // Writes each entry to backup file. + sp.getAll().entrySet().forEach(entry -> backupKey(entry, backupEditor, existingWidgets)); + backupEditor.apply(); + + super.performBackup(oldState, data, newState); + } + + /** + * Restores backed up values to backup file via super's restoreEntity, then transfers them + * back to regular storage. Restore operations for each users are done in sequence, so we can + * safely use the same backup file names. + */ + @Override + public void restoreEntity(BackupDataInputStream data) { + if (DEBUG) Log.d(TAG, "Restoring Conversation widgets."); + super.restoreEntity(data); + + // Open backup file for reading values. + SharedPreferences backupSp = mContext.getSharedPreferences( + SHARED_BACKUP, Context.MODE_PRIVATE); + + // Open default file and follow-up file for writing. + SharedPreferences sp = PreferenceManager.getDefaultSharedPreferences(mContext); + SharedPreferences.Editor editor = sp.edit(); + SharedPreferences followUp = mContext.getSharedPreferences( + SHARED_FOLLOW_UP, Context.MODE_PRIVATE); + SharedPreferences.Editor followUpEditor = followUp.edit(); + + // Writes each entry back to default value. + boolean shouldScheduleJob = false; + for (Map.Entry<String, ?> entry : backupSp.getAll().entrySet()) { + boolean restored = restoreKey(entry, editor, followUpEditor, backupSp); + if (!restored) { + shouldScheduleJob = true; + } + } + + editor.apply(); + followUpEditor.apply(); + SharedPreferencesHelper.clear(backupSp); + + // If any of the widgets is not yet available, schedule a follow-up job to check later. + if (shouldScheduleJob) { + if (DEBUG) Log.d(TAG, "At least one shortcut is not available, scheduling follow-up."); + PeopleBackupFollowUpJob.scheduleJob(mContext); + } + + updateWidgets(mContext); + } + + /** Backs up an entry from default file to backup file. */ + public void backupKey(Map.Entry<String, ?> entry, SharedPreferences.Editor backupEditor, + List<String> existingWidgets) { + String key = entry.getKey(); + if (TextUtils.isEmpty(key)) { + return; + } + + SharedFileEntryType entryType = getEntryType(entry); + switch(entryType) { + case WIDGET_ID: + backupWidgetIdKey(key, String.valueOf(entry.getValue()), backupEditor, + existingWidgets); + break; + case PEOPLE_TILE_KEY: + backupPeopleTileKey(key, (Set<String>) entry.getValue(), backupEditor, + existingWidgets); + break; + case CONTACT_URI: + backupContactUriKey(key, (Set<String>) entry.getValue(), backupEditor); + break; + case UNKNOWN: + default: + Log.w(TAG, "Key not identified, skipping: " + key); + } + } + + /** + * Tries to restore an entry from backup file to default file. + * Returns true if restore is finished, false if it needs to be checked later. + */ + boolean restoreKey(Map.Entry<String, ?> entry, SharedPreferences.Editor editor, + SharedPreferences.Editor followUpEditor, SharedPreferences backupSp) { + String key = entry.getKey(); + SharedFileEntryType keyType = getEntryType(entry); + int storedUserId = backupSp.getInt(ADD_USER_ID_TO_URI + key, INVALID_USER_ID); + switch (keyType) { + case WIDGET_ID: + restoreWidgetIdKey(key, String.valueOf(entry.getValue()), editor, storedUserId); + return true; + case PEOPLE_TILE_KEY: + return restorePeopleTileKeyAndCorrespondingWidgetFile( + key, (Set<String>) entry.getValue(), editor, followUpEditor); + case CONTACT_URI: + restoreContactUriKey(key, (Set<String>) entry.getValue(), editor, storedUserId); + return true; + case UNKNOWN: + default: + Log.e(TAG, "Key not identified, skipping:" + key); + return true; + } + } + + /** + * Backs up a [widgetId, contactURI] pair, if widget id corresponds to current user. + * If contact URI has a user id, stores it so it can be re-added on restore. + */ + private void backupWidgetIdKey(String key, String uriString, SharedPreferences.Editor editor, + List<String> existingWidgets) { + if (!existingWidgets.contains(key)) { + if (DEBUG) Log.d(TAG, "Widget: " + key + " does't correspond to this user, skipping."); + return; + } + Uri uri = Uri.parse(uriString); + if (ContentProvider.uriHasUserId(uri)) { + if (DEBUG) Log.d(TAG, "Contact URI value has user ID, removing from: " + uri); + int userId = ContentProvider.getUserIdFromUri(uri); + editor.putInt(ADD_USER_ID_TO_URI + key, userId); + uri = ContentProvider.getUriWithoutUserId(uri); + } + if (DEBUG) Log.d(TAG, "Backing up widgetId key: " + key + " . Value: " + uri.toString()); + editor.putString(key, uri.toString()); + } + + /** Restores a [widgetId, contactURI] pair, and a potential {@code storedUserId}. */ + private void restoreWidgetIdKey(String key, String uriString, SharedPreferences.Editor editor, + int storedUserId) { + Uri uri = Uri.parse(uriString); + if (storedUserId != INVALID_USER_ID) { + uri = ContentProvider.createContentUriForUser(uri, UserHandle.of(storedUserId)); + if (DEBUG) Log.d(TAG, "UserId was removed from URI on back up, re-adding as:" + uri); + + } + if (DEBUG) Log.d(TAG, "Restoring widgetId key: " + key + " . Value: " + uri.toString()); + editor.putString(key, uri.toString()); + } + + /** + * Backs up a [PeopleTileKey, {widgetIds}] pair, if PeopleTileKey's user is the same as current + * user, stripping out the user id. + */ + private void backupPeopleTileKey(String key, Set<String> widgetIds, + SharedPreferences.Editor editor, List<String> existingWidgets) { + PeopleTileKey peopleTileKey = PeopleTileKey.fromString(key); + if (peopleTileKey.getUserId() != mUserHandle.getIdentifier()) { + if (DEBUG) Log.d(TAG, "PeopleTileKey corresponds to different user, skipping backup."); + return; + } + + Set<String> filteredWidgets = widgetIds.stream() + .filter(id -> existingWidgets.contains(id)) + .collect(Collectors.toSet()); + if (filteredWidgets.isEmpty()) { + return; + } + + peopleTileKey.setUserId(INVALID_USER_ID); + if (DEBUG) { + Log.d(TAG, "Backing up PeopleTileKey key: " + peopleTileKey.toString() + ". Value: " + + filteredWidgets); + } + editor.putStringSet(peopleTileKey.toString(), filteredWidgets); + } + + /** + * Restores a [PeopleTileKey, {widgetIds}] pair, restoring the user id. Checks if the + * corresponding shortcut exists, and if not, we should schedule a follow up to check later. + * Also restores corresponding [widgetId, PeopleTileKey], which is not backed up since the + * information can be inferred from this. + * Returns true if restore is finished, false if we should check if shortcut is available later. + */ + private boolean restorePeopleTileKeyAndCorrespondingWidgetFile(String key, + Set<String> widgetIds, SharedPreferences.Editor editor, + SharedPreferences.Editor followUpEditor) { + PeopleTileKey peopleTileKey = PeopleTileKey.fromString(key); + // Should never happen, as type of key has been checked. + if (peopleTileKey == null) { + if (DEBUG) Log.d(TAG, "PeopleTileKey key to be restored is null, skipping."); + return true; + } + + peopleTileKey.setUserId(mUserHandle.getIdentifier()); + if (!PeopleTileKey.isValid(peopleTileKey)) { + if (DEBUG) Log.d(TAG, "PeopleTileKey key to be restored is not valid, skipping."); + return true; + } + + boolean restored = isReadyForRestore( + mIPeopleManager, mPackageManager, peopleTileKey); + if (!restored) { + if (DEBUG) Log.d(TAG, "Adding key to follow-up storage: " + peopleTileKey.toString()); + // Follow-up file stores shortcuts that need to be checked later, and possibly wiped + // from our storage. + followUpEditor.putStringSet(peopleTileKey.toString(), widgetIds); + } + + if (DEBUG) { + Log.d(TAG, "Restoring PeopleTileKey key: " + peopleTileKey.toString() + " . Value: " + + widgetIds); + } + editor.putStringSet(peopleTileKey.toString(), widgetIds); + restoreWidgetIdFiles(mContext, widgetIds, peopleTileKey); + return restored; + } + + /** + * Backs up a [contactURI, {widgetIds}] pair. If contactURI contains a userId, we back up + * this entry in the corresponding user. If it doesn't, we back it up as user 0. + * If contact URI has a user id, stores it so it can be re-added on restore. + * We do not take existing widgets for this user into consideration. + */ + private void backupContactUriKey(String key, Set<String> widgetIds, + SharedPreferences.Editor editor) { + Uri uri = Uri.parse(String.valueOf(key)); + if (ContentProvider.uriHasUserId(uri)) { + int userId = ContentProvider.getUserIdFromUri(uri); + if (DEBUG) Log.d(TAG, "Contact URI has user Id: " + userId); + if (userId == mUserHandle.getIdentifier()) { + uri = ContentProvider.getUriWithoutUserId(uri); + if (DEBUG) { + Log.d(TAG, "Backing up contactURI key: " + uri.toString() + " . Value: " + + widgetIds); + } + editor.putInt(ADD_USER_ID_TO_URI + uri.toString(), userId); + editor.putStringSet(uri.toString(), widgetIds); + } else { + if (DEBUG) Log.d(TAG, "ContactURI corresponds to different user, skipping."); + } + } else if (mUserHandle.isSystem()) { + if (DEBUG) { + Log.d(TAG, "Backing up contactURI key: " + uri.toString() + " . Value: " + + widgetIds); + } + editor.putStringSet(uri.toString(), widgetIds); + } + } + + /** Restores a [contactURI, {widgetIds}] pair, and a potential {@code storedUserId}. */ + private void restoreContactUriKey(String key, Set<String> widgetIds, + SharedPreferences.Editor editor, int storedUserId) { + Uri uri = Uri.parse(key); + if (storedUserId != INVALID_USER_ID) { + uri = ContentProvider.createContentUriForUser(uri, UserHandle.of(storedUserId)); + if (DEBUG) Log.d(TAG, "UserId was removed from URI on back up, re-adding as:" + uri); + } + if (DEBUG) { + Log.d(TAG, "Restoring contactURI key: " + uri.toString() + " . Value: " + widgetIds); + } + editor.putStringSet(uri.toString(), widgetIds); + } + + /** Restores the widget-specific files that contain PeopleTileKey information. */ + public static void restoreWidgetIdFiles(Context context, Set<String> widgetIds, + PeopleTileKey key) { + for (String id : widgetIds) { + if (DEBUG) Log.d(TAG, "Restoring widget Id file: " + id + " . Value: " + key); + SharedPreferences dest = context.getSharedPreferences(id, Context.MODE_PRIVATE); + SharedPreferencesHelper.setPeopleTileKey(dest, key); + } + } + + private List<String> getExistingWidgetsForUser(int userId) { + List<String> existingWidgets = new ArrayList<>(); + int[] ids = mAppWidgetManager.getAppWidgetIds( + new ComponentName(mContext, PeopleSpaceWidgetProvider.class)); + for (int id : ids) { + String idString = String.valueOf(id); + SharedPreferences sp = mContext.getSharedPreferences(idString, Context.MODE_PRIVATE); + if (sp.getInt(USER_ID, INVALID_USER_ID) == userId) { + existingWidgets.add(idString); + } + } + if (DEBUG) Log.d(TAG, "Existing widgets: " + existingWidgets); + return existingWidgets; + } + + /** + * Returns whether {@code key} corresponds to a shortcut that is ready for restore, either + * because it is available or because it never will be. If not ready, we schedule a job to check + * again later. + */ + public static boolean isReadyForRestore(IPeopleManager peopleManager, + PackageManager packageManager, PeopleTileKey key) { + if (DEBUG) Log.d(TAG, "Checking if we should schedule a follow up job : " + key); + if (!PeopleTileKey.isValid(key)) { + if (DEBUG) Log.d(TAG, "Key is invalid, should not follow up."); + return true; + } + + try { + PackageInfo info = packageManager.getPackageInfoAsUser( + key.getPackageName(), 0, key.getUserId()); + } catch (PackageManager.NameNotFoundException e) { + if (DEBUG) Log.d(TAG, "Package is not installed, should follow up."); + return false; + } + + try { + boolean isConversation = peopleManager.isConversation( + key.getPackageName(), key.getUserId(), key.getShortcutId()); + if (DEBUG) { + Log.d(TAG, "Checked if shortcut exists, should follow up: " + !isConversation); + } + return isConversation; + } catch (Exception e) { + if (DEBUG) Log.d(TAG, "Error checking if backed up info is a shortcut."); + return false; + } + } + + /** Parses default file {@code entry} to determine the entry's type.*/ + public static SharedFileEntryType getEntryType(Map.Entry<String, ?> entry) { + String key = entry.getKey(); + if (key == null) { + return SharedFileEntryType.UNKNOWN; + } + + try { + int id = Integer.parseInt(key); + try { + String contactUri = (String) entry.getValue(); + } catch (Exception e) { + Log.w(TAG, "Malformed value, skipping:" + entry.getValue()); + return SharedFileEntryType.UNKNOWN; + } + return SharedFileEntryType.WIDGET_ID; + } catch (NumberFormatException ignored) { } + + try { + Set<String> widgetIds = (Set<String>) entry.getValue(); + } catch (Exception e) { + Log.w(TAG, "Malformed value, skipping:" + entry.getValue()); + return SharedFileEntryType.UNKNOWN; + } + + PeopleTileKey peopleTileKey = PeopleTileKey.fromString(key); + if (peopleTileKey != null) { + return SharedFileEntryType.PEOPLE_TILE_KEY; + } + + try { + Uri uri = Uri.parse(key); + return SharedFileEntryType.CONTACT_URI; + } catch (Exception e) { + return SharedFileEntryType.UNKNOWN; + } + } + + /** Sends a broadcast to update the existing Conversation widgets. */ + public static void updateWidgets(Context context) { + int[] widgetIds = AppWidgetManager.getInstance(context) + .getAppWidgetIds(new ComponentName(context, PeopleSpaceWidgetProvider.class)); + if (DEBUG) { + for (int id : widgetIds) { + Log.d(TAG, "Calling update to widget: " + id); + } + } + if (widgetIds != null && widgetIds.length != 0) { + Intent intent = new Intent(context, PeopleSpaceWidgetProvider.class); + intent.setAction(AppWidgetManager.ACTION_APPWIDGET_UPDATE); + intent.putExtra(AppWidgetManager.EXTRA_APPWIDGET_IDS, widgetIds); + context.sendBroadcast(intent); + } + } +} diff --git a/packages/SystemUI/src/com/android/systemui/people/widget/PeopleSpaceWidgetManager.java b/packages/SystemUI/src/com/android/systemui/people/widget/PeopleSpaceWidgetManager.java index 62a0df270698..72cddd0b4b3f 100644 --- a/packages/SystemUI/src/com/android/systemui/people/widget/PeopleSpaceWidgetManager.java +++ b/packages/SystemUI/src/com/android/systemui/people/widget/PeopleSpaceWidgetManager.java @@ -22,6 +22,7 @@ import static android.app.NotificationManager.INTERRUPTION_FILTER_ALL; import static android.app.NotificationManager.INTERRUPTION_FILTER_NONE; import static android.app.NotificationManager.INTERRUPTION_FILTER_PRIORITY; import static android.content.Intent.ACTION_BOOT_COMPLETED; +import static android.content.Intent.ACTION_PACKAGE_ADDED; import static android.content.Intent.ACTION_PACKAGE_REMOVED; import static android.service.notification.ZenPolicy.CONVERSATION_SENDERS_ANYONE; @@ -29,6 +30,7 @@ import static com.android.systemui.people.NotificationHelper.getContactUri; import static com.android.systemui.people.NotificationHelper.getHighestPriorityNotification; import static com.android.systemui.people.NotificationHelper.shouldFilterOut; import static com.android.systemui.people.NotificationHelper.shouldMatchNotificationByUri; +import static com.android.systemui.people.PeopleBackupFollowUpJob.SHARED_FOLLOW_UP; import static com.android.systemui.people.PeopleSpaceUtils.EMPTY_STRING; import static com.android.systemui.people.PeopleSpaceUtils.INVALID_USER_ID; import static com.android.systemui.people.PeopleSpaceUtils.PACKAGE_NAME; @@ -38,6 +40,7 @@ import static com.android.systemui.people.PeopleSpaceUtils.augmentTileFromNotifi import static com.android.systemui.people.PeopleSpaceUtils.getMessagesCount; import static com.android.systemui.people.PeopleSpaceUtils.getNotificationsByUri; import static com.android.systemui.people.PeopleSpaceUtils.removeNotificationFields; +import static com.android.systemui.people.widget.PeopleBackupHelper.getEntryType; import android.annotation.NonNull; import android.annotation.Nullable; @@ -46,6 +49,8 @@ import android.app.NotificationChannel; import android.app.NotificationManager; import android.app.PendingIntent; import android.app.Person; +import android.app.backup.BackupManager; +import android.app.job.JobScheduler; import android.app.people.ConversationChannel; import android.app.people.IPeopleManager; import android.app.people.PeopleManager; @@ -84,8 +89,10 @@ import com.android.systemui.broadcast.BroadcastDispatcher; import com.android.systemui.dagger.SysUISingleton; import com.android.systemui.dagger.qualifiers.Background; import com.android.systemui.people.NotificationHelper; +import com.android.systemui.people.PeopleBackupFollowUpJob; import com.android.systemui.people.PeopleSpaceUtils; import com.android.systemui.people.PeopleTileViewHelper; +import com.android.systemui.people.SharedPreferencesHelper; import com.android.systemui.statusbar.NotificationListener; import com.android.systemui.statusbar.NotificationListener.NotificationHandler; import com.android.systemui.statusbar.notification.NotificationEntryManager; @@ -93,6 +100,7 @@ import com.android.systemui.statusbar.notification.collection.NotificationEntry; import com.android.wm.shell.bubbles.Bubbles; import java.util.ArrayList; +import java.util.Arrays; import java.util.Collections; import java.util.HashMap; import java.util.HashSet; @@ -126,6 +134,7 @@ public class PeopleSpaceWidgetManager { private Optional<Bubbles> mBubblesOptional; private UserManager mUserManager; private PeopleSpaceWidgetManager mManager; + private BackupManager mBackupManager; public UiEventLogger mUiEventLogger = new UiEventLoggerImpl(); private NotificationManager mNotificationManager; private BroadcastDispatcher mBroadcastDispatcher; @@ -164,6 +173,7 @@ public class PeopleSpaceWidgetManager { ServiceManager.getService(Context.NOTIFICATION_SERVICE)); mBubblesOptional = bubblesOptional; mUserManager = userManager; + mBackupManager = new BackupManager(context); mNotificationManager = notificationManager; mManager = this; mBroadcastDispatcher = broadcastDispatcher; @@ -189,6 +199,7 @@ public class PeopleSpaceWidgetManager { null /* executor */, UserHandle.ALL); IntentFilter perAppFilter = new IntentFilter(ACTION_PACKAGE_REMOVED); + perAppFilter.addAction(ACTION_PACKAGE_ADDED); perAppFilter.addDataScheme("package"); // BroadcastDispatcher doesn't allow data schemes. mContext.registerReceiver(mBaseBroadcastReceiver, perAppFilter); @@ -224,7 +235,7 @@ public class PeopleSpaceWidgetManager { AppWidgetManager appWidgetManager, IPeopleManager iPeopleManager, PeopleManager peopleManager, LauncherApps launcherApps, NotificationEntryManager notificationEntryManager, PackageManager packageManager, - Optional<Bubbles> bubblesOptional, UserManager userManager, + Optional<Bubbles> bubblesOptional, UserManager userManager, BackupManager backupManager, INotificationManager iNotificationManager, NotificationManager notificationManager, @Background Executor executor) { mContext = context; @@ -236,6 +247,7 @@ public class PeopleSpaceWidgetManager { mPackageManager = packageManager; mBubblesOptional = bubblesOptional; mUserManager = userManager; + mBackupManager = backupManager; mINotificationManager = iNotificationManager; mNotificationManager = notificationManager; mManager = this; @@ -257,8 +269,6 @@ public class PeopleSpaceWidgetManager { if (DEBUG) Log.d(TAG, "no widgets to update"); return; } - - if (DEBUG) Log.d(TAG, "updating " + widgetIds.length + " widgets: " + widgetIds); synchronized (mLock) { updateSingleConversationWidgets(widgetIds); } @@ -274,6 +284,7 @@ public class PeopleSpaceWidgetManager { public void updateSingleConversationWidgets(int[] appWidgetIds) { Map<Integer, PeopleSpaceTile> widgetIdToTile = new HashMap<>(); for (int appWidgetId : appWidgetIds) { + if (DEBUG) Log.d(TAG, "Updating widget: " + appWidgetId); PeopleSpaceTile tile = getTileForExistingWidget(appWidgetId); if (tile == null) { Log.e(TAG, "Matching conversation not found for shortcut ID"); @@ -293,7 +304,8 @@ public class PeopleSpaceWidgetManager { private void updateAppWidgetViews(int appWidgetId, PeopleSpaceTile tile, Bundle options) { PeopleTileKey key = getKeyFromStorageByWidgetId(appWidgetId); if (DEBUG) Log.d(TAG, "Widget: " + appWidgetId + " for: " + key.toString()); - if (!key.isValid()) { + + if (!PeopleTileKey.isValid(key)) { Log.e(TAG, "Cannot update invalid widget"); return; } @@ -301,6 +313,7 @@ public class PeopleSpaceWidgetManager { options, key); // Tell the AppWidgetManager to perform an update on the current app widget. + if (DEBUG) Log.d(TAG, "Calling update widget for widgetId: " + appWidgetId); mAppWidgetManager.updateAppWidget(appWidgetId, views); } @@ -314,6 +327,9 @@ public class PeopleSpaceWidgetManager { /** Updates tile in app widget options and the current view. */ public void updateAppWidgetOptionsAndView(int appWidgetId, PeopleSpaceTile tile) { + if (tile == null) { + if (DEBUG) Log.w(TAG, "Storing null tile"); + } synchronized (mTiles) { mTiles.put(appWidgetId, tile); } @@ -368,7 +384,7 @@ public class PeopleSpaceWidgetManager { @Nullable public PeopleSpaceTile getTileFromPersistentStorage(PeopleTileKey key, int appWidgetId) throws PackageManager.NameNotFoundException { - if (!key.isValid()) { + if (!PeopleTileKey.isValid(key)) { Log.e(TAG, "PeopleTileKey invalid: " + key.toString()); return null; } @@ -430,7 +446,8 @@ public class PeopleSpaceWidgetManager { try { PeopleTileKey key = new PeopleTileKey( sbn.getShortcutId(), sbn.getUser().getIdentifier(), sbn.getPackageName()); - if (!key.isValid()) { + if (!PeopleTileKey.isValid(key)) { + Log.d(TAG, "Sbn doesn't contain valid PeopleTileKey: " + key.toString()); return; } int[] widgetIds = mAppWidgetManager.getAppWidgetIds( @@ -561,7 +578,7 @@ public class PeopleSpaceWidgetManager { if (DEBUG) Log.d(TAG, "Augmenting tile from notification, key: " + key.toString()); return augmentTileFromNotification(mContext, tile, key, highestPriority, messagesCount, - appWidgetId); + appWidgetId, mBackupManager); } /** Returns an augmented tile for an existing widget. */ @@ -588,7 +605,7 @@ public class PeopleSpaceWidgetManager { /** Returns stored widgets for the conversation specified. */ public Set<String> getMatchingKeyWidgetIds(PeopleTileKey key) { - if (!key.isValid()) { + if (!PeopleTileKey.isValid(key)) { return new HashSet<>(); } return new HashSet<>(mSharedPrefs.getStringSet(key.toString(), new HashSet<>())); @@ -776,7 +793,7 @@ public class PeopleSpaceWidgetManager { // PeopleTileKey arguments. if (DEBUG) Log.d(TAG, "onAppWidgetOptionsChanged called for widget: " + appWidgetId); PeopleTileKey optionsKey = AppWidgetOptionsHelper.getPeopleTileKeyFromBundle(newOptions); - if (optionsKey.isValid()) { + if (PeopleTileKey.isValid(optionsKey)) { if (DEBUG) { Log.d(TAG, "PeopleTileKey was present in Options, shortcutId: " + optionsKey.getShortcutId()); @@ -808,7 +825,7 @@ public class PeopleSpaceWidgetManager { existingKeyIfStored = getKeyFromStorageByWidgetId(appWidgetId); } // Delete previous storage if the widget already existed and is just reconfigured. - if (existingKeyIfStored.isValid()) { + if (PeopleTileKey.isValid(existingKeyIfStored)) { if (DEBUG) Log.d(TAG, "Remove previous storage for widget: " + appWidgetId); deleteWidgets(new int[]{appWidgetId}); } else { @@ -820,7 +837,7 @@ public class PeopleSpaceWidgetManager { synchronized (mLock) { if (DEBUG) Log.d(TAG, "Add storage for : " + key.toString()); PeopleSpaceUtils.setSharedPreferencesStorageForTile(mContext, key, appWidgetId, - tile.getContactUri()); + tile.getContactUri(), mBackupManager); } if (DEBUG) Log.d(TAG, "Ensure listener is registered for widget: " + appWidgetId); registerConversationListenerIfNeeded(appWidgetId, key); @@ -838,7 +855,7 @@ public class PeopleSpaceWidgetManager { /** Registers a conversation listener for {@code appWidgetId} if not already registered. */ public void registerConversationListenerIfNeeded(int widgetId, PeopleTileKey key) { // Retrieve storage needed for registration. - if (!key.isValid()) { + if (!PeopleTileKey.isValid(key)) { if (DEBUG) Log.w(TAG, "Could not register listener for widget: " + widgetId); return; } @@ -887,7 +904,7 @@ public class PeopleSpaceWidgetManager { widgetSp.getString(SHORTCUT_ID, null), widgetSp.getInt(USER_ID, INVALID_USER_ID), widgetSp.getString(PACKAGE_NAME, null)); - if (!key.isValid()) { + if (!PeopleTileKey.isValid(key)) { if (DEBUG) Log.e(TAG, "Could not delete " + widgetId); return; } @@ -1053,6 +1070,7 @@ public class PeopleSpaceWidgetManager { return; } for (int appWidgetId : appWidgetIds) { + if (DEBUG) Log.d(TAG, "Updating widget from broadcast, widget id: " + appWidgetId); PeopleSpaceTile existingTile = null; PeopleSpaceTile updatedTile = null; try { @@ -1060,7 +1078,7 @@ public class PeopleSpaceWidgetManager { existingTile = getTileForExistingWidgetThrowing(appWidgetId); if (existingTile == null) { Log.e(TAG, "Matching conversation not found for shortcut ID"); - return; + continue; } updatedTile = getTileWithCurrentState(existingTile, entryPoint); updateAppWidgetOptionsAndView(appWidgetId, updatedTile); @@ -1068,6 +1086,14 @@ public class PeopleSpaceWidgetManager { } catch (PackageManager.NameNotFoundException e) { // Delete data for uninstalled widgets. Log.e(TAG, "Package no longer found for tile: " + e); + JobScheduler jobScheduler = mContext.getSystemService(JobScheduler.class); + if (jobScheduler != null + && jobScheduler.getPendingJob(PeopleBackupFollowUpJob.JOB_ID) != null) { + if (DEBUG) { + Log.d(TAG, "Device was recently restored, wait before deleting storage."); + } + continue; + } synchronized (mLock) { updateAppWidgetOptionsAndView(appWidgetId, updatedTile); } @@ -1185,4 +1211,139 @@ public class PeopleSpaceWidgetManager { return PeopleSpaceTile.BLOCK_CONVERSATIONS; } } + + /** + * Modifies widgets storage after a restore operation, since widget ids get remapped on restore. + * This is guaranteed to run after the PeopleBackupHelper restore operation. + */ + public void remapWidgets(int[] oldWidgetIds, int[] newWidgetIds) { + if (DEBUG) { + Log.d(TAG, "Remapping widgets, old: " + Arrays.toString(oldWidgetIds) + ". new: " + + Arrays.toString(newWidgetIds)); + } + + Map<String, String> widgets = new HashMap<>(); + for (int i = 0; i < oldWidgetIds.length; i++) { + widgets.put(String.valueOf(oldWidgetIds[i]), String.valueOf(newWidgetIds[i])); + } + + remapWidgetFiles(widgets); + remapSharedFile(widgets); + remapFollowupFile(widgets); + + int[] widgetIds = mAppWidgetManager.getAppWidgetIds( + new ComponentName(mContext, PeopleSpaceWidgetProvider.class)); + Bundle b = new Bundle(); + b.putBoolean(AppWidgetManager.OPTION_APPWIDGET_RESTORE_COMPLETED, true); + for (int id : widgetIds) { + if (DEBUG) Log.d(TAG, "Setting widget as restored, widget id:" + id); + mAppWidgetManager.updateAppWidgetOptions(id, b); + } + + updateWidgets(widgetIds); + } + + /** Remaps widget ids in widget specific files. */ + public void remapWidgetFiles(Map<String, String> widgets) { + if (DEBUG) Log.d(TAG, "Remapping widget files"); + for (Map.Entry<String, String> entry : widgets.entrySet()) { + String from = String.valueOf(entry.getKey()); + String to = String.valueOf(entry.getValue()); + + SharedPreferences src = mContext.getSharedPreferences(from, Context.MODE_PRIVATE); + PeopleTileKey key = SharedPreferencesHelper.getPeopleTileKey(src); + if (PeopleTileKey.isValid(key)) { + if (DEBUG) { + Log.d(TAG, "Moving PeopleTileKey: " + key.toString() + " from file: " + + from + ", to file: " + to); + } + SharedPreferences dest = mContext.getSharedPreferences(to, Context.MODE_PRIVATE); + SharedPreferencesHelper.setPeopleTileKey(dest, key); + SharedPreferencesHelper.clear(src); + } + } + } + + /** Remaps widget ids in default shared storage. */ + public void remapSharedFile(Map<String, String> widgets) { + if (DEBUG) Log.d(TAG, "Remapping shared file"); + SharedPreferences sp = PreferenceManager.getDefaultSharedPreferences(mContext); + SharedPreferences.Editor editor = sp.edit(); + Map<String, ?> all = sp.getAll(); + for (Map.Entry<String, ?> entry : all.entrySet()) { + String key = entry.getKey(); + PeopleBackupHelper.SharedFileEntryType keyType = getEntryType(entry); + if (DEBUG) Log.d(TAG, "Remapping key:" + key); + switch (keyType) { + case WIDGET_ID: + String newId = widgets.get(key); + if (TextUtils.isEmpty(newId)) { + Log.w(TAG, "Key is widget id without matching new id, skipping: " + key); + break; + } + if (DEBUG) Log.d(TAG, "Key is widget id: " + key + ", replace with: " + newId); + try { + editor.putString(newId, (String) entry.getValue()); + } catch (Exception e) { + Log.e(TAG, "Malformed entry value: " + entry.getValue()); + } + editor.remove(key); + break; + case PEOPLE_TILE_KEY: + case CONTACT_URI: + Set<String> oldWidgetIds; + try { + oldWidgetIds = (Set<String>) entry.getValue(); + } catch (Exception e) { + Log.e(TAG, "Malformed entry value: " + entry.getValue()); + editor.remove(key); + break; + } + Set<String> newWidgets = getNewWidgets(oldWidgetIds, widgets); + if (DEBUG) { + Log.d(TAG, "Key is PeopleTileKey or contact URI: " + key + + ", replace values with new ids: " + newWidgets); + } + editor.putStringSet(key, newWidgets); + break; + case UNKNOWN: + Log.e(TAG, "Key not identified:" + key); + } + } + editor.apply(); + } + + /** Remaps widget ids in follow-up job file. */ + public void remapFollowupFile(Map<String, String> widgets) { + if (DEBUG) Log.d(TAG, "Remapping follow up file"); + SharedPreferences followUp = mContext.getSharedPreferences( + SHARED_FOLLOW_UP, Context.MODE_PRIVATE); + SharedPreferences.Editor followUpEditor = followUp.edit(); + Map<String, ?> followUpAll = followUp.getAll(); + for (Map.Entry<String, ?> entry : followUpAll.entrySet()) { + String key = entry.getKey(); + Set<String> oldWidgetIds; + try { + oldWidgetIds = (Set<String>) entry.getValue(); + } catch (Exception e) { + Log.e(TAG, "Malformed entry value: " + entry.getValue()); + followUpEditor.remove(key); + continue; + } + Set<String> newWidgets = getNewWidgets(oldWidgetIds, widgets); + if (DEBUG) { + Log.d(TAG, "Follow up key: " + key + ", replace with new ids: " + newWidgets); + } + followUpEditor.putStringSet(key, newWidgets); + } + followUpEditor.apply(); + } + + private Set<String> getNewWidgets(Set<String> oldWidgets, Map<String, String> widgetsMapping) { + return oldWidgets + .stream() + .map(widgetsMapping::get) + .filter(id -> !TextUtils.isEmpty(id)) + .collect(Collectors.toSet()); + } } diff --git a/packages/SystemUI/src/com/android/systemui/people/widget/PeopleSpaceWidgetPinnedReceiver.java b/packages/SystemUI/src/com/android/systemui/people/widget/PeopleSpaceWidgetPinnedReceiver.java index a28da43a80b6..c4be197504be 100644 --- a/packages/SystemUI/src/com/android/systemui/people/widget/PeopleSpaceWidgetPinnedReceiver.java +++ b/packages/SystemUI/src/com/android/systemui/people/widget/PeopleSpaceWidgetPinnedReceiver.java @@ -75,7 +75,7 @@ public class PeopleSpaceWidgetPinnedReceiver extends BroadcastReceiver { String packageName = intent.getStringExtra(Intent.EXTRA_PACKAGE_NAME); int userId = intent.getIntExtra(Intent.EXTRA_USER_ID, INVALID_USER_ID); PeopleTileKey key = new PeopleTileKey(shortcutId, userId, packageName); - if (!key.isValid()) { + if (!PeopleTileKey.isValid(key)) { if (DEBUG) Log.w(TAG, "Skipping: key is not valid: " + key.toString()); return; } diff --git a/packages/SystemUI/src/com/android/systemui/people/widget/PeopleSpaceWidgetProvider.java b/packages/SystemUI/src/com/android/systemui/people/widget/PeopleSpaceWidgetProvider.java index 3522b76e6460..36939b735a07 100644 --- a/packages/SystemUI/src/com/android/systemui/people/widget/PeopleSpaceWidgetProvider.java +++ b/packages/SystemUI/src/com/android/systemui/people/widget/PeopleSpaceWidgetProvider.java @@ -70,6 +70,13 @@ public class PeopleSpaceWidgetProvider extends AppWidgetProvider { mPeopleSpaceWidgetManager.deleteWidgets(appWidgetIds); } + @Override + public void onRestored(Context context, int[] oldWidgetIds, int[] newWidgetIds) { + super.onRestored(context, oldWidgetIds, newWidgetIds); + ensurePeopleSpaceWidgetManagerInitialized(); + mPeopleSpaceWidgetManager.remapWidgets(oldWidgetIds, newWidgetIds); + } + private void ensurePeopleSpaceWidgetManagerInitialized() { mPeopleSpaceWidgetManager.init(); } diff --git a/packages/SystemUI/src/com/android/systemui/people/widget/PeopleTileKey.java b/packages/SystemUI/src/com/android/systemui/people/widget/PeopleTileKey.java index 319df85b4872..6e6ca254dee0 100644 --- a/packages/SystemUI/src/com/android/systemui/people/widget/PeopleTileKey.java +++ b/packages/SystemUI/src/com/android/systemui/people/widget/PeopleTileKey.java @@ -25,6 +25,8 @@ import android.text.TextUtils; import com.android.systemui.statusbar.notification.collection.NotificationEntry; import java.util.Objects; +import java.util.regex.Matcher; +import java.util.regex.Pattern; /** Class that encapsulates fields identifying a Conversation. */ public class PeopleTileKey { @@ -32,6 +34,8 @@ public class PeopleTileKey { private int mUserId; private String mPackageName; + private static final Pattern KEY_PATTERN = Pattern.compile("(.+)/(-?\\d+)/(\\p{L}.*)"); + public PeopleTileKey(String shortcutId, int userId, String packageName) { mShortcutId = shortcutId; mUserId = userId; @@ -66,8 +70,12 @@ public class PeopleTileKey { return mPackageName; } + public void setUserId(int userId) { + mUserId = userId; + } + /** Returns whether PeopleTileKey is valid/well-formed. */ - public boolean isValid() { + private boolean validate() { return !TextUtils.isEmpty(mShortcutId) && !TextUtils.isEmpty(mPackageName) && mUserId >= 0; } @@ -88,10 +96,31 @@ public class PeopleTileKey { */ @Override public String toString() { - if (!isValid()) return EMPTY_STRING; return mShortcutId + "/" + mUserId + "/" + mPackageName; } + /** Parses {@code key} into a {@link PeopleTileKey}. */ + public static PeopleTileKey fromString(String key) { + if (key == null) { + return null; + } + Matcher m = KEY_PATTERN.matcher(key); + if (m.find()) { + try { + int userId = Integer.parseInt(m.group(2)); + return new PeopleTileKey(m.group(1), userId, m.group(3)); + } catch (NumberFormatException e) { + return null; + } + } + return null; + } + + /** Returns whether {@code key} is a valid {@link PeopleTileKey}. */ + public static boolean isValid(PeopleTileKey key) { + return key != null && key.validate(); + } + @Override public boolean equals(Object other) { if (this == other) { diff --git a/packages/SystemUI/src/com/android/systemui/power/InattentiveSleepWarningView.java b/packages/SystemUI/src/com/android/systemui/power/InattentiveSleepWarningView.java index 1ed98c0a8f90..03d1f15bf379 100644 --- a/packages/SystemUI/src/com/android/systemui/power/InattentiveSleepWarningView.java +++ b/packages/SystemUI/src/com/android/systemui/power/InattentiveSleepWarningView.java @@ -93,6 +93,8 @@ public class InattentiveSleepWarningView extends FrameLayout { setAlpha(1f); setVisibility(View.VISIBLE); mWindowManager.addView(this, getLayoutParams(mWindowToken)); + announceForAccessibility( + getContext().getString(R.string.inattentive_sleep_warning_message)); } /** diff --git a/packages/SystemUI/src/com/android/systemui/qs/QSAnimator.java b/packages/SystemUI/src/com/android/systemui/qs/QSAnimator.java index 14bf8ab78e2c..3f7a0e2bb3b9 100644 --- a/packages/SystemUI/src/com/android/systemui/qs/QSAnimator.java +++ b/packages/SystemUI/src/com/android/systemui/qs/QSAnimator.java @@ -81,6 +81,7 @@ public class QSAnimator implements Callback, PageListener, Listener, OnLayoutCha private QSExpansionPathInterpolator mQSExpansionPathInterpolator; private TouchAnimator mFirstPageAnimator; private TouchAnimator mFirstPageDelayedAnimator; + private TouchAnimator mTranslationXAnimator; private TouchAnimator mTranslationYAnimator; private TouchAnimator mNonfirstPageAnimator; private TouchAnimator mNonfirstPageDelayedAnimator; @@ -223,18 +224,25 @@ public class QSAnimator implements Callback, PageListener, Listener, OnLayoutCha View qqsView, View qsView, View commonParent, + int xOffset, int yOffset, int[] temp, - TouchAnimator.Builder animatorBuilder + TouchAnimator.Builder animatorBuilderX, + TouchAnimator.Builder animatorBuilderY ) { getRelativePosition(temp, qqsView, commonParent); - int qqsPos = temp[1]; + int qqsPosX = temp[0]; + int qqsPosY = temp[1]; getRelativePosition(temp, qsView, commonParent); - int qsPos = temp[1]; - - int diff = qsPos - qqsPos - yOffset; - animatorBuilder.addFloat(qqsView, "translationY", 0, diff); - animatorBuilder.addFloat(qsView, "translationY", -diff, 0); + int qsPosX = temp[0]; + int qsPosY = temp[1]; + + int xDiff = qsPosX - qqsPosX - xOffset; + animatorBuilderX.addFloat(qqsView, "translationX", 0, xDiff); + animatorBuilderX.addFloat(qsView, "translationX", -xDiff, 0); + int yDiff = qsPosY - qqsPosY - yOffset; + animatorBuilderY.addFloat(qqsView, "translationY", 0, yDiff); + animatorBuilderY.addFloat(qsView, "translationY", -yDiff, 0); mAllViews.add(qqsView); mAllViews.add(qsView); } @@ -243,6 +251,7 @@ public class QSAnimator implements Callback, PageListener, Listener, OnLayoutCha mNeedsAnimatorUpdate = false; TouchAnimator.Builder firstPageBuilder = new Builder(); TouchAnimator.Builder translationYBuilder = new Builder(); + TouchAnimator.Builder translationXBuilder = new Builder(); Collection<QSTile> tiles = mHost.getTiles(); int count = 0; @@ -289,6 +298,7 @@ public class QSAnimator implements Callback, PageListener, Listener, OnLayoutCha getRelativePosition(loc1, quickTileView, view); getRelativePosition(loc2, tileView, view); int yOffset = loc2[1] - loc1[1]; + int xOffset = loc2[0] - loc1[0]; // Offset the translation animation on the views // (that goes from 0 to getOffsetTranslation) @@ -299,6 +309,9 @@ public class QSAnimator implements Callback, PageListener, Listener, OnLayoutCha translationYBuilder.addFloat(tileView, "translationY", -offsetWithQSBHTranslation, 0); + translationXBuilder.addFloat(quickTileView, "translationX", 0, xOffset); + translationXBuilder.addFloat(tileView, "translationX", -xOffset, 0); + if (mQQSTileHeightAnimator == null) { mQQSTileHeightAnimator = new HeightExpansionAnimator(this, quickTileView.getHeight(), tileView.getHeight()); @@ -312,8 +325,10 @@ public class QSAnimator implements Callback, PageListener, Listener, OnLayoutCha quickTileView.getIcon(), tileView.getIcon(), view, + xOffset, yOffset, loc1, + translationXBuilder, translationYBuilder ); @@ -322,8 +337,10 @@ public class QSAnimator implements Callback, PageListener, Listener, OnLayoutCha quickTileView.getLabelContainer(), tileView.getLabelContainer(), view, + xOffset, yOffset, loc1, + translationXBuilder, translationYBuilder ); @@ -332,8 +349,10 @@ public class QSAnimator implements Callback, PageListener, Listener, OnLayoutCha quickTileView.getSecondaryIcon(), tileView.getSecondaryIcon(), view, + xOffset, yOffset, loc1, + translationXBuilder, translationYBuilder ); @@ -401,7 +420,9 @@ public class QSAnimator implements Callback, PageListener, Listener, OnLayoutCha mAllPagesDelayedAnimator = builder.build(); mAllViews.add(mSecurityFooter.getView()); translationYBuilder.setInterpolator(mQSExpansionPathInterpolator.getYInterpolator()); + translationXBuilder.setInterpolator(mQSExpansionPathInterpolator.getXInterpolator()); mTranslationYAnimator = translationYBuilder.build(); + mTranslationXAnimator = translationXBuilder.build(); if (mQQSTileHeightAnimator != null) { mQQSTileHeightAnimator.setInterpolator( mQSExpansionPathInterpolator.getYInterpolator()); @@ -474,6 +495,7 @@ public class QSAnimator implements Callback, PageListener, Listener, OnLayoutCha mFirstPageAnimator.setPosition(position); mFirstPageDelayedAnimator.setPosition(position); mTranslationYAnimator.setPosition(position); + mTranslationXAnimator.setPosition(position); if (mQQSTileHeightAnimator != null) { mQQSTileHeightAnimator.setPosition(position); } diff --git a/packages/SystemUI/src/com/android/systemui/qs/QSPanel.java b/packages/SystemUI/src/com/android/systemui/qs/QSPanel.java index db8efd530195..7c7f56658919 100644 --- a/packages/SystemUI/src/com/android/systemui/qs/QSPanel.java +++ b/packages/SystemUI/src/com/android/systemui/qs/QSPanel.java @@ -98,11 +98,7 @@ public class QSPanel extends LinearLayout implements Tunable { private LinearLayout mHorizontalLinearLayout; protected LinearLayout mHorizontalContentContainer; - // Only used with media - private QSTileLayout mHorizontalTileLayout; - protected QSTileLayout mRegularTileLayout; protected QSTileLayout mTileLayout; - private int mLastOrientation = -1; private int mMediaTotalBottomMargin; public QSPanel(Context context, AttributeSet attrs) { @@ -119,8 +115,7 @@ public class QSPanel extends LinearLayout implements Tunable { } void initialize() { - mRegularTileLayout = createRegularTileLayout(); - mTileLayout = mRegularTileLayout; + mTileLayout = getOrCreateTileLayout(); if (mUsingMediaPlayer) { mHorizontalLinearLayout = new RemeasuringLinearLayout(mContext); @@ -133,7 +128,6 @@ public class QSPanel extends LinearLayout implements Tunable { mHorizontalContentContainer.setClipChildren(true); mHorizontalContentContainer.setClipToPadding(false); - mHorizontalTileLayout = createHorizontalTileLayout(); LayoutParams lp = new LayoutParams(0, LayoutParams.WRAP_CONTENT, 1); int marginSize = (int) mContext.getResources().getDimension(R.dimen.qs_media_padding); lp.setMarginStart(0); @@ -176,17 +170,12 @@ public class QSPanel extends LinearLayout implements Tunable { } /** */ - public QSTileLayout createRegularTileLayout() { - if (mRegularTileLayout == null) { - mRegularTileLayout = (QSTileLayout) LayoutInflater.from(mContext) + public QSTileLayout getOrCreateTileLayout() { + if (mTileLayout == null) { + mTileLayout = (QSTileLayout) LayoutInflater.from(mContext) .inflate(R.layout.qs_paged_tile_layout, this, false); } - return mRegularTileLayout; - } - - - protected QSTileLayout createHorizontalTileLayout() { - return createRegularTileLayout(); + return mTileLayout; } @Override @@ -273,18 +262,18 @@ public class QSPanel extends LinearLayout implements Tunable { * @param pageIndicator indicator to use for page scrolling */ public void setFooterPageIndicator(PageIndicator pageIndicator) { - if (mRegularTileLayout instanceof PagedTileLayout) { + if (mTileLayout instanceof PagedTileLayout) { mFooterPageIndicator = pageIndicator; updatePageIndicator(); } } private void updatePageIndicator() { - if (mRegularTileLayout instanceof PagedTileLayout) { + if (mTileLayout instanceof PagedTileLayout) { if (mFooterPageIndicator != null) { mFooterPageIndicator.setVisibility(View.GONE); - ((PagedTileLayout) mRegularTileLayout).setPageIndicator(mFooterPageIndicator); + ((PagedTileLayout) mTileLayout).setPageIndicator(mFooterPageIndicator); } } } @@ -354,7 +343,7 @@ public class QSPanel extends LinearLayout implements Tunable { return true; } - protected boolean needsDynamicRowsAndColumns() { + private boolean needsDynamicRowsAndColumns() { return true; } @@ -669,39 +658,20 @@ public class QSPanel extends LinearLayout implements Tunable { } protected void setPageMargin(int pageMargin) { - if (mRegularTileLayout instanceof PagedTileLayout) { - ((PagedTileLayout) mRegularTileLayout).setPageMargin(pageMargin); - } - if (mHorizontalTileLayout != mRegularTileLayout - && mHorizontalTileLayout instanceof PagedTileLayout) { - ((PagedTileLayout) mHorizontalTileLayout).setPageMargin(pageMargin); + if (mTileLayout instanceof PagedTileLayout) { + ((PagedTileLayout) mTileLayout).setPageMargin(pageMargin); } } - void setUsingHorizontalLayout(boolean horizontal, ViewGroup mediaHostView, boolean force, - UiEventLogger uiEventLogger) { + void setUsingHorizontalLayout(boolean horizontal, ViewGroup mediaHostView, boolean force) { if (horizontal != mUsingHorizontalLayout || force) { mUsingHorizontalLayout = horizontal; - View visibleView = horizontal ? mHorizontalLinearLayout : (View) mRegularTileLayout; - View hiddenView = horizontal ? (View) mRegularTileLayout : mHorizontalLinearLayout; ViewGroup newParent = horizontal ? mHorizontalContentContainer : this; - QSPanel.QSTileLayout newLayout = horizontal - ? mHorizontalTileLayout : mRegularTileLayout; - if (hiddenView != null - && (mRegularTileLayout != mHorizontalTileLayout - || hiddenView != mRegularTileLayout)) { - // Only hide the view if the horizontal and the regular view are different, - // otherwise its reattached. - hiddenView.setVisibility(View.GONE); - } - visibleView.setVisibility(View.VISIBLE); - switchAllContentToParent(newParent, newLayout); + switchAllContentToParent(newParent, mTileLayout); reAttachMediaHost(mediaHostView, horizontal); - mTileLayout = newLayout; - newLayout.setListening(mListening, uiEventLogger); if (needsDynamicRowsAndColumns()) { - newLayout.setMinRows(horizontal ? 2 : 1); - newLayout.setMaxColumns(horizontal ? 2 : 4); + mTileLayout.setMinRows(horizontal ? 2 : 1); + mTileLayout.setMaxColumns(horizontal ? 2 : 4); } updateMargins(mediaHostView); } diff --git a/packages/SystemUI/src/com/android/systemui/qs/QSPanelController.java b/packages/SystemUI/src/com/android/systemui/qs/QSPanelController.java index 9810e96ed199..ae0f5104d20f 100644 --- a/packages/SystemUI/src/com/android/systemui/qs/QSPanelController.java +++ b/packages/SystemUI/src/com/android/systemui/qs/QSPanelController.java @@ -147,14 +147,14 @@ public class QSPanelController extends QSPanelControllerBase<QSPanel> { mBrightnessMirrorController.addCallback(mBrightnessMirrorListener); } - ((PagedTileLayout) mView.createRegularTileLayout()) + ((PagedTileLayout) mView.getOrCreateTileLayout()) .setOnTouchListener(mTileLayoutTouchListener); } @Override protected QSTileRevealController createTileRevealController() { return mQsTileRevealControllerFactory.create( - this, (PagedTileLayout) mView.createRegularTileLayout()); + this, (PagedTileLayout) mView.getOrCreateTileLayout()); } @Override diff --git a/packages/SystemUI/src/com/android/systemui/qs/QSPanelControllerBase.java b/packages/SystemUI/src/com/android/systemui/qs/QSPanelControllerBase.java index 77591b50c103..7a0982688b49 100644 --- a/packages/SystemUI/src/com/android/systemui/qs/QSPanelControllerBase.java +++ b/packages/SystemUI/src/com/android/systemui/qs/QSPanelControllerBase.java @@ -297,20 +297,12 @@ public abstract class QSPanelControllerBase<T extends QSPanel> extends ViewContr } boolean switchTileLayout(boolean force) { - /** Whether or not the QuickQSPanel currently contains a media player. */ + /* Whether or not the panel currently contains a media player. */ boolean horizontal = shouldUseHorizontalLayout(); if (horizontal != mUsingHorizontalLayout || force) { mUsingHorizontalLayout = horizontal; - for (QSPanelControllerBase.TileRecord record : mRecords) { - mView.removeTile(record); - record.tile.removeCallback(record.callback); - } - mView.setUsingHorizontalLayout(mUsingHorizontalLayout, mMediaHost.getHostView(), force, - mUiEventLogger); + mView.setUsingHorizontalLayout(mUsingHorizontalLayout, mMediaHost.getHostView(), force); updateMediaDisappearParameters(); - - setTiles(); - return true; } return false; diff --git a/packages/SystemUI/src/com/android/systemui/qs/QuickQSPanel.java b/packages/SystemUI/src/com/android/systemui/qs/QuickQSPanel.java index 659475d19277..68962b0cd17a 100644 --- a/packages/SystemUI/src/com/android/systemui/qs/QuickQSPanel.java +++ b/packages/SystemUI/src/com/android/systemui/qs/QuickQSPanel.java @@ -61,21 +61,10 @@ public class QuickQSPanel extends QSPanel { } @Override - public TileLayout createRegularTileLayout() { + public TileLayout getOrCreateTileLayout() { return new QQSSideLabelTileLayout(mContext); } - @Override - protected QSTileLayout createHorizontalTileLayout() { - TileLayout t = createRegularTileLayout(); - t.setMaxColumns(2); - return t; - } - - @Override - protected boolean needsDynamicRowsAndColumns() { - return false; // QQS always have the same layout - } @Override protected boolean displayMediaMarginsOnMedia() { diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/QuickAccessWalletTile.java b/packages/SystemUI/src/com/android/systemui/qs/tiles/QuickAccessWalletTile.java index 98cd88af232f..4dc7508f42b5 100644 --- a/packages/SystemUI/src/com/android/systemui/qs/tiles/QuickAccessWalletTile.java +++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/QuickAccessWalletTile.java @@ -73,6 +73,7 @@ public class QuickAccessWalletTile extends QSTileImpl<QSTile.State> { private final QuickAccessWalletController mController; private WalletCard mSelectedCard; + private boolean mIsWalletUpdating = true; @VisibleForTesting Drawable mCardViewDrawable; @Inject @@ -110,7 +111,8 @@ public class QuickAccessWalletTile extends QSTileImpl<QSTile.State> { super.handleSetListening(listening); if (listening) { mController.setupWalletChangeObservers(mCardRetriever, DEFAULT_PAYMENT_APP_CHANGE); - if (!mController.getWalletClient().isWalletServiceAvailable()) { + if (!mController.getWalletClient().isWalletServiceAvailable() + || !mController.getWalletClient().isWalletFeatureAvailable()) { Log.i(TAG, "QAW service is unavailable, recreating the wallet client."); mController.reCreateWalletClient(); } @@ -158,7 +160,8 @@ public class QuickAccessWalletTile extends QSTileImpl<QSTile.State> { state.contentDescription = state.label; state.icon = ResourceIcon.get(R.drawable.ic_wallet_lockscreen); boolean isDeviceLocked = !mKeyguardStateController.isUnlocked(); - if (mController.getWalletClient().isWalletServiceAvailable()) { + if (mController.getWalletClient().isWalletServiceAvailable() + && mController.getWalletClient().isWalletFeatureAvailable()) { if (mSelectedCard != null) { if (isDeviceLocked) { state.state = Tile.STATE_INACTIVE; @@ -172,7 +175,11 @@ public class QuickAccessWalletTile extends QSTileImpl<QSTile.State> { } } else { state.state = Tile.STATE_INACTIVE; - state.secondaryLabel = mContext.getString(R.string.wallet_secondary_label_no_card); + state.secondaryLabel = + mContext.getString( + mIsWalletUpdating + ? R.string.wallet_secondary_label_updating + : R.string.wallet_secondary_label_no_card); state.sideViewCustomDrawable = null; } state.stateDescription = state.secondaryLabel; @@ -218,6 +225,7 @@ public class QuickAccessWalletTile extends QSTileImpl<QSTile.State> { @Override public void onWalletCardsRetrieved(@NonNull GetWalletCardsResponse response) { Log.i(TAG, "Successfully retrieved wallet cards."); + mIsWalletUpdating = false; List<WalletCard> cards = response.getWalletCards(); if (cards.isEmpty()) { Log.d(TAG, "No wallet cards exist."); @@ -240,7 +248,7 @@ public class QuickAccessWalletTile extends QSTileImpl<QSTile.State> { @Override public void onWalletCardRetrievalError(@NonNull GetWalletCardsError error) { - Log.w(TAG, "Error retrieve wallet cards"); + mIsWalletUpdating = false; mCardViewDrawable = null; mSelectedCard = null; refreshState(); diff --git a/packages/SystemUI/src/com/android/systemui/screenshot/CropView.java b/packages/SystemUI/src/com/android/systemui/screenshot/CropView.java index 0a60f6da159e..a9cecaaf1f76 100644 --- a/packages/SystemUI/src/com/android/systemui/screenshot/CropView.java +++ b/packages/SystemUI/src/com/android/systemui/screenshot/CropView.java @@ -44,6 +44,7 @@ import androidx.core.view.accessibility.AccessibilityNodeInfoCompat; import androidx.customview.widget.ExploreByTouchHelper; import androidx.interpolator.view.animation.FastOutSlowInInterpolator; +import com.android.internal.graphics.ColorUtils; import com.android.systemui.R; import java.util.List; @@ -95,7 +96,9 @@ public class CropView extends View { TypedArray t = context.getTheme().obtainStyledAttributes( attrs, R.styleable.CropView, 0, 0); mShadePaint = new Paint(); - mShadePaint.setColor(t.getColor(R.styleable.CropView_scrimColor, Color.TRANSPARENT)); + int alpha = t.getInteger(R.styleable.CropView_scrimAlpha, 255); + int scrimColor = t.getColor(R.styleable.CropView_scrimColor, Color.TRANSPARENT); + mShadePaint.setColor(ColorUtils.setAlphaComponent(scrimColor, alpha)); mContainerBackgroundPaint = new Paint(); mContainerBackgroundPaint.setColor(t.getColor(R.styleable.CropView_containerBackgroundColor, Color.TRANSPARENT)); diff --git a/packages/SystemUI/src/com/android/systemui/screenshot/LongScreenshotActivity.java b/packages/SystemUI/src/com/android/systemui/screenshot/LongScreenshotActivity.java index 741dddc49378..35637f66d0df 100644 --- a/packages/SystemUI/src/com/android/systemui/screenshot/LongScreenshotActivity.java +++ b/packages/SystemUI/src/com/android/systemui/screenshot/LongScreenshotActivity.java @@ -126,6 +126,8 @@ public class LongScreenshotActivity extends Activity { mTransitionView = requireViewById(R.id.transition); mEnterTransitionView = requireViewById(R.id.enter_transition); + requireViewById(R.id.cancel).setOnClickListener(v -> finishAndRemoveTask()); + mSave.setOnClickListener(this::onClicked); mEdit.setOnClickListener(this::onClicked); mShare.setOnClickListener(this::onClicked); diff --git a/packages/SystemUI/src/com/android/systemui/screenshot/MagnifierView.java b/packages/SystemUI/src/com/android/systemui/screenshot/MagnifierView.java index 34b40f79836b..78737329750a 100644 --- a/packages/SystemUI/src/com/android/systemui/screenshot/MagnifierView.java +++ b/packages/SystemUI/src/com/android/systemui/screenshot/MagnifierView.java @@ -33,6 +33,7 @@ import android.view.ViewPropertyAnimator; import androidx.annotation.Nullable; +import com.android.internal.graphics.ColorUtils; import com.android.systemui.R; /** @@ -83,7 +84,9 @@ public class MagnifierView extends View implements CropView.CropInteractionListe TypedArray t = context.getTheme().obtainStyledAttributes( attrs, R.styleable.MagnifierView, 0, 0); mShadePaint = new Paint(); - mShadePaint.setColor(t.getColor(R.styleable.MagnifierView_scrimColor, Color.TRANSPARENT)); + int alpha = t.getInteger(R.styleable.MagnifierView_scrimAlpha, 255); + int scrimColor = t.getColor(R.styleable.MagnifierView_scrimColor, Color.TRANSPARENT); + mShadePaint.setColor(ColorUtils.setAlphaComponent(scrimColor, alpha)); mHandlePaint = new Paint(); mHandlePaint.setColor(t.getColor(R.styleable.MagnifierView_handleColor, Color.BLACK)); mHandlePaint.setStrokeWidth( diff --git a/packages/SystemUI/src/com/android/systemui/screenshot/ScreenshotController.java b/packages/SystemUI/src/com/android/systemui/screenshot/ScreenshotController.java index 63ecc0b89783..5a3d3f9c6f3e 100644 --- a/packages/SystemUI/src/com/android/systemui/screenshot/ScreenshotController.java +++ b/packages/SystemUI/src/com/android/systemui/screenshot/ScreenshotController.java @@ -16,6 +16,7 @@ package com.android.systemui.screenshot; +import static android.content.res.Configuration.ORIENTATION_PORTRAIT; import static android.view.Display.DEFAULT_DISPLAY; import static android.view.ViewGroup.LayoutParams.MATCH_PARENT; import static android.view.WindowManager.LayoutParams.TYPE_SCREENSHOT; @@ -259,6 +260,7 @@ public class ScreenshotController { private ScreenshotView mScreenshotView; private Bitmap mScreenBitmap; private SaveImageInBackgroundTask mSaveInBgTask; + private boolean mScreenshotTakenInPortrait; private Animator mScreenshotAnimation; private RequestCallback mCurrentRequestCallback; @@ -488,6 +490,9 @@ public class ScreenshotController { * Takes a screenshot of the current display and shows an animation. */ private void takeScreenshotInternal(Consumer<Uri> finisher, Rect crop) { + mScreenshotTakenInPortrait = + mContext.getResources().getConfiguration().orientation == ORIENTATION_PORTRAIT; + // copy the input Rect, since SurfaceControl.screenshot can mutate it Rect screenRect = new Rect(crop); Bitmap screenshot = captureScreenshot(crop); @@ -661,7 +666,8 @@ public class ScreenshotController { Bitmap newScreenshot = captureScreenshot( new Rect(0, 0, displayMetrics.widthPixels, displayMetrics.heightPixels)); - mScreenshotView.prepareScrollingTransition(response, mScreenBitmap, newScreenshot); + mScreenshotView.prepareScrollingTransition(response, mScreenBitmap, newScreenshot, + mScreenshotTakenInPortrait); // delay starting scroll capture to make sure the scrim is up before the app moves mScreenshotView.post(() -> { // Clear the reference to prevent close() in dismissScreenshot diff --git a/packages/SystemUI/src/com/android/systemui/screenshot/ScreenshotView.java b/packages/SystemUI/src/com/android/systemui/screenshot/ScreenshotView.java index 9fe8c84bc13d..dc736374cd59 100644 --- a/packages/SystemUI/src/com/android/systemui/screenshot/ScreenshotView.java +++ b/packages/SystemUI/src/com/android/systemui/screenshot/ScreenshotView.java @@ -162,6 +162,7 @@ public class ScreenshotView extends FrameLayout implements private GestureDetector mSwipeDetector; private SwipeDismissHandler mSwipeDismissHandler; private InputMonitorCompat mInputMonitor; + private boolean mShowScrollablePreview; private final ArrayList<ScreenshotActionChip> mSmartChips = new ArrayList<>(); private PendingInteraction mPendingInteraction; @@ -772,70 +773,99 @@ public class ScreenshotView extends FrameLayout implements void startLongScreenshotTransition(Rect destination, Runnable onTransitionEnd, ScrollCaptureController.LongScreenshot longScreenshot) { - mScrollablePreview.setImageBitmap(longScreenshot.toBitmap()); - ValueAnimator anim = ValueAnimator.ofFloat(0, 1); - float startX = mScrollablePreview.getX(); - float startY = mScrollablePreview.getY(); - int[] locInScreen = mScrollablePreview.getLocationOnScreen(); - destination.offset((int) startX - locInScreen[0], (int) startY - locInScreen[1]); - mScrollablePreview.setPivotX(0); - mScrollablePreview.setPivotY(0); - mScrollablePreview.setAlpha(1f); - float currentScale = mScrollablePreview.getWidth() / (float) longScreenshot.getWidth(); - Matrix matrix = new Matrix(); - matrix.setScale(currentScale, currentScale); - matrix.postTranslate( - longScreenshot.getLeft() * currentScale, longScreenshot.getTop() * currentScale); - mScrollablePreview.setImageMatrix(matrix); - float destinationScale = destination.width() / (float) mScrollablePreview.getWidth(); - anim.addUpdateListener(animation -> { - float t = animation.getAnimatedFraction(); - mScrollingScrim.setAlpha(1 - t); - float currScale = MathUtils.lerp(1, destinationScale, t); - mScrollablePreview.setScaleX(currScale); - mScrollablePreview.setScaleY(currScale); - mScrollablePreview.setX(MathUtils.lerp(startX, destination.left, t)); - mScrollablePreview.setY(MathUtils.lerp(startY, destination.top, t)); - }); - anim.addListener(new AnimatorListenerAdapter() { + AnimatorSet animSet = new AnimatorSet(); + + ValueAnimator scrimAnim = ValueAnimator.ofFloat(0, 1); + scrimAnim.addUpdateListener(animation -> + mScrollingScrim.setAlpha(1 - animation.getAnimatedFraction())); + + if (mShowScrollablePreview) { + mScrollablePreview.setImageBitmap(longScreenshot.toBitmap()); + float startX = mScrollablePreview.getX(); + float startY = mScrollablePreview.getY(); + int[] locInScreen = mScrollablePreview.getLocationOnScreen(); + destination.offset((int) startX - locInScreen[0], (int) startY - locInScreen[1]); + mScrollablePreview.setPivotX(0); + mScrollablePreview.setPivotY(0); + mScrollablePreview.setAlpha(1f); + float currentScale = mScrollablePreview.getWidth() / (float) longScreenshot.getWidth(); + Matrix matrix = new Matrix(); + matrix.setScale(currentScale, currentScale); + matrix.postTranslate( + longScreenshot.getLeft() * currentScale, + longScreenshot.getTop() * currentScale); + mScrollablePreview.setImageMatrix(matrix); + float destinationScale = destination.width() / (float) mScrollablePreview.getWidth(); + + ValueAnimator previewAnim = ValueAnimator.ofFloat(0, 1); + previewAnim.addUpdateListener(animation -> { + float t = animation.getAnimatedFraction(); + float currScale = MathUtils.lerp(1, destinationScale, t); + mScrollablePreview.setScaleX(currScale); + mScrollablePreview.setScaleY(currScale); + mScrollablePreview.setX(MathUtils.lerp(startX, destination.left, t)); + mScrollablePreview.setY(MathUtils.lerp(startY, destination.top, t)); + }); + ValueAnimator previewFadeAnim = ValueAnimator.ofFloat(1, 0); + previewFadeAnim.addUpdateListener(animation -> + mScrollablePreview.setAlpha(1 - animation.getAnimatedFraction())); + animSet.play(previewAnim).with(scrimAnim).before(previewFadeAnim); + previewAnim.addListener(new AnimatorListenerAdapter() { + @Override + public void onAnimationEnd(Animator animation) { + super.onAnimationEnd(animation); + onTransitionEnd.run(); + } + }); + } else { + // if we switched orientations between the original screenshot and the long screenshot + // capture, just fade out the scrim instead of running the preview animation + animSet.play(scrimAnim); + animSet.addListener(new AnimatorListenerAdapter() { + @Override + public void onAnimationEnd(Animator animation) { + super.onAnimationEnd(animation); + onTransitionEnd.run(); + } + }); + } + animSet.addListener(new AnimatorListenerAdapter() { @Override public void onAnimationEnd(Animator animation) { super.onAnimationEnd(animation); - onTransitionEnd.run(); - mScrollablePreview.animate().alpha(0).setListener(new AnimatorListenerAdapter() { - @Override - public void onAnimationEnd(Animator animation) { - super.onAnimationEnd(animation); mCallbacks.onDismiss(); } - }); - } }); - anim.start(); + animSet.start(); } void prepareScrollingTransition(ScrollCaptureResponse response, Bitmap screenBitmap, - Bitmap newBitmap) { + Bitmap newBitmap, boolean screenshotTakenInPortrait) { + mShowScrollablePreview = (screenshotTakenInPortrait == mOrientationPortrait); + mScrollingScrim.setImageBitmap(newBitmap); mScrollingScrim.setVisibility(View.VISIBLE); - Rect scrollableArea = scrollableAreaOnScreen(response); - float scale = mCornerSizeX - / (mOrientationPortrait ? screenBitmap.getWidth() : screenBitmap.getHeight()); - ConstraintLayout.LayoutParams params = - (ConstraintLayout.LayoutParams) mScrollablePreview.getLayoutParams(); - - params.width = (int) (scale * scrollableArea.width()); - params.height = (int) (scale * scrollableArea.height()); - Matrix matrix = new Matrix(); - matrix.setScale(scale, scale); - matrix.postTranslate(-scrollableArea.left * scale, -scrollableArea.top * scale); - - mScrollablePreview.setTranslationX(scale * scrollableArea.left); - mScrollablePreview.setTranslationY(scale * scrollableArea.top); - mScrollablePreview.setImageMatrix(matrix); - - mScrollablePreview.setImageBitmap(screenBitmap); - mScrollablePreview.setVisibility(View.VISIBLE); + + if (mShowScrollablePreview) { + Rect scrollableArea = scrollableAreaOnScreen(response); + + float scale = mCornerSizeX + / (mOrientationPortrait ? screenBitmap.getWidth() : screenBitmap.getHeight()); + ConstraintLayout.LayoutParams params = + (ConstraintLayout.LayoutParams) mScrollablePreview.getLayoutParams(); + + params.width = (int) (scale * scrollableArea.width()); + params.height = (int) (scale * scrollableArea.height()); + Matrix matrix = new Matrix(); + matrix.setScale(scale, scale); + matrix.postTranslate(-scrollableArea.left * scale, -scrollableArea.top * scale); + + mScrollablePreview.setTranslationX(scale * scrollableArea.left); + mScrollablePreview.setTranslationY(scale * scrollableArea.top); + mScrollablePreview.setImageMatrix(matrix); + mScrollablePreview.setImageBitmap(screenBitmap); + mScrollablePreview.setVisibility(View.VISIBLE); + } mDismissButton.setVisibility(View.GONE); mActionsContainer.setVisibility(View.GONE); mBackgroundProtection.setVisibility(View.GONE); @@ -919,6 +949,8 @@ public class ScreenshotView extends FrameLayout implements mActionsContainer.setVisibility(View.GONE); mBackgroundProtection.setAlpha(0f); mDismissButton.setVisibility(View.GONE); + mScrollingScrim.setVisibility(View.GONE); + mScrollablePreview.setVisibility(View.GONE); mScreenshotStatic.setTranslationX(0); mScreenshotPreview.setTranslationY(0); mScreenshotPreview.setContentDescription( diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/charging/ChargingRippleView.kt b/packages/SystemUI/src/com/android/systemui/statusbar/charging/ChargingRippleView.kt index 3196eba7d33a..4a467ce3c987 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/charging/ChargingRippleView.kt +++ b/packages/SystemUI/src/com/android/systemui/statusbar/charging/ChargingRippleView.kt @@ -39,9 +39,15 @@ class ChargingRippleView(context: Context?, attrs: AttributeSet?) : View(context var rippleInProgress: Boolean = false var radius: Float = 0.0f - set(value) { rippleShader.radius = value } + set(value) { + rippleShader.radius = value + field = value + } var origin: PointF = PointF() - set(value) { rippleShader.origin = value } + set(value) { + rippleShader.origin = value + field = value + } var duration: Long = 1750 init { @@ -94,6 +100,11 @@ class ChargingRippleView(context: Context?, attrs: AttributeSet?) : View(context } override fun onDraw(canvas: Canvas?) { - canvas?.drawRect(0f, 0f, width.toFloat(), height.toFloat(), ripplePaint) + // To reduce overdraw, we mask the effect to a circle whose radius is big enough to cover + // the active effect area. Values here should be kept in sync with the + // animation implementation in the ripple shader. + val maskRadius = (1 - (1 - rippleShader.progress) * (1 - rippleShader.progress) * + (1 - rippleShader.progress)) * radius * 1.5f + canvas?.drawCircle(origin.x, origin.y, maskRadius, ripplePaint) } } diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/StackScrollAlgorithm.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/StackScrollAlgorithm.java index 9ce9aa80604e..add6fda4f8b9 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/StackScrollAlgorithm.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/StackScrollAlgorithm.java @@ -27,7 +27,6 @@ import android.view.ViewGroup; import com.android.systemui.R; import com.android.systemui.animation.Interpolators; import com.android.systemui.statusbar.NotificationShelf; -import com.android.systemui.statusbar.notification.dagger.SilentHeader; import com.android.systemui.statusbar.notification.row.ActivatableNotificationView; import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow; import com.android.systemui.statusbar.notification.row.ExpandableView; @@ -37,9 +36,9 @@ import java.util.ArrayList; import java.util.List; /** - * The Algorithm of the {@link com.android.systemui.statusbar.notification.stack - * .NotificationStackScrollLayout} which can be queried for {@link com.android.systemui.statusbar - * .stack.StackScrollState} + * The Algorithm of the + * {@link com.android.systemui.statusbar.notification.stack.NotificationStackScrollLayout} which can + * be queried for {@link StackScrollAlgorithmState} */ public class StackScrollAlgorithm { @@ -96,7 +95,7 @@ public class StackScrollAlgorithm { // First we reset the view states to their default values. resetChildViewStates(); - initAlgorithmState(mHostView, algorithmState, ambientState); + initAlgorithmState(algorithmState, ambientState); updatePositionsForState(algorithmState, ambientState); updateZValuesForState(algorithmState, ambientState); updateHeadsUpStates(algorithmState, ambientState); @@ -216,19 +215,18 @@ public class StackScrollAlgorithm { /** * Initialize the algorithm state like updating the visible children. */ - private void initAlgorithmState(ViewGroup hostView, StackScrollAlgorithmState state, - AmbientState ambientState) { + private void initAlgorithmState(StackScrollAlgorithmState state, AmbientState ambientState) { state.scrollY = ambientState.getScrollY(); state.mCurrentYPosition = -state.scrollY; state.mCurrentExpandedYPosition = -state.scrollY; //now init the visible children and update paddings - int childCount = hostView.getChildCount(); + int childCount = mHostView.getChildCount(); state.visibleChildren.clear(); state.visibleChildren.ensureCapacity(childCount); int notGoneIndex = 0; for (int i = 0; i < childCount; i++) { - ExpandableView v = (ExpandableView) hostView.getChildAt(i); + ExpandableView v = (ExpandableView) mHostView.getChildAt(i); if (v.getVisibility() != View.GONE) { if (v == ambientState.getShelf()) { continue; @@ -237,7 +235,7 @@ public class StackScrollAlgorithm { if (v instanceof ExpandableNotificationRow) { ExpandableNotificationRow row = (ExpandableNotificationRow) v; - // handle the notgoneIndex for the children as well + // handle the notGoneIndex for the children as well List<ExpandableNotificationRow> children = row.getAttachedChildren(); if (row.isSummaryWithChildren() && children != null) { for (ExpandableNotificationRow childRow : children) { @@ -407,29 +405,33 @@ public class StackScrollAlgorithm { final boolean noSpaceForFooter = footerEnd > ambientState.getStackEndHeight(); ((FooterView.FooterViewState) viewState).hideContent = shadeClosed || isShelfShowing || noSpaceForFooter; - - } else if (view != ambientState.getTrackedHeadsUpRow()) { - if (ambientState.isExpansionChanging()) { - // Show all views. Views below the shelf will later be clipped (essentially hidden) - // in NotificationShelf. - viewState.hidden = false; - viewState.inShelf = algorithmState.firstViewInShelf != null - && i >= algorithmState.visibleChildren.indexOf( - algorithmState.firstViewInShelf); - } else if (ambientState.getShelf() != null) { - // When pulsing (incoming notification on AOD), innerHeight is 0; clamp all - // to shelf start, thereby hiding all notifications (except the first one, which we - // later unhide in updatePulsingState) - final int stackBottom = !ambientState.isShadeExpanded() || ambientState.isDozing() - ? ambientState.getInnerHeight() : (int) ambientState.getStackHeight(); - final int shelfStart = stackBottom - ambientState.getShelf().getIntrinsicHeight(); - viewState.yTranslation = Math.min(viewState.yTranslation, shelfStart); - if (viewState.yTranslation >= shelfStart) { - viewState.hidden = !view.isExpandAnimationRunning() - && !view.hasExpandingChild(); - viewState.inShelf = true; - // Notifications in the shelf cannot be visible HUNs. - viewState.headsUpIsVisible = false; + } else { + if (view != ambientState.getTrackedHeadsUpRow()) { + if (ambientState.isExpansionChanging()) { + // Show all views. Views below the shelf will later be clipped (essentially + // hidden) in NotificationShelf. + viewState.hidden = false; + viewState.inShelf = algorithmState.firstViewInShelf != null + && i >= algorithmState.visibleChildren.indexOf( + algorithmState.firstViewInShelf); + } else if (ambientState.getShelf() != null) { + // When pulsing (incoming notification on AOD), innerHeight is 0; clamp all + // to shelf start, thereby hiding all notifications (except the first one, which + // we later unhide in updatePulsingState) + final int stackBottom = + !ambientState.isShadeExpanded() || ambientState.isDozing() + ? ambientState.getInnerHeight() + : (int) ambientState.getStackHeight(); + final int shelfStart = + stackBottom - ambientState.getShelf().getIntrinsicHeight(); + viewState.yTranslation = Math.min(viewState.yTranslation, shelfStart); + if (viewState.yTranslation >= shelfStart) { + viewState.hidden = !view.isExpandAnimationRunning() + && !view.hasExpandingChild(); + viewState.inShelf = true; + // Notifications in the shelf cannot be visible HUNs. + viewState.headsUpIsVisible = false; + } } } @@ -485,7 +487,7 @@ public class StackScrollAlgorithm { View previousChild) { return sectionProvider.beginsSection(child, previousChild) && visibleIndex > 0 - && !(previousChild instanceof SilentHeader) + && !(previousChild instanceof SectionHeaderView) && !(child instanceof FooterView); } @@ -696,7 +698,7 @@ public class StackScrollAlgorithm { this.mIsExpanded = isExpanded; } - public class StackScrollAlgorithmState { + public static class StackScrollAlgorithmState { /** * The scroll position of the algorithm (absolute scrolling). diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelViewController.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelViewController.java index 1a2b9898398b..5ebfacfd5e04 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelViewController.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelViewController.java @@ -121,6 +121,7 @@ import com.android.systemui.statusbar.KeyguardAffordanceView; import com.android.systemui.statusbar.KeyguardIndicationController; import com.android.systemui.statusbar.LockscreenShadeTransitionController; import com.android.systemui.statusbar.NotificationLockscreenUserManager; +import com.android.systemui.statusbar.NotificationRemoteInputManager; import com.android.systemui.statusbar.NotificationShadeDepthController; import com.android.systemui.statusbar.NotificationShelfController; import com.android.systemui.statusbar.PulseExpansionHandler; @@ -314,6 +315,7 @@ public class NotificationPanelViewController extends PanelViewController { private final ScrimController mScrimController; private final PrivacyDotViewController mPrivacyDotViewController; private final QuickAccessWalletController mQuickAccessWalletController; + private final NotificationRemoteInputManager mRemoteInputManager; // Maximum # notifications to show on Keyguard; extras will be collapsed in an overflow card. // If there are exactly 1 + mMaxKeyguardNotifications, then still shows all notifications @@ -372,7 +374,6 @@ public class NotificationPanelViewController extends PanelViewController { private float mLastOverscroll; private boolean mQsExpansionEnabledPolicy = true; private boolean mQsExpansionEnabledAmbient = true; - private boolean mQsExpansionEnabled = mQsExpansionEnabledPolicy && mQsExpansionEnabledAmbient; private ValueAnimator mQsExpansionAnimator; private FlingAnimationUtils mFlingAnimationUtils; private int mStatusBarMinHeight; @@ -696,7 +697,8 @@ public class NotificationPanelViewController extends PanelViewController { QuickAccessWalletController quickAccessWalletController, @Main Executor uiExecutor, SecureSettings secureSettings, - UnlockedScreenOffAnimationController unlockedScreenOffAnimationController) { + UnlockedScreenOffAnimationController unlockedScreenOffAnimationController, + NotificationRemoteInputManager remoteInputManager) { super(view, falsingManager, dozeLog, keyguardStateController, (SysuiStatusBarStateController) statusBarStateController, vibratorHelper, statusBarKeyguardViewManager, latencyTracker, flingAnimationUtilsBuilder.get(), @@ -789,6 +791,8 @@ public class NotificationPanelViewController extends PanelViewController { mAuthController = authController; mLockIconViewController = lockIconViewController; mUnlockedScreenOffAnimationController = unlockedScreenOffAnimationController; + mRemoteInputManager = remoteInputManager; + int currentMode = navigationModeController.addListener( mode -> mIsGestureNavigation = QuickStepContract.isGesturalMode(mode)); mIsGestureNavigation = QuickStepContract.isGesturalMode(currentMode); @@ -1460,9 +1464,8 @@ public class NotificationPanelViewController extends PanelViewController { } private void setQsExpansionEnabled() { - mQsExpansionEnabled = mQsExpansionEnabledPolicy && mQsExpansionEnabledAmbient; if (mQs == null) return; - mQs.setHeaderClickable(mQsExpansionEnabled); + mQs.setHeaderClickable(isQsExpansionEnabled()); } public void setQsExpansionEnabledPolicy(boolean qsExpansionEnabledPolicy) { @@ -1531,8 +1534,13 @@ public class NotificationPanelViewController extends PanelViewController { flingSettings(0 /* vel */, animateAway ? FLING_HIDE : FLING_COLLAPSE); } + private boolean isQsExpansionEnabled() { + return mQsExpansionEnabledPolicy && mQsExpansionEnabledAmbient + && !mRemoteInputManager.getController().isRemoteInputActive(); + } + public void expandWithQs() { - if (mQsExpansionEnabled) { + if (isQsExpansionEnabled()) { mQsExpandImmediate = true; mNotificationStackScrollLayoutController.setShouldShowShelfOnly(true); } @@ -1797,7 +1805,7 @@ public class NotificationPanelViewController extends PanelViewController { private boolean handleQsTouch(MotionEvent event) { final int action = event.getActionMasked(); if (action == MotionEvent.ACTION_DOWN && getExpandedFraction() == 1f - && mBarState != KEYGUARD && !mQsExpanded && mQsExpansionEnabled) { + && mBarState != KEYGUARD && !mQsExpanded && isQsExpansionEnabled()) { // Down in the empty area while fully expanded - go to QS. mQsTracking = true; traceQsJank(true /* startTracing */, false /* wasCancelled */); @@ -1819,7 +1827,7 @@ public class NotificationPanelViewController extends PanelViewController { if (action == MotionEvent.ACTION_CANCEL || action == MotionEvent.ACTION_UP) { mConflictingQsExpansionGesture = false; } - if (action == MotionEvent.ACTION_DOWN && isFullyCollapsed() && mQsExpansionEnabled) { + if (action == MotionEvent.ACTION_DOWN && isFullyCollapsed() && isQsExpansionEnabled()) { mTwoFingerQsExpandPossible = true; } if (mTwoFingerQsExpandPossible && isOpenQsEvent(event) && event.getY(event.getActionIndex()) @@ -2657,7 +2665,7 @@ public class NotificationPanelViewController extends PanelViewController { * @return Whether we should intercept a gesture to open Quick Settings. */ private boolean shouldQuickSettingsIntercept(float x, float y, float yDiff) { - if (!mQsExpansionEnabled || mCollapsedOnDown || (mKeyguardShowing + if (!isQsExpansionEnabled() || mCollapsedOnDown || (mKeyguardShowing && mKeyguardBypassController.getBypassEnabled())) { return false; } @@ -3476,7 +3484,7 @@ public class NotificationPanelViewController extends PanelViewController { mQs = (QS) fragment; mQs.setPanelView(mHeightListener); mQs.setExpandClickListener(mOnClickListener); - mQs.setHeaderClickable(mQsExpansionEnabled); + mQs.setHeaderClickable(isQsExpansionEnabled()); updateQSPulseExpansion(); mQs.setOverscrolling(mStackScrollerOverscrolling); mQs.setTranslateWhileExpanding(mShouldUseSplitNotificationShade); @@ -3967,7 +3975,7 @@ public class NotificationPanelViewController extends PanelViewController { if (mQsExpanded) { flingSettings(0 /* vel */, FLING_COLLAPSE, null /* onFinishRunnable */, true /* isClick */); - } else if (mQsExpansionEnabled) { + } else if (isQsExpansionEnabled()) { mLockscreenGestureLogger.write(MetricsEvent.ACTION_SHADE_QS_TAP, 0, 0); flingSettings(0 /* vel */, FLING_EXPAND, null /* onFinishRunnable */, true /* isClick */); @@ -3984,7 +3992,7 @@ public class NotificationPanelViewController extends PanelViewController { return; } cancelQsAnimation(); - if (!mQsExpansionEnabled) { + if (!isQsExpansionEnabled()) { amount = 0f; } float rounded = amount >= 1f ? amount : 0f; @@ -4012,8 +4020,9 @@ public class NotificationPanelViewController extends PanelViewController { setOverScrolling(false); } setQsExpansion(mQsExpansionHeight); - flingSettings(!mQsExpansionEnabled && open ? 0f : velocity, - open && mQsExpansionEnabled ? FLING_EXPAND : FLING_COLLAPSE, () -> { + boolean canExpand = isQsExpansionEnabled(); + flingSettings(!canExpand && open ? 0f : velocity, + open && canExpand ? FLING_EXPAND : FLING_COLLAPSE, () -> { setOverScrolling(false); updateQsState(); }, false /* isClick */); diff --git a/packages/SystemUI/src/com/android/systemui/wallet/controller/QuickAccessWalletController.java b/packages/SystemUI/src/com/android/systemui/wallet/controller/QuickAccessWalletController.java index 65f236b77a64..0ecc4e25047f 100644 --- a/packages/SystemUI/src/com/android/systemui/wallet/controller/QuickAccessWalletController.java +++ b/packages/SystemUI/src/com/android/systemui/wallet/controller/QuickAccessWalletController.java @@ -25,6 +25,7 @@ import android.provider.Settings; import android.service.quickaccesswallet.GetWalletCardsRequest; import android.service.quickaccesswallet.QuickAccessWalletClient; import android.service.quickaccesswallet.QuickAccessWalletClientImpl; +import android.util.Log; import com.android.systemui.R; import com.android.systemui.dagger.SysUISingleton; @@ -142,6 +143,10 @@ public class QuickAccessWalletController { */ public void queryWalletCards( QuickAccessWalletClient.OnWalletCardsRetrievedCallback cardsRetriever) { + if (!mQuickAccessWalletClient.isWalletFeatureAvailable()) { + Log.d(TAG, "QuickAccessWallet feature is not available."); + return; + } int cardWidth = mContext.getResources().getDimensionPixelSize(R.dimen.wallet_tile_card_view_width); int cardHeight = diff --git a/packages/SystemUI/tests/src/com/android/systemui/people/PeopleBackupFollowUpJobTest.java b/packages/SystemUI/tests/src/com/android/systemui/people/PeopleBackupFollowUpJobTest.java new file mode 100644 index 000000000000..00e012e5d30c --- /dev/null +++ b/packages/SystemUI/tests/src/com/android/systemui/people/PeopleBackupFollowUpJobTest.java @@ -0,0 +1,202 @@ +/* + * Copyright (C) 2021 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.systemui.people; + +import static com.android.systemui.people.PeopleBackupFollowUpJob.SHARED_FOLLOW_UP; + +import static com.google.common.truth.Truth.assertThat; + +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.ArgumentMatchers.anyInt; +import static org.mockito.Mockito.times; +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.when; + +import android.app.job.JobScheduler; +import android.app.people.IPeopleManager; +import android.content.Context; +import android.content.SharedPreferences; +import android.content.pm.PackageInfo; +import android.content.pm.PackageManager; +import android.net.Uri; +import android.os.RemoteException; +import android.preference.PreferenceManager; +import android.testing.AndroidTestingRunner; + +import androidx.test.filters.SmallTest; + +import com.android.systemui.SysuiTestCase; +import com.android.systemui.people.widget.PeopleTileKey; + +import org.junit.After; +import org.junit.Before; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.mockito.Mock; +import org.mockito.MockitoAnnotations; + +import java.time.Duration; +import java.util.Arrays; +import java.util.HashMap; +import java.util.HashSet; +import java.util.Map; +import java.util.Set; + +@RunWith(AndroidTestingRunner.class) +@SmallTest +public class PeopleBackupFollowUpJobTest extends SysuiTestCase { + private static final String SHORTCUT_ID_1 = "101"; + private static final String PACKAGE_NAME_1 = "package_name"; + private static final int USER_ID_1 = 0; + + private static final PeopleTileKey PEOPLE_TILE_KEY = + new PeopleTileKey(SHORTCUT_ID_1, USER_ID_1, PACKAGE_NAME_1); + + private static final String WIDGET_ID_STRING = "3"; + private static final String SECOND_WIDGET_ID_STRING = "12"; + private static final Set<String> WIDGET_IDS = new HashSet<>( + Arrays.asList(WIDGET_ID_STRING, SECOND_WIDGET_ID_STRING)); + + private static final Uri URI = Uri.parse("fake_uri"); + + @Mock + private PackageManager mPackageManager; + @Mock + private PackageInfo mPackageInfo; + @Mock + private IPeopleManager mIPeopleManager; + @Mock + private JobScheduler mJobScheduler; + + private final SharedPreferences mSp = PreferenceManager.getDefaultSharedPreferences(mContext); + private final SharedPreferences.Editor mEditor = mSp.edit(); + private final SharedPreferences mFollowUpSp = mContext.getSharedPreferences( + SHARED_FOLLOW_UP, Context.MODE_PRIVATE); + private final SharedPreferences.Editor mFollowUpEditor = mFollowUpSp.edit(); + private final SharedPreferences mWidgetIdSp = mContext.getSharedPreferences( + WIDGET_ID_STRING, Context.MODE_PRIVATE); + private final SharedPreferences mSecondWidgetIdSp = mContext.getSharedPreferences( + SECOND_WIDGET_ID_STRING, Context.MODE_PRIVATE); + + private PeopleBackupFollowUpJob mPeopleBackupFollowUpJob; + + @Before + public void setUp() throws Exception { + MockitoAnnotations.initMocks(this); + + when(mPackageManager.getPackageInfoAsUser(any(), anyInt(), anyInt())) + .thenReturn(mPackageInfo); + when(mIPeopleManager.isConversation(any(), anyInt(), any())).thenReturn(true); + + mPeopleBackupFollowUpJob = new PeopleBackupFollowUpJob(); + mPeopleBackupFollowUpJob.setManagers( + mContext, mPackageManager, mIPeopleManager, mJobScheduler); + } + + @After + public void tearDown() { + mEditor.clear().commit(); + mFollowUpEditor.clear().commit(); + mWidgetIdSp.edit().clear().commit(); + mSecondWidgetIdSp.edit().clear().commit(); + } + + @Test + public void testProcessFollowUpFile_shouldFollowUp() throws RemoteException { + when(mIPeopleManager.isConversation(any(), anyInt(), any())).thenReturn(false); + mFollowUpEditor.putStringSet(PEOPLE_TILE_KEY.toString(), WIDGET_IDS); + mFollowUpEditor.apply(); + + Map<String, Set<String>> remainingWidgets = + mPeopleBackupFollowUpJob.processFollowUpFile(mFollowUpSp, mFollowUpEditor); + mEditor.apply(); + mFollowUpEditor.apply(); + + assertThat(remainingWidgets.size()).isEqualTo(1); + assertThat(remainingWidgets.get(PEOPLE_TILE_KEY.toString())) + .containsExactly(WIDGET_ID_STRING, SECOND_WIDGET_ID_STRING); + assertThat(mFollowUpSp.getStringSet(PEOPLE_TILE_KEY.toString(), new HashSet<>())) + .containsExactly(WIDGET_ID_STRING, SECOND_WIDGET_ID_STRING); + } + + @Test + public void testProcessFollowUpFile_shouldRestore() { + mFollowUpEditor.putStringSet(PEOPLE_TILE_KEY.toString(), WIDGET_IDS); + mFollowUpEditor.apply(); + + Map<String, Set<String>> remainingWidgets = + mPeopleBackupFollowUpJob.processFollowUpFile(mFollowUpSp, mFollowUpEditor); + mEditor.apply(); + mFollowUpEditor.apply(); + + assertThat(remainingWidgets).isEmpty(); + assertThat(mFollowUpSp.getStringSet(PEOPLE_TILE_KEY.toString(), new HashSet<>())).isEmpty(); + } + + @Test + public void testShouldCancelJob_noRemainingWidgets_shouldCancel() { + assertThat(mPeopleBackupFollowUpJob.shouldCancelJob( + new HashMap<>(), 10, Duration.ofMinutes(1).toMillis())).isTrue(); + } + + @Test + public void testShouldCancelJob_noRemainingWidgets_longTimeElapsed_shouldCancel() { + assertThat(mPeopleBackupFollowUpJob.shouldCancelJob( + new HashMap<>(), 10, Duration.ofHours(25).toMillis())).isTrue(); + } + + @Test + public void testShouldCancelJob_remainingWidgets_shortTimeElapsed_shouldNotCancel() { + Map<String, Set<String>> remainingWidgets = new HashMap<>(); + remainingWidgets.put(PEOPLE_TILE_KEY.toString(), WIDGET_IDS); + assertThat(mPeopleBackupFollowUpJob.shouldCancelJob(remainingWidgets, 10, 1000)).isFalse(); + } + + @Test + public void testShouldCancelJob_remainingWidgets_longTimeElapsed_shouldCancel() { + Map<String, Set<String>> remainingWidgets = new HashMap<>(); + remainingWidgets.put(PEOPLE_TILE_KEY.toString(), WIDGET_IDS); + assertThat(mPeopleBackupFollowUpJob.shouldCancelJob( + remainingWidgets, 10, 1000 * 60 * 60 * 25)).isTrue(); + } + + @Test + public void testCancelJobAndClearRemainingWidgets() { + SharedPreferencesHelper.setPeopleTileKey(mWidgetIdSp, PEOPLE_TILE_KEY); + SharedPreferencesHelper.setPeopleTileKey(mSecondWidgetIdSp, PEOPLE_TILE_KEY); + mEditor.putStringSet(URI.toString(), WIDGET_IDS); + mEditor.putString(WIDGET_ID_STRING, URI.toString()); + mEditor.putString(SECOND_WIDGET_ID_STRING, URI.toString()); + mEditor.apply(); + Map<String, Set<String>> remainingWidgets = new HashMap<>(); + remainingWidgets.put(PEOPLE_TILE_KEY.toString(), WIDGET_IDS); + + mPeopleBackupFollowUpJob.cancelJobAndClearRemainingWidgets( + remainingWidgets, mFollowUpEditor, mSp); + mEditor.apply(); + mFollowUpEditor.apply(); + + verify(mJobScheduler, times(1)).cancel(anyInt()); + assertThat(mFollowUpSp.getAll()).isEmpty(); + assertThat(mWidgetIdSp.getAll()).isEmpty(); + assertThat(mSecondWidgetIdSp.getAll()).isEmpty(); + assertThat(mSp.getStringSet(PEOPLE_TILE_KEY.toString(), new HashSet<>())).isEmpty(); + assertThat(mSp.getStringSet(URI.toString(), new HashSet<>())).isEmpty(); + assertThat(mSp.getString(WIDGET_ID_STRING, null)).isNull(); + assertThat(mSp.getString(SECOND_WIDGET_ID_STRING, null)).isNull(); + } +} diff --git a/packages/SystemUI/tests/src/com/android/systemui/people/PeopleSpaceUtilsTest.java b/packages/SystemUI/tests/src/com/android/systemui/people/PeopleSpaceUtilsTest.java index 33c7a571ce27..fba19861b006 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/people/PeopleSpaceUtilsTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/people/PeopleSpaceUtilsTest.java @@ -33,6 +33,7 @@ import static org.mockito.Mockito.when; import android.app.INotificationManager; import android.app.Notification; import android.app.Person; +import android.app.backup.BackupManager; import android.app.people.IPeopleManager; import android.app.people.PeopleSpaceTile; import android.appwidget.AppWidgetManager; @@ -198,6 +199,8 @@ public class PeopleSpaceUtilsTest extends SysuiTestCase { private NotificationEntryManager mNotificationEntryManager; @Mock private PeopleSpaceWidgetManager mPeopleSpaceWidgetManager; + @Mock + private BackupManager mBackupManager; private Bundle mOptions; @@ -252,7 +255,7 @@ public class PeopleSpaceUtilsTest extends SysuiTestCase { PeopleTileKey key = new PeopleTileKey(tile); PeopleSpaceTile actual = PeopleSpaceUtils .augmentTileFromNotification(mContext, tile, key, mNotificationEntry1, 0, - Optional.empty()); + Optional.empty(), mBackupManager); assertThat(actual.getNotificationContent().toString()).isEqualTo(NOTIFICATION_TEXT_2); assertThat(actual.getNotificationSender()).isEqualTo(null); @@ -292,7 +295,7 @@ public class PeopleSpaceUtilsTest extends SysuiTestCase { PeopleTileKey key = new PeopleTileKey(tile); PeopleSpaceTile actual = PeopleSpaceUtils .augmentTileFromNotification(mContext, tile, key, notificationEntry, 0, - Optional.empty()); + Optional.empty(), mBackupManager); assertThat(actual.getNotificationContent().toString()).isEqualTo(NOTIFICATION_TEXT_2); assertThat(actual.getNotificationSender().toString()).isEqualTo("name"); @@ -325,7 +328,7 @@ public class PeopleSpaceUtilsTest extends SysuiTestCase { PeopleTileKey key = new PeopleTileKey(tile); PeopleSpaceTile actual = PeopleSpaceUtils .augmentTileFromNotification(mContext, tile, key, notificationEntry, 0, - Optional.empty()); + Optional.empty(), mBackupManager); assertThat(actual.getNotificationContent().toString()).isEqualTo(NOTIFICATION_TEXT_1); assertThat(actual.getNotificationDataUri()).isEqualTo(URI); @@ -358,7 +361,7 @@ public class PeopleSpaceUtilsTest extends SysuiTestCase { PeopleTileKey key = new PeopleTileKey(tile); PeopleSpaceTile actual = PeopleSpaceUtils .augmentTileFromNotification(mContext, tile, key, notificationEntry, 0, - Optional.empty()); + Optional.empty(), mBackupManager); assertThat(actual.getNotificationContent().toString()).isEqualTo(NOTIFICATION_TEXT_1); assertThat(actual.getNotificationDataUri()).isNull(); @@ -376,7 +379,7 @@ public class PeopleSpaceUtilsTest extends SysuiTestCase { PeopleTileKey key = new PeopleTileKey(tile); PeopleSpaceTile actual = PeopleSpaceUtils .augmentTileFromNotification(mContext, tile, key, mNotificationEntry3, 0, - Optional.empty()); + Optional.empty(), mBackupManager); assertThat(actual.getNotificationContent()).isEqualTo(null); } diff --git a/packages/SystemUI/tests/src/com/android/systemui/people/SharedPreferencesHelperTest.java b/packages/SystemUI/tests/src/com/android/systemui/people/SharedPreferencesHelperTest.java new file mode 100644 index 000000000000..7cd5e22a9b97 --- /dev/null +++ b/packages/SystemUI/tests/src/com/android/systemui/people/SharedPreferencesHelperTest.java @@ -0,0 +1,100 @@ +/* + * Copyright (C) 2021 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.android.systemui.people; + +import static com.android.systemui.people.PeopleSpaceUtils.INVALID_USER_ID; +import static com.android.systemui.people.PeopleSpaceUtils.PACKAGE_NAME; +import static com.android.systemui.people.PeopleSpaceUtils.SHORTCUT_ID; +import static com.android.systemui.people.PeopleSpaceUtils.USER_ID; + +import static com.google.common.truth.Truth.assertThat; + +import android.content.Context; +import android.content.SharedPreferences; +import android.testing.AndroidTestingRunner; + +import androidx.test.filters.SmallTest; + +import com.android.systemui.SysuiTestCase; +import com.android.systemui.people.widget.PeopleTileKey; + +import org.junit.Test; +import org.junit.runner.RunWith; + +@RunWith(AndroidTestingRunner.class) +@SmallTest +public class SharedPreferencesHelperTest extends SysuiTestCase { + private static final String SHORTCUT_ID_1 = "101"; + private static final String PACKAGE_NAME_1 = "package_name"; + private static final int USER_ID_1 = 0; + + private static final PeopleTileKey PEOPLE_TILE_KEY = + new PeopleTileKey(SHORTCUT_ID_1, USER_ID_1, PACKAGE_NAME_1); + + private static final int WIDGET_ID = 1; + + private void setStorageForTile(PeopleTileKey peopleTileKey, int widgetId) { + SharedPreferences widgetSp = mContext.getSharedPreferences( + String.valueOf(widgetId), + Context.MODE_PRIVATE); + SharedPreferences.Editor widgetEditor = widgetSp.edit(); + widgetEditor.putString(PeopleSpaceUtils.PACKAGE_NAME, peopleTileKey.getPackageName()); + widgetEditor.putString(PeopleSpaceUtils.SHORTCUT_ID, peopleTileKey.getShortcutId()); + widgetEditor.putInt(PeopleSpaceUtils.USER_ID, peopleTileKey.getUserId()); + widgetEditor.apply(); + } + + @Test + public void testGetPeopleTileKey() { + setStorageForTile(PEOPLE_TILE_KEY, WIDGET_ID); + + SharedPreferences sp = mContext.getSharedPreferences( + String.valueOf(WIDGET_ID), + Context.MODE_PRIVATE); + PeopleTileKey actual = SharedPreferencesHelper.getPeopleTileKey(sp); + + assertThat(actual.getPackageName()).isEqualTo(PACKAGE_NAME_1); + assertThat(actual.getShortcutId()).isEqualTo(SHORTCUT_ID_1); + assertThat(actual.getUserId()).isEqualTo(USER_ID_1); + } + + @Test + public void testSetPeopleTileKey() { + SharedPreferences sp = mContext.getSharedPreferences( + String.valueOf(WIDGET_ID), + Context.MODE_PRIVATE); + SharedPreferencesHelper.setPeopleTileKey(sp, PEOPLE_TILE_KEY); + + assertThat(sp.getString(SHORTCUT_ID, null)).isEqualTo(SHORTCUT_ID_1); + assertThat(sp.getString(PACKAGE_NAME, null)).isEqualTo(PACKAGE_NAME_1); + assertThat(sp.getInt(USER_ID, INVALID_USER_ID)).isEqualTo(USER_ID_1); + } + + @Test + public void testClear() { + setStorageForTile(PEOPLE_TILE_KEY, WIDGET_ID); + + SharedPreferences sp = mContext.getSharedPreferences( + String.valueOf(WIDGET_ID), + Context.MODE_PRIVATE); + SharedPreferencesHelper.clear(sp); + + assertThat(sp.getString(SHORTCUT_ID, null)).isEqualTo(null); + assertThat(sp.getString(PACKAGE_NAME, null)).isEqualTo(null); + assertThat(sp.getInt(USER_ID, INVALID_USER_ID)).isEqualTo(INVALID_USER_ID); + + } +} diff --git a/packages/SystemUI/tests/src/com/android/systemui/people/widget/LaunchConversationActivityTest.java b/packages/SystemUI/tests/src/com/android/systemui/people/widget/LaunchConversationActivityTest.java index f6264ffc6a70..d8ba164851ed 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/people/widget/LaunchConversationActivityTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/people/widget/LaunchConversationActivityTest.java @@ -49,7 +49,6 @@ import com.android.systemui.wmshell.BubblesManager; import com.android.wm.shell.bubbles.Bubble; import org.junit.Before; -import org.junit.Ignore; import org.junit.Test; import org.junit.runner.RunWith; import org.mockito.ArgumentCaptor; @@ -92,20 +91,23 @@ public class LaunchConversationActivityTest extends SysuiTestCase { private NotificationListenerService.Ranking mRanking; @Mock private UserManager mUserManager; - + @Mock private CommandQueue mCommandQueue; @Captor private ArgumentCaptor<NotificationVisibility> mNotificationVisibilityCaptor; + @Captor + private ArgumentCaptor<CommandQueue.Callbacks> mCallbacksCaptor; private Intent mIntent; @Before public void setUp() throws Exception { MockitoAnnotations.initMocks(this); - mCommandQueue = new CommandQueue(mContext); mActivity = new LaunchConversationActivity(mNotificationEntryManager, Optional.of(mBubblesManager), mUserManager, mCommandQueue); + verify(mCommandQueue, times(1)).addCallback(mCallbacksCaptor.capture()); + mActivity.setIsForTesting(true, mIStatusBarService); mIntent = new Intent(); mIntent.putExtra(PeopleSpaceWidgetProvider.EXTRA_TILE_ID, "tile ID"); @@ -169,7 +171,7 @@ public class LaunchConversationActivityTest extends SysuiTestCase { mActivity.onCreate(new Bundle()); assertThat(mActivity.isFinishing()).isTrue(); - mCommandQueue.appTransitionFinished(DEFAULT_DISPLAY); + mCallbacksCaptor.getValue().appTransitionFinished(DEFAULT_DISPLAY); verify(mIStatusBarService, times(1)).onNotificationClear(any(), anyInt(), any(), anyInt(), anyInt(), mNotificationVisibilityCaptor.capture()); @@ -183,17 +185,20 @@ public class LaunchConversationActivityTest extends SysuiTestCase { @Test public void testBubbleEntryOpensBubbleAndDoesNotClearNotification() throws Exception { + when(mBubblesManager.getBubbleWithShortcutId(any())).thenReturn(null); mIntent.putExtra(PeopleSpaceWidgetProvider.EXTRA_NOTIFICATION_KEY, NOTIF_KEY_CAN_BUBBLE); mActivity.setIntent(mIntent); mActivity.onCreate(new Bundle()); assertThat(mActivity.isFinishing()).isTrue(); - mCommandQueue.appTransitionFinished(DEFAULT_DISPLAY); + mCallbacksCaptor.getValue().appTransitionFinished(DEFAULT_DISPLAY); // Don't clear the notification for bubbles. verify(mIStatusBarService, never()).onNotificationClear(any(), anyInt(), any(), anyInt(), anyInt(), any()); + // Select the bubble. + verify(mBubblesManager, times(1)).getBubbleWithShortcutId(any()); verify(mBubblesManager, times(1)).expandStackAndSelectBubble(eq(mNotifEntryCanBubble)); } @@ -214,7 +219,7 @@ public class LaunchConversationActivityTest extends SysuiTestCase { verify(mBubblesManager, never()).expandStackAndSelectBubble(any(NotificationEntry.class)); } - @Ignore + @Test public void testBubbleWithNoNotifOpensBubble() throws Exception { Bubble bubble = mock(Bubble.class); @@ -226,7 +231,7 @@ public class LaunchConversationActivityTest extends SysuiTestCase { mActivity.onCreate(new Bundle()); assertThat(mActivity.isFinishing()).isTrue(); - mCommandQueue.appTransitionFinished(DEFAULT_DISPLAY); + mCallbacksCaptor.getValue().appTransitionFinished(DEFAULT_DISPLAY); verify(mBubblesManager, times(1)).expandStackAndSelectBubble(eq(bubble)); } diff --git a/packages/SystemUI/tests/src/com/android/systemui/people/widget/PeopleBackupHelperTest.java b/packages/SystemUI/tests/src/com/android/systemui/people/widget/PeopleBackupHelperTest.java new file mode 100644 index 000000000000..5d526e102b91 --- /dev/null +++ b/packages/SystemUI/tests/src/com/android/systemui/people/widget/PeopleBackupHelperTest.java @@ -0,0 +1,537 @@ +/* + * Copyright (C) 2021 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.android.systemui.people.widget; + +import static com.android.systemui.people.PeopleBackupFollowUpJob.SHARED_FOLLOW_UP; +import static com.android.systemui.people.PeopleSpaceUtils.INVALID_USER_ID; +import static com.android.systemui.people.widget.PeopleBackupHelper.ADD_USER_ID_TO_URI; +import static com.android.systemui.people.widget.PeopleBackupHelper.SHARED_BACKUP; +import static com.android.systemui.people.widget.PeopleBackupHelper.SharedFileEntryType; +import static com.android.systemui.people.widget.PeopleBackupHelper.getEntryType; + +import static com.google.common.truth.Truth.assertThat; + +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.ArgumentMatchers.anyInt; +import static org.mockito.Mockito.when; + +import android.app.people.IPeopleManager; +import android.content.Context; +import android.content.SharedPreferences; +import android.content.pm.PackageInfo; +import android.content.pm.PackageManager; +import android.os.RemoteException; +import android.os.UserHandle; +import android.preference.PreferenceManager; +import android.testing.AndroidTestingRunner; + +import androidx.test.filters.SmallTest; + +import com.android.systemui.SysuiTestCase; +import com.android.systemui.people.SharedPreferencesHelper; + +import org.junit.After; +import org.junit.Before; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.mockito.Mock; +import org.mockito.MockitoAnnotations; + +import java.util.AbstractMap; +import java.util.Arrays; +import java.util.Collections; +import java.util.HashSet; +import java.util.Map; +import java.util.Set; + +@RunWith(AndroidTestingRunner.class) +@SmallTest +public class PeopleBackupHelperTest extends SysuiTestCase { + private static final String SHORTCUT_ID_1 = "101"; + private static final String PACKAGE_NAME_1 = "package_name"; + private static final int USER_ID_0 = 0; + private static final int USER_ID_10 = 10; + + private static final PeopleTileKey PEOPLE_TILE_KEY = + new PeopleTileKey(SHORTCUT_ID_1, USER_ID_0, PACKAGE_NAME_1); + private static final PeopleTileKey OTHER_PEOPLE_TILE_KEY = + new PeopleTileKey(SHORTCUT_ID_1, USER_ID_10, PACKAGE_NAME_1); + private static final PeopleTileKey INVALID_USER_ID_PEOPLE_TILE_KEY = + new PeopleTileKey(SHORTCUT_ID_1, INVALID_USER_ID, PACKAGE_NAME_1); + + private static final String WIDGET_ID_STRING = "3"; + private static final String SECOND_WIDGET_ID_STRING = "12"; + private static final String OTHER_WIDGET_ID_STRING = "7"; + private static final Set<String> WIDGET_IDS = new HashSet<>( + Arrays.asList(WIDGET_ID_STRING, SECOND_WIDGET_ID_STRING)); + + private static final String URI_STRING = "content://mms"; + private static final String URI_WITH_USER_ID_0 = "content://0@mms"; + private static final String URI_WITH_USER_ID_10 = "content://10@mms"; + + private final SharedPreferences mBackupSp = mContext.getSharedPreferences( + SHARED_BACKUP, Context.MODE_PRIVATE); + private final SharedPreferences.Editor mBackupEditor = mBackupSp.edit(); + private final SharedPreferences mSp = PreferenceManager.getDefaultSharedPreferences(mContext); + private final SharedPreferences.Editor mEditor = mSp.edit(); + private final SharedPreferences mFollowUpSp = mContext.getSharedPreferences( + SHARED_FOLLOW_UP, Context.MODE_PRIVATE); + private final SharedPreferences.Editor mFollowUpEditor = mFollowUpSp.edit(); + private final SharedPreferences mWidgetIdSp = mContext.getSharedPreferences( + WIDGET_ID_STRING, Context.MODE_PRIVATE); + private final SharedPreferences mSecondWidgetIdSp = mContext.getSharedPreferences( + SECOND_WIDGET_ID_STRING, Context.MODE_PRIVATE); + + @Mock + private PackageManager mPackageManager; + @Mock + private PackageInfo mPackageInfo; + @Mock + private IPeopleManager mIPeopleManager; + + private PeopleBackupHelper mHelper; + private PeopleBackupHelper mOtherHelper; + + @Before + public void setUp() throws Exception { + MockitoAnnotations.initMocks(this); + mHelper = new PeopleBackupHelper(mContext, + UserHandle.of(0), new String[]{SHARED_BACKUP}, mPackageManager, mIPeopleManager); + mOtherHelper = new PeopleBackupHelper(mContext, + UserHandle.of(10), new String[]{SHARED_BACKUP}, mPackageManager, mIPeopleManager); + + when(mPackageManager.getPackageInfoAsUser(any(), anyInt(), anyInt())) + .thenReturn(mPackageInfo); + when(mIPeopleManager.isConversation(any(), anyInt(), any())).thenReturn(true); + } + + @After + public void tearDown() { + mBackupEditor.clear().commit(); + mEditor.clear().commit(); + mFollowUpEditor.clear().commit(); + mWidgetIdSp.edit().clear().commit(); + mSecondWidgetIdSp.edit().clear().commit(); + } + + @Test + public void testGetKeyType_widgetId() { + Map.Entry<String, ?> entry = new AbstractMap.SimpleEntry<>(WIDGET_ID_STRING, "contact"); + assertThat(getEntryType(entry)).isEqualTo(SharedFileEntryType.WIDGET_ID); + } + + @Test + public void testGetKeyType_widgetId_twoDigits() { + Map.Entry<String, ?> entry = new AbstractMap.SimpleEntry<>( + SECOND_WIDGET_ID_STRING, URI_STRING); + assertThat(getEntryType(entry)).isEqualTo(SharedFileEntryType.WIDGET_ID); + } + + @Test + public void testGetKeyType_peopleTileKey_valid() { + Map.Entry<String, ?> entry = new AbstractMap.SimpleEntry<>( + "shortcut_id/12/com.android.systemui", WIDGET_IDS); + assertThat(getEntryType(entry)).isEqualTo(SharedFileEntryType.PEOPLE_TILE_KEY); + } + + @Test + public void testGetKeyType_peopleTileKey_validWithSlashes() { + Map.Entry<String, ?> entry = new AbstractMap.SimpleEntry<>( + "shortcut_id/with/slashes/12/com.android.systemui2", WIDGET_IDS); + assertThat(getEntryType(entry)).isEqualTo(SharedFileEntryType.PEOPLE_TILE_KEY); + } + + @Test + public void testGetKeyType_peopleTileKey_negativeNumber() { + Map.Entry<String, ?> entry = new AbstractMap.SimpleEntry<>( + "shortcut_id/with/slashes/-1/com.android.systemui2", WIDGET_IDS); + assertThat(getEntryType(entry)).isEqualTo(SharedFileEntryType.PEOPLE_TILE_KEY); + } + + @Test + public void testGetKeyType_contactUri() { + Map.Entry<String, ?> entry = new AbstractMap.SimpleEntry<>( + "shortcut_id/1f/com.android.systemui2", WIDGET_IDS); + assertThat(getEntryType(entry)).isEqualTo(SharedFileEntryType.CONTACT_URI); + } + + @Test + public void testGetKeyType_contactUri_valid() { + Map.Entry<String, ?> entry = new AbstractMap.SimpleEntry<>( + "http://content.fake", WIDGET_IDS); + assertThat(getEntryType(entry)).isEqualTo(SharedFileEntryType.CONTACT_URI); + } + + @Test + public void testGetKeyType_contactUri_invalidPackageName() { + Map.Entry<String, ?> entry = new AbstractMap.SimpleEntry<>( + "shortcut_id/with/slashes/12/2r/com.android.systemui2", WIDGET_IDS); + assertThat(getEntryType(entry)).isEqualTo(SharedFileEntryType.CONTACT_URI); + } + + @Test + public void testGetKeyType_unknown_unexpectedValueForPeopleTileKey() { + Map.Entry<String, ?> entry = new AbstractMap.SimpleEntry<>( + "shortcut_id/12/com.android.systemui", URI_STRING); + assertThat(getEntryType(entry)).isEqualTo(SharedFileEntryType.UNKNOWN); + } + + @Test + public void testGetKeyType_unknown_unexpectedValueForContactUri() { + Map.Entry<String, ?> entry = new AbstractMap.SimpleEntry<>( + URI_STRING, "12"); + assertThat(getEntryType(entry)).isEqualTo(SharedFileEntryType.UNKNOWN); + } + + @Test + public void testGetKeyType_unknown() { + Map.Entry<String, ?> entry = new AbstractMap.SimpleEntry<>( + null, WIDGET_IDS); + assertThat(getEntryType(entry)).isEqualTo(SharedFileEntryType.UNKNOWN); + } + + @Test + public void testBackupKey_widgetIdKey_containsWidget_noUserIdInUri() { + Map.Entry<String, ?> entry = new AbstractMap.SimpleEntry<>(WIDGET_ID_STRING, URI_STRING); + + mHelper.backupKey(entry, mBackupEditor, Collections.singletonList(WIDGET_ID_STRING)); + mBackupEditor.apply(); + + assertThat(mBackupSp.getString(WIDGET_ID_STRING, null)).isEqualTo(URI_STRING); + } + + @Test + public void testBackupKey_widgetIdKey_doesNotContainWidget_noUserIdInUri() { + Map.Entry<String, ?> entry = new AbstractMap.SimpleEntry<>(WIDGET_ID_STRING, URI_STRING); + + mHelper.backupKey(entry, mBackupEditor, Collections.singletonList(OTHER_WIDGET_ID_STRING)); + mBackupEditor.apply(); + + assertThat(mBackupSp.getString(WIDGET_ID_STRING, null)).isNull(); + } + + @Test + public void testBackupKey_widgetIdKey_containsOneWidget_differentUserIdInUri() { + Map.Entry<String, ?> entry = new AbstractMap.SimpleEntry<>(WIDGET_ID_STRING, + URI_WITH_USER_ID_10); + + mHelper.backupKey(entry, mBackupEditor, Collections.singletonList(WIDGET_ID_STRING)); + mBackupEditor.apply(); + + assertThat(mBackupSp.getString(WIDGET_ID_STRING, null)).isEqualTo(URI_STRING); + assertThat(mBackupSp.getInt(ADD_USER_ID_TO_URI + WIDGET_ID_STRING, INVALID_USER_ID)) + .isEqualTo(USER_ID_10); + } + + @Test + public void testBackupKey_widgetIdKey_containsWidget_SameUserIdInUri() { + Map.Entry<String, ?> entry = new AbstractMap.SimpleEntry<>( + WIDGET_ID_STRING, URI_WITH_USER_ID_10); + + mOtherHelper.backupKey(entry, mBackupEditor, Collections.singletonList(WIDGET_ID_STRING)); + mBackupEditor.apply(); + + assertThat(mBackupSp.getString(WIDGET_ID_STRING, null)).isEqualTo(URI_STRING); + assertThat(mBackupSp.getInt(ADD_USER_ID_TO_URI + WIDGET_ID_STRING, INVALID_USER_ID)) + .isEqualTo(USER_ID_10); + } + + @Test + public void testBackupKey_contactUriKey_ignoresExistingWidgets() { + Map.Entry<String, ?> entry = new AbstractMap.SimpleEntry<>(URI_STRING, WIDGET_IDS); + + mHelper.backupKey(entry, mBackupEditor, Collections.singletonList(WIDGET_ID_STRING)); + mBackupEditor.apply(); + + assertThat(mBackupSp.getStringSet(URI_STRING, new HashSet<>())) + .containsExactly(WIDGET_ID_STRING, SECOND_WIDGET_ID_STRING); + } + + @Test + public void testBackupKey_contactUriKey_ignoresExistingWidgets_otherWidget() { + Map.Entry<String, ?> entry = new AbstractMap.SimpleEntry<>(URI_STRING, WIDGET_IDS); + + mHelper.backupKey(entry, mBackupEditor, Collections.singletonList(WIDGET_ID_STRING)); + mBackupEditor.apply(); + + assertThat(mBackupSp.getStringSet(URI_STRING, new HashSet<>())) + .containsExactly(WIDGET_ID_STRING, SECOND_WIDGET_ID_STRING); + } + + @Test + public void testBackupKey_contactUriKey_noUserId_otherUser_doesntBackup() { + Map.Entry<String, ?> entry = new AbstractMap.SimpleEntry<>(URI_STRING, WIDGET_IDS); + + mOtherHelper.backupKey(entry, mBackupEditor, Collections.singletonList(WIDGET_ID_STRING)); + mBackupEditor.apply(); + + assertThat(mBackupSp.getStringSet(URI_STRING, new HashSet<>())).isEmpty(); + } + + @Test + public void testBackupKey_contactUriKey_sameUserId() { + Map.Entry<String, ?> entry = new AbstractMap.SimpleEntry<>(URI_WITH_USER_ID_10, WIDGET_IDS); + + mOtherHelper.backupKey(entry, mBackupEditor, Collections.singletonList(WIDGET_ID_STRING)); + mBackupEditor.apply(); + + assertThat(mBackupSp.getStringSet(URI_STRING, new HashSet<>())) + .containsExactly(WIDGET_ID_STRING, SECOND_WIDGET_ID_STRING); + assertThat(mBackupSp.getInt(ADD_USER_ID_TO_URI + URI_STRING, INVALID_USER_ID)) + .isEqualTo(USER_ID_10); + } + + @Test + public void testBackupKey_contactUriKey_differentUserId_runningAsUser0() { + Map.Entry<String, ?> entry = new AbstractMap.SimpleEntry<>(URI_WITH_USER_ID_10, WIDGET_IDS); + + mHelper.backupKey(entry, mBackupEditor, Collections.singletonList(WIDGET_ID_STRING)); + mBackupEditor.apply(); + + assertThat(mBackupSp.getStringSet(URI_STRING, new HashSet<>())).isEmpty(); + assertThat(mBackupSp.getInt(ADD_USER_ID_TO_URI + URI_STRING, INVALID_USER_ID)) + .isEqualTo(INVALID_USER_ID); + } + + @Test + public void testBackupKey_contactUriKey_differentUserId_runningAsUser10() { + Map.Entry<String, ?> entry = new AbstractMap.SimpleEntry<>(URI_WITH_USER_ID_0, WIDGET_IDS); + + mOtherHelper.backupKey(entry, mBackupEditor, Collections.singletonList(WIDGET_ID_STRING)); + mBackupEditor.apply(); + + assertThat(mBackupSp.getStringSet(URI_STRING, new HashSet<>())).isEmpty(); + assertThat(mBackupSp.getInt(ADD_USER_ID_TO_URI + URI_STRING, INVALID_USER_ID)) + .isEqualTo(INVALID_USER_ID); + } + + @Test + public void testBackupKey_peopleTileKey_containsWidget() { + Map.Entry<String, ?> entry = new AbstractMap.SimpleEntry<>( + PEOPLE_TILE_KEY.toString(), WIDGET_IDS); + + mHelper.backupKey(entry, mBackupEditor, Collections.singletonList(WIDGET_ID_STRING)); + mBackupEditor.apply(); + + assertThat(mBackupSp.getStringSet( + INVALID_USER_ID_PEOPLE_TILE_KEY.toString(), new HashSet<>())) + .containsExactly(WIDGET_ID_STRING); + } + + @Test + public void testBackupKey_peopleTileKey_containsBothWidgets() { + Map.Entry<String, ?> entry = new AbstractMap.SimpleEntry<>( + PEOPLE_TILE_KEY.toString(), WIDGET_IDS); + + mHelper.backupKey(entry, mBackupEditor, + Arrays.asList(WIDGET_ID_STRING, SECOND_WIDGET_ID_STRING)); + mBackupEditor.apply(); + + assertThat( + mBackupSp.getStringSet(INVALID_USER_ID_PEOPLE_TILE_KEY.toString(), new HashSet<>())) + .containsExactly(WIDGET_ID_STRING, SECOND_WIDGET_ID_STRING); + } + + @Test + public void testBackupKey_peopleTileKey_doesNotContainWidget() { + Map.Entry<String, ?> entry = new AbstractMap.SimpleEntry<>( + PEOPLE_TILE_KEY.toString(), WIDGET_IDS); + + mHelper.backupKey(entry, mBackupEditor, Collections.singletonList(OTHER_WIDGET_ID_STRING)); + mBackupEditor.apply(); + + assertThat(mBackupSp.getStringSet( + INVALID_USER_ID_PEOPLE_TILE_KEY.toString(), new HashSet<>())).isEmpty(); + } + + @Test + public void testBackupKey_peopleTileKey_differentUserId() { + Map.Entry<String, ?> entry = new AbstractMap.SimpleEntry<>( + OTHER_PEOPLE_TILE_KEY.toString(), WIDGET_IDS); + + mHelper.backupKey(entry, mBackupEditor, Collections.singletonList(WIDGET_ID_STRING)); + mBackupEditor.apply(); + + assertThat(mBackupSp.getStringSet( + INVALID_USER_ID_PEOPLE_TILE_KEY.toString(), new HashSet<>())).isEmpty(); + } + + @Test + public void testRestoreKey_widgetIdKey_noUserIdInUri() { + Map.Entry<String, ?> entry = new AbstractMap.SimpleEntry<>(WIDGET_ID_STRING, URI_STRING); + + boolean restored = mHelper.restoreKey(entry, mEditor, mFollowUpEditor, mBackupSp); + mEditor.apply(); + mFollowUpEditor.apply(); + + assertThat(restored).isTrue(); + assertThat(mSp.getString(WIDGET_ID_STRING, null)).isEqualTo(URI_STRING); + assertThat(mFollowUpSp.getAll()).isEmpty(); + } + + @Test + public void testRestoreKey_widgetIdKey_sameUserInUri() { + Map.Entry<String, ?> entry = new AbstractMap.SimpleEntry<>(WIDGET_ID_STRING, URI_STRING); + mBackupEditor.putInt(ADD_USER_ID_TO_URI + WIDGET_ID_STRING, USER_ID_0); + mBackupEditor.apply(); + + boolean restored = mHelper.restoreKey(entry, mEditor, mFollowUpEditor, mBackupSp); + mEditor.apply(); + mFollowUpEditor.apply(); + + assertThat(restored).isTrue(); + assertThat(mSp.getString(WIDGET_ID_STRING, null)).isEqualTo(URI_WITH_USER_ID_0); + assertThat(mFollowUpSp.getAll()).isEmpty(); + } + + @Test + public void testRestoreKey_widgetIdKey_differentUserInUri() { + Map.Entry<String, ?> entry = new AbstractMap.SimpleEntry<>(WIDGET_ID_STRING, URI_STRING); + mBackupEditor.putInt(ADD_USER_ID_TO_URI + WIDGET_ID_STRING, USER_ID_10); + mBackupEditor.apply(); + + boolean restored = mHelper.restoreKey(entry, mEditor, mFollowUpEditor, mBackupSp); + mEditor.apply(); + mFollowUpEditor.apply(); + + assertThat(restored).isTrue(); + assertThat(mSp.getString(WIDGET_ID_STRING, null)).isEqualTo(URI_WITH_USER_ID_10); + assertThat(mFollowUpSp.getAll()).isEmpty(); + } + + @Test + public void testRestoreKey_widgetIdKey_nonSystemUser_differentUser() { + Map.Entry<String, ?> entry = new AbstractMap.SimpleEntry<>(WIDGET_ID_STRING, URI_STRING); + mBackupEditor.putInt(ADD_USER_ID_TO_URI + WIDGET_ID_STRING, USER_ID_0); + mBackupEditor.apply(); + + boolean restored = mOtherHelper.restoreKey(entry, mEditor, mFollowUpEditor, mBackupSp); + mEditor.apply(); + mFollowUpEditor.apply(); + + assertThat(restored).isTrue(); + assertThat(mSp.getString(WIDGET_ID_STRING, null)).isEqualTo(URI_WITH_USER_ID_0); + assertThat(mFollowUpSp.getAll()).isEmpty(); + } + + @Test + public void testRestoreKey_contactUriKey_noUserIdInUri() { + Map.Entry<String, ?> entry = new AbstractMap.SimpleEntry<>(URI_STRING, WIDGET_IDS); + + boolean restored = mHelper.restoreKey(entry, mEditor, mFollowUpEditor, mBackupSp); + mEditor.apply(); + mFollowUpEditor.apply(); + + assertThat(restored).isTrue(); + assertThat(mSp.getStringSet(URI_STRING, new HashSet<>())) + .containsExactly(WIDGET_ID_STRING, SECOND_WIDGET_ID_STRING); + assertThat(mFollowUpSp.getAll()).isEmpty(); + } + + @Test + public void testRestoreKey_contactUriKey_sameUserInUri() { + Map.Entry<String, ?> entry = new AbstractMap.SimpleEntry<>(URI_STRING, WIDGET_IDS); + mBackupEditor.putInt(ADD_USER_ID_TO_URI + URI_STRING, USER_ID_0); + mBackupEditor.apply(); + + boolean restored = mHelper.restoreKey(entry, mEditor, mFollowUpEditor, mBackupSp); + mEditor.apply(); + mFollowUpEditor.apply(); + + assertThat(restored).isTrue(); + assertThat(mSp.getStringSet(URI_WITH_USER_ID_0, new HashSet<>())) + .containsExactly(WIDGET_ID_STRING, SECOND_WIDGET_ID_STRING); + assertThat(mSp.getStringSet(URI_STRING, new HashSet<>())).isEmpty(); + assertThat(mFollowUpSp.getAll()).isEmpty(); + } + + @Test + public void testRestoreKey_contactUriKey_differentUserInUri() { + Map.Entry<String, ?> entry = new AbstractMap.SimpleEntry<>(URI_STRING, WIDGET_IDS); + mBackupEditor.putInt(ADD_USER_ID_TO_URI + URI_STRING, USER_ID_10); + mBackupEditor.apply(); + + boolean restored = mHelper.restoreKey(entry, mEditor, mFollowUpEditor, mBackupSp); + mEditor.apply(); + mFollowUpEditor.apply(); + + assertThat(restored).isTrue(); + assertThat(mSp.getStringSet(URI_WITH_USER_ID_10, new HashSet<>())) + .containsExactly(WIDGET_ID_STRING, SECOND_WIDGET_ID_STRING); + assertThat(mSp.getStringSet(URI_STRING, new HashSet<>())).isEmpty(); + assertThat(mFollowUpSp.getAll()).isEmpty(); + } + + @Test + public void testRestoreKey_contactUriKey_nonSystemUser_differentUser() { + Map.Entry<String, ?> entry = new AbstractMap.SimpleEntry<>(URI_STRING, WIDGET_IDS); + mBackupEditor.putInt(ADD_USER_ID_TO_URI + URI_STRING, USER_ID_0); + mBackupEditor.apply(); + + boolean restored = mOtherHelper.restoreKey(entry, mEditor, mFollowUpEditor, mBackupSp); + mEditor.apply(); + mFollowUpEditor.apply(); + + assertThat(restored).isTrue(); + assertThat(mSp.getStringSet(URI_WITH_USER_ID_0, new HashSet<>())) + .containsExactly(WIDGET_ID_STRING, SECOND_WIDGET_ID_STRING); + assertThat(mSp.getStringSet(URI_STRING, new HashSet<>())).isEmpty(); + assertThat(mFollowUpSp.getAll()).isEmpty(); + } + + @Test + public void testRestoreKey_peopleTileKey_shouldNotFollowUp() { + Map.Entry<String, ?> entry = new AbstractMap.SimpleEntry<>( + INVALID_USER_ID_PEOPLE_TILE_KEY.toString(), WIDGET_IDS); + + boolean restored = mHelper.restoreKey(entry, mEditor, mFollowUpEditor, mBackupSp); + mEditor.apply(); + mFollowUpEditor.apply(); + + assertThat(restored).isTrue(); + assertThat(mSp.getStringSet(PEOPLE_TILE_KEY.toString(), new HashSet<>())) + .containsExactly(WIDGET_ID_STRING, SECOND_WIDGET_ID_STRING); + assertThat(SharedPreferencesHelper.getPeopleTileKey(mWidgetIdSp)) + .isEqualTo(PEOPLE_TILE_KEY); + assertThat(SharedPreferencesHelper.getPeopleTileKey(mSecondWidgetIdSp)) + .isEqualTo(PEOPLE_TILE_KEY); + assertThat(mFollowUpSp.getAll()).isEmpty(); + } + + @Test + public void testRestoreKey_peopleTileKey_shortcutNotYetRestored_shouldFollowUpBoth() + throws RemoteException { + when(mIPeopleManager.isConversation(any(), anyInt(), any())).thenReturn(false); + + Map.Entry<String, ?> entry = new AbstractMap.SimpleEntry<>( + INVALID_USER_ID_PEOPLE_TILE_KEY.toString(), WIDGET_IDS); + + boolean restored = mHelper.restoreKey(entry, mEditor, mFollowUpEditor, mBackupSp); + mEditor.apply(); + mFollowUpEditor.apply(); + + assertThat(restored).isFalse(); + assertThat(mSp.getStringSet(PEOPLE_TILE_KEY.toString(), new HashSet<>())) + .containsExactly(WIDGET_ID_STRING, SECOND_WIDGET_ID_STRING); + assertThat(SharedPreferencesHelper.getPeopleTileKey(mWidgetIdSp)) + .isEqualTo(PEOPLE_TILE_KEY); + assertThat(SharedPreferencesHelper.getPeopleTileKey(mSecondWidgetIdSp)) + .isEqualTo(PEOPLE_TILE_KEY); + + assertThat(mFollowUpSp.getStringSet(PEOPLE_TILE_KEY.toString(), new HashSet<>())) + .containsExactly(WIDGET_ID_STRING, SECOND_WIDGET_ID_STRING); + } +} diff --git a/packages/SystemUI/tests/src/com/android/systemui/people/widget/PeopleSpaceWidgetManagerTest.java b/packages/SystemUI/tests/src/com/android/systemui/people/widget/PeopleSpaceWidgetManagerTest.java index c48f26b8c853..ddad7581899b 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/people/widget/PeopleSpaceWidgetManagerTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/people/widget/PeopleSpaceWidgetManagerTest.java @@ -73,6 +73,7 @@ import android.app.Notification; import android.app.NotificationChannel; import android.app.NotificationManager; import android.app.Person; +import android.app.backup.BackupManager; import android.app.people.ConversationChannel; import android.app.people.ConversationStatus; import android.app.people.IPeopleManager; @@ -102,7 +103,9 @@ import androidx.test.filters.SmallTest; import com.android.systemui.R; import com.android.systemui.SysuiTestCase; +import com.android.systemui.people.PeopleBackupFollowUpJob; import com.android.systemui.people.PeopleSpaceUtils; +import com.android.systemui.people.SharedPreferencesHelper; import com.android.systemui.statusbar.NotificationListener; import com.android.systemui.statusbar.NotificationListener.NotificationHandler; import com.android.systemui.statusbar.SbnBuilder; @@ -151,6 +154,11 @@ public class PeopleSpaceWidgetManagerTest extends SysuiTestCase { private static final int WIDGET_ID_WITH_KEY_IN_OPTIONS = 4; private static final int WIDGET_ID_WITH_SAME_URI = 5; private static final int WIDGET_ID_WITH_DIFFERENT_URI = 6; + private static final int WIDGET_ID_8 = 8; + private static final int WIDGET_ID_9 = 9; + private static final int WIDGET_ID_11 = 11; + private static final int WIDGET_ID_14 = 14; + private static final int WIDGET_ID_15 = 15; private static final String SHORTCUT_ID = "101"; private static final String OTHER_SHORTCUT_ID = "102"; private static final String NOTIFICATION_KEY = "0|com.android.systemui.tests|0|null|0"; @@ -195,6 +203,14 @@ public class PeopleSpaceWidgetManagerTest extends SysuiTestCase { | SUPPRESSED_EFFECT_NOTIFICATION_LIST; private static final long SBN_POST_TIME = 567L; + private static final Map<String, String> WIDGETS_MAPPING = Map.of( + String.valueOf(WIDGET_ID_8), String.valueOf(WIDGET_ID_WITH_SHORTCUT), + String.valueOf(WIDGET_ID_9), String.valueOf(WIDGET_ID_WITHOUT_SHORTCUT), + String.valueOf(WIDGET_ID_11), String.valueOf(WIDGET_ID_WITH_KEY_IN_OPTIONS), + String.valueOf(WIDGET_ID_14), String.valueOf(WIDGET_ID_WITH_SAME_URI), + String.valueOf(WIDGET_ID_15), String.valueOf(WIDGET_ID_WITH_DIFFERENT_URI) + ); + private ShortcutInfo mShortcutInfo; private NotificationEntry mNotificationEntry; @@ -228,6 +244,8 @@ public class PeopleSpaceWidgetManagerTest extends SysuiTestCase { private NotificationManager.Policy mNotificationPolicy; @Mock private Bubbles mBubbles; + @Mock + private BackupManager mBackupManager; @Captor private ArgumentCaptor<NotificationHandler> mListenerCaptor; @@ -246,8 +264,8 @@ public class PeopleSpaceWidgetManagerTest extends SysuiTestCase { mDependency.injectTestDependency(NotificationEntryManager.class, mNotificationEntryManager); mManager = new PeopleSpaceWidgetManager(mContext, mAppWidgetManager, mIPeopleManager, mPeopleManager, mLauncherApps, mNotificationEntryManager, mPackageManager, - Optional.of(mBubbles), mUserManager, mINotificationManager, mNotificationManager, - mFakeExecutor); + Optional.of(mBubbles), mUserManager, mBackupManager, mINotificationManager, + mNotificationManager, mFakeExecutor); mManager.attach(mListenerService); verify(mListenerService).addNotificationHandler(mListenerCaptor.capture()); @@ -1410,6 +1428,89 @@ public class PeopleSpaceWidgetManagerTest extends SysuiTestCase { assertThat(tile.getNotificationPolicyState()).isEqualTo(expected | SHOW_CONVERSATIONS); } + @Test + public void testRemapWidgetFiles() { + setStorageForTile(SHORTCUT_ID, TEST_PACKAGE_A, WIDGET_ID_8, URI); + setStorageForTile(OTHER_SHORTCUT_ID, TEST_PACKAGE_B, WIDGET_ID_11, URI); + + mManager.remapWidgetFiles(WIDGETS_MAPPING); + + SharedPreferences sp1 = mContext.getSharedPreferences( + String.valueOf(WIDGET_ID_WITH_SHORTCUT), Context.MODE_PRIVATE); + PeopleTileKey key1 = SharedPreferencesHelper.getPeopleTileKey(sp1); + assertThat(key1.getShortcutId()).isEqualTo(SHORTCUT_ID); + assertThat(key1.getPackageName()).isEqualTo(TEST_PACKAGE_A); + + SharedPreferences sp4 = mContext.getSharedPreferences( + String.valueOf(WIDGET_ID_WITH_KEY_IN_OPTIONS), Context.MODE_PRIVATE); + PeopleTileKey key4 = SharedPreferencesHelper.getPeopleTileKey(sp4); + assertThat(key4.getShortcutId()).isEqualTo(OTHER_SHORTCUT_ID); + assertThat(key4.getPackageName()).isEqualTo(TEST_PACKAGE_B); + + SharedPreferences sp8 = mContext.getSharedPreferences( + String.valueOf(WIDGET_ID_8), Context.MODE_PRIVATE); + PeopleTileKey key8 = SharedPreferencesHelper.getPeopleTileKey(sp8); + assertThat(key8.getShortcutId()).isNull(); + assertThat(key8.getPackageName()).isNull(); + + SharedPreferences sp11 = mContext.getSharedPreferences( + String.valueOf(WIDGET_ID_11), Context.MODE_PRIVATE); + PeopleTileKey key11 = SharedPreferencesHelper.getPeopleTileKey(sp11); + assertThat(key11.getShortcutId()).isNull(); + assertThat(key11.getPackageName()).isNull(); + } + + @Test + public void testRemapSharedFile() { + setStorageForTile(SHORTCUT_ID, TEST_PACKAGE_A, WIDGET_ID_8, URI); + setStorageForTile(OTHER_SHORTCUT_ID, TEST_PACKAGE_B, WIDGET_ID_11, URI); + + mManager.remapSharedFile(WIDGETS_MAPPING); + + SharedPreferences sp = PreferenceManager.getDefaultSharedPreferences(mContext); + + assertThat(sp.getString(String.valueOf(WIDGET_ID_8), null)).isNull(); + assertThat(sp.getString(String.valueOf(WIDGET_ID_11), null)).isNull(); + assertThat(sp.getString(String.valueOf(WIDGET_ID_WITH_SHORTCUT), null)) + .isEqualTo(URI.toString()); + assertThat(sp.getString(String.valueOf(WIDGET_ID_WITH_KEY_IN_OPTIONS), null)) + .isEqualTo(URI.toString()); + + assertThat(sp.getStringSet(URI.toString(), new HashSet<>())).containsExactly( + String.valueOf(WIDGET_ID_WITH_SHORTCUT), + String.valueOf(WIDGET_ID_WITH_KEY_IN_OPTIONS)); + + PeopleTileKey key8 = new PeopleTileKey(SHORTCUT_ID, 0, TEST_PACKAGE_A); + assertThat(sp.getStringSet(key8.toString(), new HashSet<>())).containsExactly( + String.valueOf(WIDGET_ID_WITH_SHORTCUT)); + + PeopleTileKey key11 = new PeopleTileKey(OTHER_SHORTCUT_ID, 0, TEST_PACKAGE_B); + assertThat(sp.getStringSet(key11.toString(), new HashSet<>())).containsExactly( + String.valueOf(WIDGET_ID_WITH_KEY_IN_OPTIONS)); + } + + @Test + public void testRemapFollowupFile() { + PeopleTileKey key8 = new PeopleTileKey(SHORTCUT_ID, 0, TEST_PACKAGE_A); + PeopleTileKey key11 = new PeopleTileKey(OTHER_SHORTCUT_ID, 0, TEST_PACKAGE_B); + Set<String> set8 = new HashSet<>(Collections.singleton(String.valueOf(WIDGET_ID_8))); + Set<String> set11 = new HashSet<>(Collections.singleton(String.valueOf(WIDGET_ID_11))); + + SharedPreferences followUp = mContext.getSharedPreferences( + PeopleBackupFollowUpJob.SHARED_FOLLOW_UP, Context.MODE_PRIVATE); + SharedPreferences.Editor followUpEditor = followUp.edit(); + followUpEditor.putStringSet(key8.toString(), set8); + followUpEditor.putStringSet(key11.toString(), set11); + followUpEditor.apply(); + + mManager.remapFollowupFile(WIDGETS_MAPPING); + + assertThat(followUp.getStringSet(key8.toString(), new HashSet<>())).containsExactly( + String.valueOf(WIDGET_ID_WITH_SHORTCUT)); + assertThat(followUp.getStringSet(key11.toString(), new HashSet<>())).containsExactly( + String.valueOf(WIDGET_ID_WITH_KEY_IN_OPTIONS)); + } + private void setFinalField(String fieldName, int value) { try { Field field = NotificationManager.Policy.class.getDeclaredField(fieldName); diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/QSPanelControllerBaseTest.java b/packages/SystemUI/tests/src/com/android/systemui/qs/QSPanelControllerBaseTest.java index 1f066d81793a..65e5f9703d84 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/qs/QSPanelControllerBaseTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/qs/QSPanelControllerBaseTest.java @@ -127,7 +127,7 @@ public class QSPanelControllerBaseTest extends SysuiTestCase { when(mQSPanel.getDumpableTag()).thenReturn("QSPanel"); when(mQSPanel.openPanelEvent()).thenReturn(QSEvent.QS_PANEL_EXPANDED); when(mQSPanel.closePanelEvent()).thenReturn(QSEvent.QS_PANEL_COLLAPSED); - when(mQSPanel.createRegularTileLayout()).thenReturn(mPagedTileLayout); + when(mQSPanel.getOrCreateTileLayout()).thenReturn(mPagedTileLayout); when(mQSPanel.getTileLayout()).thenReturn(mPagedTileLayout); when(mQSTile.getTileSpec()).thenReturn("dnd"); when(mQSTileHost.getTiles()).thenReturn(Collections.singleton(mQSTile)); diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/QSPanelControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/qs/QSPanelControllerTest.java index 53eae8c46d2f..bf6c981bf05c 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/qs/QSPanelControllerTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/qs/QSPanelControllerTest.java @@ -107,7 +107,7 @@ public class QSPanelControllerTest extends SysuiTestCase { when(mQSPanel.isAttachedToWindow()).thenReturn(true); when(mQSPanel.getDumpableTag()).thenReturn("QSPanel"); - when(mQSPanel.createRegularTileLayout()).thenReturn(mPagedTileLayout); + when(mQSPanel.getOrCreateTileLayout()).thenReturn(mPagedTileLayout); when(mQSPanel.getTileLayout()).thenReturn(mPagedTileLayout); when(mQSTileHost.getTiles()).thenReturn(Collections.singleton(mQSTile)); when(mQSTileHost.createTileView(any(), eq(mQSTile), anyBoolean())).thenReturn(mQSTileView); diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/QuickAccessWalletTileTest.java b/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/QuickAccessWalletTileTest.java index a50cbe5adc48..b1e67f5cb7c8 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/QuickAccessWalletTileTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/QuickAccessWalletTileTest.java @@ -175,6 +175,15 @@ public class QuickAccessWalletTileTest extends SysuiTestCase { } @Test + public void testWalletFeatureUnavailable_recreateWalletClient() { + when(mQuickAccessWalletClient.isWalletFeatureAvailable()).thenReturn(false); + + mTile.handleSetListening(true); + + verify(mController, times(1)).reCreateWalletClient(); + } + + @Test public void testIsAvailable_qawFeatureAvailable() { when(mPackageManager.hasSystemFeature(FEATURE_NFC_HOST_CARD_EMULATION)).thenReturn(true); when(mPackageManager.hasSystemFeature("org.chromium.arc")).thenReturn(false); @@ -267,6 +276,41 @@ public class QuickAccessWalletTileTest extends SysuiTestCase { } @Test + public void testHandleUpdateState_walletIsUpdating() { + when(mKeyguardStateController.isUnlocked()).thenReturn(true); + QSTile.State state = new QSTile.State(); + GetWalletCardsResponse response = + new GetWalletCardsResponse( + Collections.singletonList(createWalletCard(mContext)), 0); + + mTile.handleSetListening(true); + + verify(mController).queryWalletCards(mCallbackCaptor.capture()); + + // Wallet cards fetching on its way; wallet updating. + mTile.handleUpdateState(state, null); + + assertEquals(Tile.STATE_INACTIVE, state.state); + assertEquals( + mContext.getString(R.string.wallet_secondary_label_updating), state.secondaryLabel); + assertNotNull(state.stateDescription); + assertNull(state.sideViewCustomDrawable); + + // Wallet cards fetching completed. + mCallbackCaptor.getValue().onWalletCardsRetrieved(response); + mTestableLooper.processAllMessages(); + + mTile.handleUpdateState(state, null); + + assertEquals(Tile.STATE_ACTIVE, state.state); + assertEquals( + "•••• 1234", + state.secondaryLabel); + assertNotNull(state.stateDescription); + assertNotNull(state.sideViewCustomDrawable); + } + + @Test public void testHandleUpdateState_hasCard_deviceLocked_tileInactive() { when(mKeyguardStateController.isUnlocked()).thenReturn(false); QSTile.State state = new QSTile.State(); @@ -315,7 +359,7 @@ public class QuickAccessWalletTileTest extends SysuiTestCase { } @Test - public void testHandleUpdateState_qawFeatureUnavailable_tileUnavailable() { + public void testHandleUpdateState_qawServiceUnavailable_tileUnavailable() { when(mQuickAccessWalletClient.isWalletServiceAvailable()).thenReturn(false); QSTile.State state = new QSTile.State(); @@ -327,6 +371,18 @@ public class QuickAccessWalletTileTest extends SysuiTestCase { } @Test + public void testHandleUpdateState_qawFeatureUnavailable_tileUnavailable() { + when(mQuickAccessWalletClient.isWalletFeatureAvailable()).thenReturn(false); + QSTile.State state = new QSTile.State(); + + mTile.handleUpdateState(state, null); + + assertEquals(Tile.STATE_UNAVAILABLE, state.state); + assertNull(state.stateDescription); + assertNull(state.sideViewCustomDrawable); + } + + @Test public void testHandleSetListening_queryCards() { mTile.handleSetListening(true); diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/NotificationPanelViewTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/NotificationPanelViewTest.java index 2693b949f6ff..a6fc02d7dafc 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/NotificationPanelViewTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/NotificationPanelViewTest.java @@ -100,9 +100,11 @@ import com.android.systemui.statusbar.KeyguardAffordanceView; import com.android.systemui.statusbar.KeyguardIndicationController; import com.android.systemui.statusbar.LockscreenShadeTransitionController; import com.android.systemui.statusbar.NotificationLockscreenUserManager; +import com.android.systemui.statusbar.NotificationRemoteInputManager; import com.android.systemui.statusbar.NotificationShadeDepthController; import com.android.systemui.statusbar.NotificationShelfController; import com.android.systemui.statusbar.PulseExpansionHandler; +import com.android.systemui.statusbar.RemoteInputController; import com.android.systemui.statusbar.StatusBarStateControllerImpl; import com.android.systemui.statusbar.SysuiStatusBarStateController; import com.android.systemui.statusbar.VibratorHelper; @@ -289,6 +291,10 @@ public class NotificationPanelViewTest extends SysuiTestCase { private FragmentHostManager mFragmentHostManager; @Mock private QuickAccessWalletController mQuickAccessWalletController; + @Mock + private NotificationRemoteInputManager mNotificationRemoteInputManager; + @Mock + private RemoteInputController mRemoteInputController; private SysuiStatusBarStateController mStatusBarStateController; private NotificationPanelViewController mNotificationPanelViewController; @@ -384,6 +390,8 @@ public class NotificationPanelViewTest extends SysuiTestCase { .thenReturn(mKeyguardStatusView); when(mLayoutInflater.inflate(eq(R.layout.keyguard_bottom_area), any(), anyBoolean())) .thenReturn(mKeyguardBottomArea); + when(mNotificationRemoteInputManager.getController()).thenReturn(mRemoteInputController); + when(mRemoteInputController.isRemoteInputActive()).thenReturn(false); reset(mView); @@ -427,7 +435,8 @@ public class NotificationPanelViewTest extends SysuiTestCase { mQuickAccessWalletController, new FakeExecutor(new FakeSystemClock()), mSecureSettings, - mUnlockedScreenOffAnimationController); + mUnlockedScreenOffAnimationController, + mNotificationRemoteInputManager); mNotificationPanelViewController.initDependencies( mStatusBar, mNotificationShelfController); diff --git a/packages/SystemUI/tests/src/com/android/systemui/wallet/controller/QuickAccessWalletControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/wallet/controller/QuickAccessWalletControllerTest.java index 23abce086728..ce0098e7672c 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/wallet/controller/QuickAccessWalletControllerTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/wallet/controller/QuickAccessWalletControllerTest.java @@ -21,7 +21,9 @@ import static org.junit.Assert.assertFalse; import static org.junit.Assert.assertNotSame; import static org.junit.Assert.assertSame; import static org.junit.Assert.assertTrue; +import static org.mockito.ArgumentMatchers.any; import static org.mockito.ArgumentMatchers.eq; +import static org.mockito.Mockito.never; import static org.mockito.Mockito.verify; import static org.mockito.Mockito.when; @@ -61,12 +63,10 @@ public class QuickAccessWalletControllerTest extends SysuiTestCase { private ArgumentCaptor<GetWalletCardsRequest> mRequestCaptor; private QuickAccessWalletController mController; - private TestableLooper mTestableLooper; @Before public void setUp() { MockitoAnnotations.initMocks(this); - mTestableLooper = TestableLooper.get(this); when(mQuickAccessWalletClient.isWalletServiceAvailable()).thenReturn(true); when(mQuickAccessWalletClient.isWalletFeatureAvailable()).thenReturn(true); when(mQuickAccessWalletClient.isWalletFeatureAvailableWhenDeviceLocked()).thenReturn(true); @@ -143,4 +143,13 @@ public class QuickAccessWalletControllerTest extends SysuiTestCase { mContext.getResources().getDimensionPixelSize(R.dimen.wallet_tile_card_view_height), request.getCardHeightPx()); } + + @Test + public void queryWalletCards_walletFeatureNotAvailable_noQuery() { + when(mQuickAccessWalletClient.isWalletFeatureAvailable()).thenReturn(false); + + mController.queryWalletCards(mCardsRetriever); + + verify(mQuickAccessWalletClient, never()).getWalletCards(any(), any(), any()); + } } diff --git a/services/companion/java/com/android/server/companion/CompanionDeviceManagerService.java b/services/companion/java/com/android/server/companion/CompanionDeviceManagerService.java index 906edb30ff12..9ff1b10c09ed 100644 --- a/services/companion/java/com/android/server/companion/CompanionDeviceManagerService.java +++ b/services/companion/java/com/android/server/companion/CompanionDeviceManagerService.java @@ -455,7 +455,7 @@ public class CompanionDeviceManagerService extends SystemService implements Bind }, FgThread.getExecutor()).whenComplete(uncheckExceptions((association, err) -> { if (err == null) { - addAssociation(association); + addAssociation(association, userId); } else { Slog.e(LOG_TAG, "Failed to discover device(s)", err); callback.onFailure("No devices found: " + err.getMessage()); @@ -646,7 +646,7 @@ public class CompanionDeviceManagerService extends SystemService implements Bind } else { return association; } - })); + }), userId); restartBleScan(); } @@ -664,7 +664,8 @@ public class CompanionDeviceManagerService extends SystemService implements Bind android.Manifest.permission.ASSOCIATE_COMPANION_DEVICES, "createAssociation"); addAssociation(new Association( - userId, macAddress, packageName, null, false, System.currentTimeMillis())); + userId, macAddress, packageName, null, false, + System.currentTimeMillis()), userId); } private void checkCanCallNotificationApi(String callingPackage) throws RemoteException { @@ -738,9 +739,9 @@ public class CompanionDeviceManagerService extends SystemService implements Bind return Binder.getCallingUid() == Process.SYSTEM_UID; } - void addAssociation(Association association) { + void addAssociation(Association association, int userId) { updateSpecialAccessPermissionForAssociatedPackage(association); - recordAssociation(association); + recordAssociation(association, userId); } void removeAssociation(int userId, String pkg, String deviceMacAddress) { @@ -752,7 +753,7 @@ public class CompanionDeviceManagerService extends SystemService implements Bind onAssociationPreRemove(association); } return notMatch; - })); + }), userId); restartBleScan(); } @@ -944,13 +945,9 @@ public class CompanionDeviceManagerService extends SystemService implements Bind }, getContext(), packageName, userId).recycleOnUse()); } - private void recordAssociation(Association association) { + private void recordAssociation(Association association, int userId) { Slog.i(LOG_TAG, "recordAssociation(" + association + ")"); - updateAssociations(associations -> CollectionUtils.add(associations, association)); - } - - private void updateAssociations(Function<Set<Association>, Set<Association>> update) { - updateAssociations(update, getCallingUserId()); + updateAssociations(associations -> CollectionUtils.add(associations, association), userId); } private void updateAssociations(Function<Set<Association>, Set<Association>> update, @@ -1515,7 +1512,7 @@ public class CompanionDeviceManagerService extends SystemService implements Bind String pkg = getNextArgRequired(); String address = getNextArgRequired(); addAssociation(new Association(userId, address, pkg, null, false, - System.currentTimeMillis())); + System.currentTimeMillis()), userId); } break; diff --git a/services/core/java/com/android/server/accounts/AccountManagerService.java b/services/core/java/com/android/server/accounts/AccountManagerService.java index 2a634ebdd555..a6a8cf018eef 100644 --- a/services/core/java/com/android/server/accounts/AccountManagerService.java +++ b/services/core/java/com/android/server/accounts/AccountManagerService.java @@ -453,7 +453,7 @@ public class AccountManagerService if (!checkAccess || hasAccountAccess(account, packageName, UserHandle.getUserHandleForUid(uid))) { cancelNotification(getCredentialPermissionNotificationId(account, - AccountManager.ACCOUNT_ACCESS_TOKEN_TYPE, uid), packageName, + AccountManager.ACCOUNT_ACCESS_TOKEN_TYPE, uid), UserHandle.getUserHandleForUid(uid)); } } @@ -3136,8 +3136,8 @@ public class AccountManagerService String authTokenType = intent.getStringExtra( GrantCredentialsPermissionActivity.EXTRAS_AUTH_TOKEN_TYPE); final String titleAndSubtitle = - mContext.getString(R.string.permission_request_notification_with_subtitle, - account.name); + mContext.getString(R.string.permission_request_notification_for_app_with_subtitle, + getApplicationLabel(packageName), account.name); final int index = titleAndSubtitle.indexOf('\n'); String title = titleAndSubtitle; String subtitle = ""; @@ -3160,7 +3160,16 @@ public class AccountManagerService null, user)) .build(); installNotification(getCredentialPermissionNotificationId( - account, authTokenType, uid), n, packageName, user.getIdentifier()); + account, authTokenType, uid), n, "android", user.getIdentifier()); + } + + private String getApplicationLabel(String packageName) { + try { + return mPackageManager.getApplicationLabel( + mPackageManager.getApplicationInfo(packageName, 0)).toString(); + } catch (PackageManager.NameNotFoundException e) { + return packageName; + } } private Intent newGrantCredentialsPermissionIntent(Account account, String packageName, @@ -3196,7 +3205,7 @@ public class AccountManagerService nId = accounts.credentialsPermissionNotificationIds.get(key); if (nId == null) { String tag = TAG + ":" + SystemMessage.NOTE_ACCOUNT_CREDENTIAL_PERMISSION - + ":" + account.hashCode() + ":" + authTokenType.hashCode(); + + ":" + account.hashCode() + ":" + authTokenType.hashCode() + ":" + uid; int id = SystemMessage.NOTE_ACCOUNT_CREDENTIAL_PERMISSION; nId = new NotificationId(tag, id); accounts.credentialsPermissionNotificationIds.put(key, nId); @@ -4147,7 +4156,7 @@ public class AccountManagerService private void handleAuthenticatorResponse(boolean accessGranted) throws RemoteException { cancelNotification(getCredentialPermissionNotificationId(account, - AccountManager.ACCOUNT_ACCESS_TOKEN_TYPE, uid), packageName, + AccountManager.ACCOUNT_ACCESS_TOKEN_TYPE, uid), UserHandle.getUserHandleForUid(uid)); if (callback != null) { Bundle result = new Bundle(); diff --git a/services/core/java/com/android/server/am/ActiveServices.java b/services/core/java/com/android/server/am/ActiveServices.java index 58a1ead3ed1d..875ef377f442 100644 --- a/services/core/java/com/android/server/am/ActiveServices.java +++ b/services/core/java/com/android/server/am/ActiveServices.java @@ -36,6 +36,7 @@ import static android.os.PowerExemptionManager.REASON_DENIED; import static android.os.PowerExemptionManager.REASON_DEVICE_DEMO_MODE; import static android.os.PowerExemptionManager.REASON_DEVICE_OWNER; import static android.os.PowerExemptionManager.REASON_FGS_BINDING; +import static android.os.PowerExemptionManager.REASON_CURRENT_INPUT_METHOD; import static android.os.PowerExemptionManager.REASON_INSTR_BACKGROUND_ACTIVITY_PERMISSION; import static android.os.PowerExemptionManager.REASON_INSTR_BACKGROUND_FGS_PERMISSION; import static android.os.PowerExemptionManager.REASON_OPT_OUT_REQUESTED; @@ -6162,6 +6163,20 @@ public final class ActiveServices { ret = REASON_OP_ACTIVATE_PLATFORM_VPN; } } + + if (ret == REASON_DENIED) { + final String inputMethod = + Settings.Secure.getStringForUser(mAm.mContext.getContentResolver(), + Settings.Secure.DEFAULT_INPUT_METHOD, + UserHandle.getUserId(callingUid)); + if (inputMethod != null) { + final ComponentName cn = ComponentName.unflattenFromString(inputMethod); + if (cn != null && cn.getPackageName().equals(callingPackage)) { + ret = REASON_CURRENT_INPUT_METHOD; + } + } + } + if (ret == REASON_DENIED) { if (mAm.mConstants.mFgsAllowOptOut && targetService != null diff --git a/services/core/java/com/android/server/am/CoreSettingsObserver.java b/services/core/java/com/android/server/am/CoreSettingsObserver.java index b325ea3b21b3..5c9d38515e49 100644 --- a/services/core/java/com/android/server/am/CoreSettingsObserver.java +++ b/services/core/java/com/android/server/am/CoreSettingsObserver.java @@ -27,6 +27,7 @@ import android.provider.DeviceConfig; import android.provider.Settings; import android.widget.WidgetFlags; +import com.android.internal.R; import com.android.internal.annotations.VisibleForTesting; import java.util.ArrayList; @@ -159,12 +160,9 @@ final class CoreSettingsObserver extends ContentObserver { DeviceConfig.NAMESPACE_WIDGET, WidgetFlags.MAGNIFIER_ASPECT_RATIO, WidgetFlags.KEY_MAGNIFIER_ASPECT_RATIO, float.class, WidgetFlags.MAGNIFIER_ASPECT_RATIO_DEFAULT)); - sDeviceConfigEntries.add(new DeviceConfigEntry<>( - DeviceConfig.NAMESPACE_WIDGET, WidgetFlags.ANALOG_CLOCK_SECONDS_HAND_FPS, - WidgetFlags.KEY_ANALOG_CLOCK_SECONDS_HAND_FPS, int.class, - WidgetFlags.ANALOG_CLOCK_SECONDS_HAND_FPS_DEFAULT)); // add other device configs here... } + private static volatile boolean sDeviceConfigContextEntriesLoaded = false; private final Bundle mCoreSettings = new Bundle(); @@ -172,11 +170,29 @@ final class CoreSettingsObserver extends ContentObserver { public CoreSettingsObserver(ActivityManagerService activityManagerService) { super(activityManagerService.mHandler); + + if (!sDeviceConfigContextEntriesLoaded) { + synchronized (sDeviceConfigEntries) { + if (!sDeviceConfigContextEntriesLoaded) { + loadDeviceConfigContextEntries(activityManagerService.mContext); + sDeviceConfigContextEntriesLoaded = true; + } + } + } + mActivityManagerService = activityManagerService; beginObserveCoreSettings(); sendCoreSettings(); } + private static void loadDeviceConfigContextEntries(Context context) { + sDeviceConfigEntries.add(new DeviceConfigEntry<>( + DeviceConfig.NAMESPACE_WIDGET, WidgetFlags.ANALOG_CLOCK_SECONDS_HAND_FPS, + WidgetFlags.KEY_ANALOG_CLOCK_SECONDS_HAND_FPS, int.class, + context.getResources() + .getInteger(R.integer.config_defaultAnalogClockSecondsHandFps))); + } + public Bundle getCoreSettingsLocked() { return (Bundle) mCoreSettings.clone(); } diff --git a/services/core/java/com/android/server/appop/AppOpsService.java b/services/core/java/com/android/server/appop/AppOpsService.java index 122880c19951..e001b059b85e 100644 --- a/services/core/java/com/android/server/appop/AppOpsService.java +++ b/services/core/java/com/android/server/appop/AppOpsService.java @@ -4523,10 +4523,15 @@ public class AppOpsService extends IAppOpsService.Stub { int callingUid = Binder.getCallingUid(); // Allow any attribution tag for resolvable uids - int pkgUid = resolveUid(packageName); - if (pkgUid != Process.INVALID_UID) { + int pkgUid; + if (Objects.equals(packageName, "com.android.shell")) { // Special case for the shell which is a package but should be able // to bypass app attribution tag restrictions. + pkgUid = Process.SHELL_UID; + } else { + pkgUid = resolveUid(packageName); + } + if (pkgUid != Process.INVALID_UID) { if (pkgUid != UserHandle.getAppId(uid)) { String otherUidMessage = DEBUG ? " but it is really " + pkgUid : " but it is not"; throw new SecurityException("Specified package " + packageName + " under uid " @@ -6993,7 +6998,6 @@ public class AppOpsService extends IAppOpsService.Stub { return Process.ROOT_UID; case "shell": case "dumpstate": - case "com.android.shell": return Process.SHELL_UID; case "media": return Process.MEDIA_UID; diff --git a/services/core/java/com/android/server/pm/AppsFilter.java b/services/core/java/com/android/server/pm/AppsFilter.java index 1401fa9b5e58..06ff69176bb7 100644 --- a/services/core/java/com/android/server/pm/AppsFilter.java +++ b/services/core/java/com/android/server/pm/AppsFilter.java @@ -17,8 +17,6 @@ package com.android.server.pm; import static android.os.Trace.TRACE_TAG_PACKAGE_MANAGER; -import static android.os.UserHandle.USER_ALL; -import static android.os.UserHandle.USER_NULL; import static android.provider.DeviceConfig.NAMESPACE_PACKAGE_MANAGER_SERVICE; import static com.android.internal.annotations.VisibleForTesting.Visibility.PRIVATE; @@ -687,7 +685,7 @@ public class AppsFilter implements Watchable, Snappable { synchronized (mCacheLock) { if (mShouldFilterCache != null) { updateShouldFilterCacheForPackage(mShouldFilterCache, null, newPkgSetting, - settings, users, USER_ALL, settings.size()); + settings, users, settings.size()); if (additionalChangedPackages != null) { for (int index = 0; index < additionalChangedPackages.size(); index++) { String changedPackage = additionalChangedPackages.valueAt(index); @@ -700,8 +698,7 @@ public class AppsFilter implements Watchable, Snappable { } updateShouldFilterCacheForPackage(mShouldFilterCache, null, - changedPkgSetting, settings, users, USER_ALL, - settings.size()); + changedPkgSetting, settings, users, settings.size()); } } } // else, rebuild entire cache when system is ready @@ -833,57 +830,24 @@ public class AppsFilter implements Watchable, Snappable { } } } - private void updateEntireShouldFilterCache() { - updateEntireShouldFilterCache(USER_ALL); - } - private void updateEntireShouldFilterCache(int subjectUserId) { + private void updateEntireShouldFilterCache() { mStateProvider.runWithState((settings, users) -> { - int userId = USER_NULL; - for (int u = 0; u < users.length; u++) { - if (subjectUserId == users[u].id) { - userId = subjectUserId; - break; - } - } - if (userId == USER_NULL) { - Slog.e(TAG, "We encountered a new user that isn't a member of known users, " - + "updating the whole cache"); - userId = USER_ALL; - } WatchedSparseBooleanMatrix cache = - updateEntireShouldFilterCacheInner(settings, users, userId); + updateEntireShouldFilterCacheInner(settings, users); synchronized (mCacheLock) { - if (userId != USER_ALL) { - // if we're only updating a single user id, we need to copy over the prior - // cached values for the other users. - int[] uids = mShouldFilterCache.keys(); - for (int i = 0; i < uids.length; i++) { - int uid1 = uids[i]; - if (UserHandle.getUserId(uid1) == userId) { - continue; - } - for (int j = 0; j < uids.length; j++) { - int uid2 = uids[j]; - if (UserHandle.getUserId(uid2) == userId) { - continue; - } - cache.put(uid1, uid2, mShouldFilterCache.get(uid1, uid2)); - } - } - } mShouldFilterCache = cache; } }); } private WatchedSparseBooleanMatrix updateEntireShouldFilterCacheInner( - ArrayMap<String, PackageSetting> settings, UserInfo[] users, int subjectUserId) { + ArrayMap<String, PackageSetting> settings, UserInfo[] users) { WatchedSparseBooleanMatrix cache = new WatchedSparseBooleanMatrix(users.length * settings.size()); for (int i = settings.size() - 1; i >= 0; i--) { updateShouldFilterCacheForPackage(cache, - null /*skipPackage*/, settings.valueAt(i), settings, users, subjectUserId, i); + null /*skipPackage*/, settings.valueAt(i), settings, users, i); } return cache; } @@ -904,8 +868,8 @@ public class AppsFilter implements Watchable, Snappable { packagesCache.put(settings.keyAt(i), pkg); } }); - WatchedSparseBooleanMatrix cache = updateEntireShouldFilterCacheInner( - settingsCopy, usersRef[0], USER_ALL); + WatchedSparseBooleanMatrix cache = + updateEntireShouldFilterCacheInner(settingsCopy, usersRef[0]); boolean[] changed = new boolean[1]; // We have a cache, let's make sure the world hasn't changed out from under us. mStateProvider.runWithState((settings, users) -> { @@ -935,10 +899,10 @@ public class AppsFilter implements Watchable, Snappable { }); } - public void onUserCreated(int newUserId) { + public void onUsersChanged() { synchronized (mCacheLock) { if (mShouldFilterCache != null) { - updateEntireShouldFilterCache(newUserId); + updateEntireShouldFilterCache(); onChanged(); } } @@ -949,7 +913,7 @@ public class AppsFilter implements Watchable, Snappable { if (mShouldFilterCache != null) { mStateProvider.runWithState((settings, users) -> { updateShouldFilterCacheForPackage(mShouldFilterCache, null /* skipPackage */, - settings.get(packageName), settings, users, USER_ALL, + settings.get(packageName), settings, users, settings.size() /*maxIndex*/); }); } @@ -958,7 +922,7 @@ public class AppsFilter implements Watchable, Snappable { private void updateShouldFilterCacheForPackage(WatchedSparseBooleanMatrix cache, @Nullable String skipPackageName, PackageSetting subjectSetting, ArrayMap<String, - PackageSetting> allSettings, UserInfo[] allUsers, int subjectUserId, int maxIndex) { + PackageSetting> allSettings, UserInfo[] allUsers, int maxIndex) { for (int i = Math.min(maxIndex, allSettings.size() - 1); i >= 0; i--) { PackageSetting otherSetting = allSettings.valueAt(i); if (subjectSetting.appId == otherSetting.appId) { @@ -968,34 +932,25 @@ public class AppsFilter implements Watchable, Snappable { if (subjectSetting.name == skipPackageName || otherSetting.name == skipPackageName) { continue; } - if (subjectUserId == USER_ALL) { - for (int su = 0; su < allUsers.length; su++) { - updateShouldFilterCacheForUser(cache, subjectSetting, allUsers, otherSetting, - allUsers[su].id); + final int userCount = allUsers.length; + final int appxUidCount = userCount * allSettings.size(); + for (int su = 0; su < userCount; su++) { + int subjectUser = allUsers[su].id; + for (int ou = 0; ou < userCount; ou++) { + int otherUser = allUsers[ou].id; + int subjectUid = UserHandle.getUid(subjectUser, subjectSetting.appId); + int otherUid = UserHandle.getUid(otherUser, otherSetting.appId); + cache.put(subjectUid, otherUid, + shouldFilterApplicationInternal( + subjectUid, subjectSetting, otherSetting, otherUser)); + cache.put(otherUid, subjectUid, + shouldFilterApplicationInternal( + otherUid, otherSetting, subjectSetting, subjectUser)); } - } else { - updateShouldFilterCacheForUser(cache, subjectSetting, allUsers, otherSetting, - subjectUserId); } } } - private void updateShouldFilterCacheForUser(WatchedSparseBooleanMatrix cache, - PackageSetting subjectSetting, UserInfo[] allUsers, PackageSetting otherSetting, - int subjectUserId) { - for (int ou = 0; ou < allUsers.length; ou++) { - int otherUser = allUsers[ou].id; - int subjectUid = UserHandle.getUid(subjectUserId, subjectSetting.appId); - int otherUid = UserHandle.getUid(otherUser, otherSetting.appId); - cache.put(subjectUid, otherUid, - shouldFilterApplicationInternal( - subjectUid, subjectSetting, otherSetting, otherUser)); - cache.put(otherUid, subjectUid, - shouldFilterApplicationInternal( - otherUid, otherSetting, subjectSetting, subjectUserId)); - } - } - private static boolean isSystemSigned(@NonNull PackageParser.SigningDetails sysSigningDetails, PackageSetting pkgSetting) { return pkgSetting.isSystem() @@ -1190,7 +1145,7 @@ public class AppsFilter implements Watchable, Snappable { continue; } updateShouldFilterCacheForPackage(mShouldFilterCache, setting.name, - siblingSetting, settings, users, USER_ALL, settings.size()); + siblingSetting, settings, users, settings.size()); } } @@ -1207,7 +1162,7 @@ public class AppsFilter implements Watchable, Snappable { } updateShouldFilterCacheForPackage(mShouldFilterCache, null, - changedPkgSetting, settings, users, USER_ALL, settings.size()); + changedPkgSetting, settings, users, settings.size()); } } } diff --git a/services/core/java/com/android/server/pm/PackageManagerService.java b/services/core/java/com/android/server/pm/PackageManagerService.java index bd9c37bef7ef..9325c6b16afe 100644 --- a/services/core/java/com/android/server/pm/PackageManagerService.java +++ b/services/core/java/com/android/server/pm/PackageManagerService.java @@ -26525,7 +26525,7 @@ public class PackageManagerService extends IPackageManager.Stub synchronized (mLock) { scheduleWritePackageRestrictionsLocked(userId); scheduleWritePackageListLocked(userId); - mAppsFilter.onUserCreated(userId); + mAppsFilter.onUsersChanged(); } } diff --git a/services/core/java/com/android/server/pm/TEST_MAPPING b/services/core/java/com/android/server/pm/TEST_MAPPING index 878eb920717e..9182d811d56d 100644 --- a/services/core/java/com/android/server/pm/TEST_MAPPING +++ b/services/core/java/com/android/server/pm/TEST_MAPPING @@ -162,9 +162,6 @@ "include-filter": "com.android.server.pm.parsing.SystemPartitionParseTest" } ] - }, - { - "name": "CtsPackageManagerBootTestCases" } ], "imports": [ diff --git a/services/core/java/com/android/server/policy/AppOpsPolicy.java b/services/core/java/com/android/server/policy/AppOpsPolicy.java index a389f40e772b..7689f5f5fd65 100644 --- a/services/core/java/com/android/server/policy/AppOpsPolicy.java +++ b/services/core/java/com/android/server/policy/AppOpsPolicy.java @@ -81,6 +81,12 @@ public final class AppOpsPolicy implements AppOpsManagerInternal.CheckOpsDelegat private final VoiceInteractionManagerInternal mVoiceInteractionManagerInternal; /** + * Whether this device allows only the HotwordDetectionService to use OP_RECORD_AUDIO_HOTWORD + * which doesn't incur the privacy indicator. + */ + private final boolean mIsHotwordDetectionServiceRequired; + + /** * The locking policy around the location tags is a bit special. Since we want to * avoid grabbing the lock on every op note we are taking the approach where the * read and write are being done via a thread-safe data structure such that the @@ -114,6 +120,8 @@ public final class AppOpsPolicy implements AppOpsManagerInternal.CheckOpsDelegat mRoleManager = mContext.getSystemService(RoleManager.class); mVoiceInteractionManagerInternal = LocalServices.getService( VoiceInteractionManagerInternal.class); + mIsHotwordDetectionServiceRequired = isHotwordDetectionServiceRequired( + mContext.getPackageManager()); final LocationManagerInternal locationManagerInternal = LocalServices.getService( LocationManagerInternal.class); @@ -174,6 +182,12 @@ public final class AppOpsPolicy implements AppOpsManagerInternal.CheckOpsDelegat initializeActivityRecognizersTags(); } + private static boolean isHotwordDetectionServiceRequired(PackageManager pm) { + // The HotwordDetectionService APIs aren't ready yet for Auto or TV. + return !(pm.hasSystemFeature(PackageManager.FEATURE_AUTOMOTIVE) + || pm.hasSystemFeature(PackageManager.FEATURE_LEANBACK)); + } + @Override public int checkOperation(int code, int uid, String packageName, @Nullable String attributionTag, boolean raw, @@ -257,6 +271,7 @@ public final class AppOpsPolicy implements AppOpsManagerInternal.CheckOpsDelegat private int resolveDatasourceOp(int code, int uid, @NonNull String packageName, @Nullable String attributionTag) { + code = resolveRecordAudioOp(code, uid); if (attributionTag == null) { return code; } @@ -359,6 +374,24 @@ public final class AppOpsPolicy implements AppOpsManagerInternal.CheckOpsDelegat return code; } + private int resolveRecordAudioOp(int code, int uid) { + if (code == AppOpsManager.OP_RECORD_AUDIO_HOTWORD) { + if (!mIsHotwordDetectionServiceRequired) { + return code; + } + // Only the HotwordDetectionService can use the HOTWORD op which doesn't incur the + // privacy indicator. Downgrade to standard RECORD_AUDIO for other processes. + final HotwordDetectionServiceIdentity hotwordDetectionServiceIdentity = + mVoiceInteractionManagerInternal.getHotwordDetectionServiceIdentity(); + if (hotwordDetectionServiceIdentity != null + && uid == hotwordDetectionServiceIdentity.getIsolatedUid()) { + return code; + } + return AppOpsManager.OP_RECORD_AUDIO; + } + return code; + } + private int resolveUid(int code, int uid) { // The HotwordDetectionService is an isolated service, which ordinarily cannot hold // permissions. So we allow it to assume the owning package identity for certain diff --git a/services/core/java/com/android/server/stats/pull/StatsPullAtomService.java b/services/core/java/com/android/server/stats/pull/StatsPullAtomService.java index cd0ce2bd46a7..dda89614aba9 100644 --- a/services/core/java/com/android/server/stats/pull/StatsPullAtomService.java +++ b/services/core/java/com/android/server/stats/pull/StatsPullAtomService.java @@ -117,6 +117,7 @@ import android.os.Parcelable; import android.os.Process; import android.os.RemoteException; import android.os.ServiceManager; +import android.os.ServiceSpecificException; import android.os.StatFs; import android.os.SynchronousResultReceiver; import android.os.SystemClock; @@ -132,6 +133,18 @@ import android.os.storage.StorageManager; import android.os.storage.VolumeInfo; import android.provider.DeviceConfig; import android.provider.Settings; +import android.security.metrics.IKeystoreMetrics; +import android.security.metrics.KeyCreationWithAuthInfo; +import android.security.metrics.KeyCreationWithGeneralInfo; +import android.security.metrics.KeyCreationWithPurposeAndModesInfo; +import android.security.metrics.KeyOperationWithGeneralInfo; +import android.security.metrics.KeyOperationWithPurposeAndModesInfo; +import android.security.metrics.Keystore2AtomWithOverflow; +import android.security.metrics.KeystoreAtom; +import android.security.metrics.KeystoreAtomPayload; +import android.security.metrics.RkpErrorStats; +import android.security.metrics.RkpPoolStats; +import android.security.metrics.StorageStats; import android.stats.storage.StorageEnums; import android.telephony.ModemActivityInfo; import android.telephony.SubscriptionInfo; @@ -373,6 +386,10 @@ public class StatsPullAtomService extends SystemService { private SelectedProcessCpuThreadReader mSurfaceFlingerProcessCpuThreadReader; + // Only access via getIKeystoreMetricsService + @GuardedBy("mKeystoreLock") + private IKeystoreMetrics mIKeystoreMetrics; + // Puller locks private final Object mDataBytesTransferLock = new Object(); private final Object mBluetoothBytesTransferLock = new Object(); @@ -428,6 +445,7 @@ public class StatsPullAtomService extends SystemService { private final Object mAttributedAppOpsLock = new Object(); private final Object mSettingsStatsLock = new Object(); private final Object mInstalledIncrementalPackagesLock = new Object(); + private final Object mKeystoreLock = new Object(); public StatsPullAtomService(Context context) { super(context); @@ -435,6 +453,7 @@ public class StatsPullAtomService extends SystemService { } private native void initializeNativePullers(); + /** * Use of this StatsPullAtomCallbackImpl means we avoid one class per tagId, which we would * get if we used lambdas. @@ -703,6 +722,16 @@ public class StatsPullAtomService extends SystemService { synchronized (mInstalledIncrementalPackagesLock) { return pullInstalledIncrementalPackagesLocked(atomTag, data); } + case FrameworkStatsLog.KEYSTORE2_STORAGE_STATS: + case FrameworkStatsLog.RKP_POOL_STATS: + case FrameworkStatsLog.KEYSTORE2_KEY_CREATION_WITH_GENERAL_INFO: + case FrameworkStatsLog.KEYSTORE2_KEY_CREATION_WITH_AUTH_INFO: + case FrameworkStatsLog.KEYSTORE2_KEY_CREATION_WITH_PURPOSE_AND_MODES_INFO: + case FrameworkStatsLog.KEYSTORE2_ATOM_WITH_OVERFLOW: + case FrameworkStatsLog.KEYSTORE2_KEY_OPERATION_WITH_PURPOSE_AND_MODES_INFO: + case FrameworkStatsLog.KEYSTORE2_KEY_OPERATION_WITH_GENERAL_INFO: + case FrameworkStatsLog.RKP_ERROR_STATS: + return pullKeystoreAtoms(atomTag, data); default: throw new UnsupportedOperationException("Unknown tagId=" + atomTag); } @@ -795,6 +824,8 @@ public class StatsPullAtomService extends SystemService { mSurfaceFlingerProcessCpuThreadReader = new SelectedProcessCpuThreadReader("/system/bin/surfaceflinger"); + + getIKeystoreMetricsService(); } void registerEventListeners() { @@ -887,6 +918,15 @@ public class StatsPullAtomService extends SystemService { registerBatteryCycleCount(); registerSettingsStats(); registerInstalledIncrementalPackages(); + registerKeystoreStorageStats(); + registerRkpPoolStats(); + registerKeystoreKeyCreationWithGeneralInfo(); + registerKeystoreKeyCreationWithAuthInfo(); + registerKeystoreKeyCreationWithPurposeModesInfo(); + registerKeystoreAtomWithOverflow(); + registerKeystoreKeyOperationWithPurposeAndModesInfo(); + registerKeystoreKeyOperationWithGeneralInfo(); + registerRkpErrorStats(); } private void initAndRegisterNetworkStatsPullers() { @@ -971,6 +1011,28 @@ public class StatsPullAtomService extends SystemService { } } + private IKeystoreMetrics getIKeystoreMetricsService() { + synchronized (mKeystoreLock) { + if (mIKeystoreMetrics == null) { + mIKeystoreMetrics = IKeystoreMetrics.Stub.asInterface( + ServiceManager.getService("android.security.metrics")); + if (mIKeystoreMetrics != null) { + try { + mIKeystoreMetrics.asBinder().linkToDeath(() -> { + synchronized (mKeystoreLock) { + mIKeystoreMetrics = null; + } + }, /* flags */ 0); + } catch (RemoteException e) { + Slog.e(TAG, "linkToDeath with IKeystoreMetrics failed", e); + mIKeystoreMetrics = null; + } + } + } + return mIKeystoreMetrics; + } + } + private IStoraged getIStoragedService() { synchronized (mStoragedLock) { if (mStorageService == null) { @@ -4005,6 +4067,253 @@ public class StatsPullAtomService extends SystemService { return StatsManager.PULL_SUCCESS; } + private void registerKeystoreStorageStats() { + mStatsManager.setPullAtomCallback( + FrameworkStatsLog.KEYSTORE2_STORAGE_STATS, + null, // use default PullAtomMetadata values, + DIRECT_EXECUTOR, + mStatsCallbackImpl); + } + + private void registerRkpPoolStats() { + mStatsManager.setPullAtomCallback( + FrameworkStatsLog.RKP_POOL_STATS, + null, // use default PullAtomMetadata values, + DIRECT_EXECUTOR, + mStatsCallbackImpl); + } + + private void registerKeystoreKeyCreationWithGeneralInfo() { + mStatsManager.setPullAtomCallback( + FrameworkStatsLog.KEYSTORE2_KEY_CREATION_WITH_GENERAL_INFO, + null, // use default PullAtomMetadata values, + DIRECT_EXECUTOR, + mStatsCallbackImpl); + } + + private void registerKeystoreKeyCreationWithAuthInfo() { + mStatsManager.setPullAtomCallback( + FrameworkStatsLog.KEYSTORE2_KEY_CREATION_WITH_AUTH_INFO, + null, // use default PullAtomMetadata values, + DIRECT_EXECUTOR, + mStatsCallbackImpl); + } + + private void registerKeystoreKeyCreationWithPurposeModesInfo() { + mStatsManager.setPullAtomCallback( + FrameworkStatsLog.KEYSTORE2_KEY_CREATION_WITH_PURPOSE_AND_MODES_INFO, + null, // use default PullAtomMetadata values, + DIRECT_EXECUTOR, + mStatsCallbackImpl); + } + + private void registerKeystoreAtomWithOverflow() { + mStatsManager.setPullAtomCallback( + FrameworkStatsLog.KEYSTORE2_ATOM_WITH_OVERFLOW, + null, // use default PullAtomMetadata values, + DIRECT_EXECUTOR, + mStatsCallbackImpl); + } + + private void registerKeystoreKeyOperationWithPurposeAndModesInfo() { + mStatsManager.setPullAtomCallback( + FrameworkStatsLog.KEYSTORE2_KEY_OPERATION_WITH_PURPOSE_AND_MODES_INFO, + null, // use default PullAtomMetadata values, + DIRECT_EXECUTOR, + mStatsCallbackImpl); + } + + private void registerKeystoreKeyOperationWithGeneralInfo() { + mStatsManager.setPullAtomCallback( + FrameworkStatsLog.KEYSTORE2_KEY_OPERATION_WITH_GENERAL_INFO, + null, // use default PullAtomMetadata values, + DIRECT_EXECUTOR, + mStatsCallbackImpl); + } + + private void registerRkpErrorStats() { + mStatsManager.setPullAtomCallback( + FrameworkStatsLog.RKP_ERROR_STATS, + null, // use default PullAtomMetadata values, + DIRECT_EXECUTOR, + mStatsCallbackImpl); + } + + int parseKeystoreStorageStats(KeystoreAtom[] atoms, List<StatsEvent> pulledData) { + for (KeystoreAtom atomWrapper : atoms) { + if (atomWrapper.payload.getTag() != KeystoreAtomPayload.storageStats) { + return StatsManager.PULL_SKIP; + } + StorageStats atom = atomWrapper.payload.getStorageStats(); + pulledData.add(FrameworkStatsLog.buildStatsEvent( + FrameworkStatsLog.KEYSTORE2_STORAGE_STATS, atom.storage_type, + atom.size, atom.unused_size)); + } + return StatsManager.PULL_SUCCESS; + } + + int parseRkpPoolStats(KeystoreAtom[] atoms, List<StatsEvent> pulledData) { + for (KeystoreAtom atomWrapper : atoms) { + if (atomWrapper.payload.getTag() != KeystoreAtomPayload.rkpPoolStats) { + return StatsManager.PULL_SKIP; + } + RkpPoolStats atom = atomWrapper.payload.getRkpPoolStats(); + pulledData.add(FrameworkStatsLog.buildStatsEvent( + FrameworkStatsLog.RKP_POOL_STATS, atom.pool_status, atom.count_of_keys)); + } + return StatsManager.PULL_SUCCESS; + } + + int parseKeystoreKeyCreationWithGeneralInfo(KeystoreAtom[] atoms, List<StatsEvent> pulledData) { + for (KeystoreAtom atomWrapper : atoms) { + if (atomWrapper.payload.getTag() + != KeystoreAtomPayload.keyCreationWithGeneralInfo) { + return StatsManager.PULL_SKIP; + } + KeyCreationWithGeneralInfo atom = atomWrapper.payload.getKeyCreationWithGeneralInfo(); + pulledData.add(FrameworkStatsLog.buildStatsEvent( + FrameworkStatsLog.KEYSTORE2_KEY_CREATION_WITH_GENERAL_INFO, atom.algorithm, + atom.key_size, atom.ec_curve, atom.key_origin, atom.error_code, + atom.attestation_requested, atomWrapper.count)); + } + return StatsManager.PULL_SUCCESS; + } + + int parseKeystoreKeyCreationWithAuthInfo(KeystoreAtom[] atoms, List<StatsEvent> pulledData) { + for (KeystoreAtom atomWrapper : atoms) { + if (atomWrapper.payload.getTag() != KeystoreAtomPayload.keyCreationWithAuthInfo) { + return StatsManager.PULL_SKIP; + } + KeyCreationWithAuthInfo atom = atomWrapper.payload.getKeyCreationWithAuthInfo(); + pulledData.add(FrameworkStatsLog.buildStatsEvent( + FrameworkStatsLog.KEYSTORE2_KEY_CREATION_WITH_AUTH_INFO, atom.user_auth_type, + atom.log10_auth_key_timeout_seconds, atom.security_level, atomWrapper.count)); + } + return StatsManager.PULL_SUCCESS; + } + + + int parseKeystoreKeyCreationWithPurposeModesInfo(KeystoreAtom[] atoms, + List<StatsEvent> pulledData) { + for (KeystoreAtom atomWrapper : atoms) { + if (atomWrapper.payload.getTag() + != KeystoreAtomPayload.keyCreationWithPurposeAndModesInfo) { + return StatsManager.PULL_SKIP; + } + KeyCreationWithPurposeAndModesInfo atom = + atomWrapper.payload.getKeyCreationWithPurposeAndModesInfo(); + pulledData.add(FrameworkStatsLog.buildStatsEvent( + FrameworkStatsLog.KEYSTORE2_KEY_CREATION_WITH_PURPOSE_AND_MODES_INFO, + atom.algorithm, atom.purpose_bitmap, + atom.padding_mode_bitmap, atom.digest_bitmap, atom.block_mode_bitmap, + atomWrapper.count)); + } + return StatsManager.PULL_SUCCESS; + } + + int parseKeystoreAtomWithOverflow(KeystoreAtom[] atoms, List<StatsEvent> pulledData) { + for (KeystoreAtom atomWrapper : atoms) { + if (atomWrapper.payload.getTag() + != KeystoreAtomPayload.keystore2AtomWithOverflow) { + return StatsManager.PULL_SKIP; + } + Keystore2AtomWithOverflow atom = atomWrapper.payload.getKeystore2AtomWithOverflow(); + pulledData.add(FrameworkStatsLog.buildStatsEvent( + FrameworkStatsLog.KEYSTORE2_ATOM_WITH_OVERFLOW, atom.atom_id, + atomWrapper.count)); + } + return StatsManager.PULL_SUCCESS; + } + + int parseKeystoreKeyOperationWithPurposeModesInfo(KeystoreAtom[] atoms, + List<StatsEvent> pulledData) { + for (KeystoreAtom atomWrapper : atoms) { + if (atomWrapper.payload.getTag() + != KeystoreAtomPayload.keyOperationWithPurposeAndModesInfo) { + return StatsManager.PULL_SKIP; + } + KeyOperationWithPurposeAndModesInfo atom = + atomWrapper.payload.getKeyOperationWithPurposeAndModesInfo(); + pulledData.add(FrameworkStatsLog.buildStatsEvent( + FrameworkStatsLog.KEYSTORE2_KEY_OPERATION_WITH_PURPOSE_AND_MODES_INFO, + atom.purpose, atom.padding_mode_bitmap, atom.digest_bitmap, + atom.block_mode_bitmap, atomWrapper.count)); + } + return StatsManager.PULL_SUCCESS; + } + + int parseKeystoreKeyOperationWithGeneralInfo(KeystoreAtom[] atoms, + List<StatsEvent> pulledData) { + for (KeystoreAtom atomWrapper : atoms) { + if (atomWrapper.payload.getTag() + != KeystoreAtomPayload.keyOperationWithGeneralInfo) { + return StatsManager.PULL_SKIP; + } + KeyOperationWithGeneralInfo atom = atomWrapper.payload.getKeyOperationWithGeneralInfo(); + pulledData.add(FrameworkStatsLog.buildStatsEvent( + FrameworkStatsLog.KEYSTORE2_KEY_OPERATION_WITH_GENERAL_INFO, atom.outcome, + atom.error_code, atom.key_upgraded, atom.security_level, atomWrapper.count)); + } + return StatsManager.PULL_SUCCESS; + } + + int parseRkpErrorStats(KeystoreAtom[] atoms, + List<StatsEvent> pulledData) { + for (KeystoreAtom atomWrapper : atoms) { + if (atomWrapper.payload.getTag() != KeystoreAtomPayload.rkpErrorStats) { + return StatsManager.PULL_SKIP; + } + RkpErrorStats atom = atomWrapper.payload.getRkpErrorStats(); + pulledData.add(FrameworkStatsLog.buildStatsEvent( + FrameworkStatsLog.RKP_ERROR_STATS, atom.rkpError, atomWrapper.count)); + } + return StatsManager.PULL_SUCCESS; + } + + int pullKeystoreAtoms(int atomTag, List<StatsEvent> pulledData) { + IKeystoreMetrics keystoreMetricsService = getIKeystoreMetricsService(); + if (keystoreMetricsService == null) { + Slog.w(TAG, "Keystore service is null"); + return StatsManager.PULL_SKIP; + } + final long callingToken = Binder.clearCallingIdentity(); + try { + KeystoreAtom[] atoms = keystoreMetricsService.pullMetrics(atomTag); + switch (atomTag) { + case FrameworkStatsLog.KEYSTORE2_STORAGE_STATS: + return parseKeystoreStorageStats(atoms, pulledData); + case FrameworkStatsLog.RKP_POOL_STATS: + return parseRkpPoolStats(atoms, pulledData); + case FrameworkStatsLog.KEYSTORE2_KEY_CREATION_WITH_GENERAL_INFO: + return parseKeystoreKeyCreationWithGeneralInfo(atoms, pulledData); + case FrameworkStatsLog.KEYSTORE2_KEY_CREATION_WITH_AUTH_INFO: + return parseKeystoreKeyCreationWithAuthInfo(atoms, pulledData); + case FrameworkStatsLog.KEYSTORE2_KEY_CREATION_WITH_PURPOSE_AND_MODES_INFO: + return parseKeystoreKeyCreationWithPurposeModesInfo(atoms, pulledData); + case FrameworkStatsLog.KEYSTORE2_ATOM_WITH_OVERFLOW: + return parseKeystoreAtomWithOverflow(atoms, pulledData); + case FrameworkStatsLog.KEYSTORE2_KEY_OPERATION_WITH_PURPOSE_AND_MODES_INFO: + return parseKeystoreKeyOperationWithPurposeModesInfo(atoms, pulledData); + case FrameworkStatsLog.KEYSTORE2_KEY_OPERATION_WITH_GENERAL_INFO: + return parseKeystoreKeyOperationWithGeneralInfo(atoms, pulledData); + case FrameworkStatsLog.RKP_ERROR_STATS: + return parseRkpErrorStats(atoms, pulledData); + default: + Slog.w(TAG, "Unsupported keystore atom: " + atomTag); + return StatsManager.PULL_SKIP; + } + } catch (RemoteException e) { + // Should not happen. + Slog.e(TAG, "Disconnected from keystore service. Cannot pull.", e); + return StatsManager.PULL_SKIP; + } catch (ServiceSpecificException e) { + Slog.e(TAG, "pulling keystore metrics failed", e); + return StatsManager.PULL_SKIP; + } finally { + Binder.restoreCallingIdentity(callingToken); + } + } + // Thermal event received from vendor thermal management subsystem private static final class ThermalEventListener extends IThermalEventListener.Stub { @Override diff --git a/services/core/java/com/android/server/timedetector/ServerFlags.java b/services/core/java/com/android/server/timedetector/ServerFlags.java index 7145f5ea4a64..fe977f8b3921 100644 --- a/services/core/java/com/android/server/timedetector/ServerFlags.java +++ b/services/core/java/com/android/server/timedetector/ServerFlags.java @@ -50,10 +50,6 @@ public final class ServerFlags { /** * An annotation used to indicate when a {@link DeviceConfig#NAMESPACE_SYSTEM_TIME} key is * required. - * - * <p>Note that the com.android.geotz module deployment of the Offline LocationTimeZoneProvider - * also shares the {@link DeviceConfig#NAMESPACE_SYSTEM_TIME}, and uses the - * prefix "geotz_" on all of its key strings. */ @StringDef(prefix = "KEY_", value = { KEY_LOCATION_TIME_ZONE_DETECTION_FEATURE_SUPPORTED, diff --git a/services/core/java/com/android/server/wm/ActivityRecord.java b/services/core/java/com/android/server/wm/ActivityRecord.java index 0ad8782cea4f..dd545290c377 100644 --- a/services/core/java/com/android/server/wm/ActivityRecord.java +++ b/services/core/java/com/android/server/wm/ActivityRecord.java @@ -5015,6 +5015,7 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A mAppStopped = true; // Reset the last saved PiP snap fraction on app stop. mDisplayContent.mPinnedTaskController.onActivityHidden(mActivityComponent); + mDisplayContent.mUnknownAppVisibilityController.appRemovedOrHidden(this); destroySurfaces(); // Remove any starting window that was added for this app if they are still around. removeStartingWindow(); diff --git a/services/core/java/com/android/server/wm/ActivityTaskManagerService.java b/services/core/java/com/android/server/wm/ActivityTaskManagerService.java index 2bf78e9b1a2c..899266d1f686 100644 --- a/services/core/java/com/android/server/wm/ActivityTaskManagerService.java +++ b/services/core/java/com/android/server/wm/ActivityTaskManagerService.java @@ -2633,10 +2633,11 @@ public class ActivityTaskManagerService extends IActivityTaskManager.Stub { } final ActivityInfo ainfo = AppGlobals.getPackageManager().getActivityInfo(comp, STOCK_PM_FLAGS, UserHandle.getUserId(callingUid)); - if (ainfo.applicationInfo.uid != callingUid) { - throw new SecurityException( - "Can't add task for another application: target uid=" - + ainfo.applicationInfo.uid + ", calling uid=" + callingUid); + if (ainfo == null || ainfo.applicationInfo.uid != callingUid) { + Slog.e(TAG, "Can't add task for another application: target uid=" + + (ainfo == null ? Process.INVALID_UID : ainfo.applicationInfo.uid) + + ", calling uid=" + callingUid); + return INVALID_TASK_ID; } final Task rootTask = r.getRootTask(); diff --git a/services/core/java/com/android/server/wm/AppTransitionController.java b/services/core/java/com/android/server/wm/AppTransitionController.java index 4acadb21b5e3..3dea68625e8d 100644 --- a/services/core/java/com/android/server/wm/AppTransitionController.java +++ b/services/core/java/com/android/server/wm/AppTransitionController.java @@ -412,7 +412,13 @@ public class AppTransitionController { return TRANSIT_OLD_TASK_CLOSE; } if (isActivityClosing) { - return TRANSIT_OLD_ACTIVITY_CLOSE; + for (int i = closingApps.size() - 1; i >= 0; i--) { + if (closingApps.valueAt(i).visibleIgnoringKeyguard) { + return TRANSIT_OLD_ACTIVITY_CLOSE; + } + } + // Skip close activity transition since no closing app can be visible + return WindowManager.TRANSIT_OLD_UNSET; } } if (appTransition.containsTransitRequest(TRANSIT_RELAUNCH) diff --git a/services/core/java/com/android/server/wm/DisplayArea.java b/services/core/java/com/android/server/wm/DisplayArea.java index b24ab93145b1..baa27e34d625 100644 --- a/services/core/java/com/android/server/wm/DisplayArea.java +++ b/services/core/java/com/android/server/wm/DisplayArea.java @@ -551,7 +551,13 @@ public class DisplayArea<T extends WindowContainer> extends WindowContainer<T> { } final WindowManagerPolicy policy = mWmService.mPolicy; if (policy.isKeyguardHostWindow(w.mAttrs)) { - if (mWmService.mKeyguardGoingAway) { + // Ignore the orientation of keyguard if it is going away or is not showing while + // the device is fully awake. In other words, use the orientation of keyguard if + // its window is visible while the device is going to sleep or is sleeping. + if (!mWmService.mAtmService.isKeyguardLocked() + && mDisplayContent.getDisplayPolicy().isAwake() + // Device is not going to sleep. + && policy.okToAnimate(true /* ignoreScreenOn */)) { return false; } // Consider unoccluding only when all unknown visibilities have been diff --git a/services/core/java/com/android/server/wm/DisplayContent.java b/services/core/java/com/android/server/wm/DisplayContent.java index 3dbc517af45c..62d8ace91834 100644 --- a/services/core/java/com/android/server/wm/DisplayContent.java +++ b/services/core/java/com/android/server/wm/DisplayContent.java @@ -1564,12 +1564,6 @@ class DisplayContent extends RootDisplayArea implements WindowManagerPolicy.Disp // window was transferred ({@link #mSkipAppTransitionAnimation}). return false; } - if ((mAppTransition.getTransitFlags() - & WindowManager.TRANSIT_FLAG_KEYGUARD_GOING_AWAY_NO_ANIMATION) != 0) { - // The transition may be finished before keyguard hidden. In order to avoid the - // intermediate orientation change, it is more stable to freeze the display. - return false; - } if (r.isState(RESUMED) && !r.getRootTask().mInResumeTopActivity) { // If the activity is executing or has done the lifecycle callback, use normal // rotation animation so the display info can be updated immediately (see diff --git a/services/core/java/com/android/server/wm/KeyguardController.java b/services/core/java/com/android/server/wm/KeyguardController.java index f8238c1d154a..3208ae3aa97d 100644 --- a/services/core/java/com/android/server/wm/KeyguardController.java +++ b/services/core/java/com/android/server/wm/KeyguardController.java @@ -72,7 +72,6 @@ class KeyguardController { private boolean mAodShowing; private boolean mKeyguardGoingAway; private boolean mDismissalRequested; - private int mBeforeUnoccludeTransit; private final SparseArray<KeyguardDisplayState> mDisplayStates = new SparseArray<>(); private final ActivityTaskManagerService mService; private RootWindowContainer mRootWindowContainer; @@ -191,14 +190,11 @@ class KeyguardController { // Irrelevant to AOD. dismissMultiWindowModeForTaskIfNeeded(null /* currentTaskControllsingOcclusion */, false /* turningScreenOn */); - setKeyguardGoingAway(false); + mKeyguardGoingAway = false; if (keyguardShowing) { mDismissalRequested = false; } } - // TODO(b/113840485): Check usage for non-default display - mWindowManager.setKeyguardOrAodShowingOnDefaultDisplay( - isKeyguardOrAodShowing(DEFAULT_DISPLAY)); // Update the sleep token first such that ensureActivitiesVisible has correct sleep token // state when evaluating visibilities. @@ -219,8 +215,8 @@ class KeyguardController { } Trace.traceBegin(TRACE_TAG_WINDOW_MANAGER, "keyguardGoingAway"); mService.deferWindowLayout(); + mKeyguardGoingAway = true; try { - setKeyguardGoingAway(true); EventLogTags.writeWmSetKeyguardShown( 1 /* keyguardShowing */, mAodShowing ? 1 : 0, @@ -258,11 +254,6 @@ class KeyguardController { mWindowManager.dismissKeyguard(callback, message); } - private void setKeyguardGoingAway(boolean keyguardGoingAway) { - mKeyguardGoingAway = keyguardGoingAway; - mWindowManager.setKeyguardGoingAway(keyguardGoingAway); - } - private void failCallback(IKeyguardDismissCallback callback) { try { callback.onDismissError(); diff --git a/services/core/java/com/android/server/wm/SplashScreenExceptionList.java b/services/core/java/com/android/server/wm/SplashScreenExceptionList.java new file mode 100644 index 000000000000..e815a0e2682a --- /dev/null +++ b/services/core/java/com/android/server/wm/SplashScreenExceptionList.java @@ -0,0 +1,127 @@ +/* + * 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.wm; + +import static android.provider.DeviceConfig.NAMESPACE_WINDOW_MANAGER; + +import android.annotation.NonNull; +import android.annotation.Nullable; +import android.content.pm.ApplicationInfo; +import android.os.Build; +import android.provider.DeviceConfig; +import android.util.Slog; + +import com.android.internal.annotations.GuardedBy; +import com.android.internal.annotations.VisibleForTesting; + +import java.util.HashSet; +import java.util.Locale; +import java.util.concurrent.Executor; +import java.util.function.Supplier; + +/** + * Handles filtering the list of package that don't use the system splash screen. + * The list is backed by a {@link DeviceConfig} property. + * <p> + * An application can manually opt-out of the exception list by setting the <meta-data + * {@value OPT_OUT_METADATA_FLAG}="true"/> in the <code><application></code> section of the + * manifest. + */ +class SplashScreenExceptionList { + + private static final boolean DEBUG = Build.isDebuggable(); + private static final String LOG_TAG = "SplashScreenExceptionList"; + private static final String KEY_SPLASH_SCREEN_EXCEPTION_LIST = "splash_screen_exception_list"; + private static final String NAMESPACE = NAMESPACE_WINDOW_MANAGER; + private static final String OPT_OUT_METADATA_FLAG = "android.splashscreen.exception_opt_out"; + + @GuardedBy("mLock") + private final HashSet<String> mDeviceConfigExcludedPackages = new HashSet<>(); + private final Object mLock = new Object(); + + @VisibleForTesting + final DeviceConfig.OnPropertiesChangedListener mOnPropertiesChangedListener; + + SplashScreenExceptionList(@NonNull Executor executor) { + updateDeviceConfig(DeviceConfig.getString(NAMESPACE, KEY_SPLASH_SCREEN_EXCEPTION_LIST, "")); + mOnPropertiesChangedListener = properties -> updateDeviceConfig( + properties.getString(KEY_SPLASH_SCREEN_EXCEPTION_LIST, "")); + DeviceConfig.addOnPropertiesChangedListener(NAMESPACE, executor, + mOnPropertiesChangedListener); + } + + private void updateDeviceConfig(String values) { + parseDeviceConfigPackageList(values); + } + + /** + * Returns true if the packageName is in the list and the target sdk is before S. + * + * @param packageName The package name of the application to check + * @param targetSdk The target sdk of the application + * @param infoSupplier A {@link Supplier} that returns an {@link ApplicationInfo} used to + * check if the application wants to opt-out of the exception list in the + * manifest metadata. Evaluated only if the application is in the exception + * list. + */ + @SuppressWarnings("AndroidFrameworkCompatChange") // Target sdk check + public boolean isException(@NonNull String packageName, int targetSdk, + @Nullable Supplier<ApplicationInfo> infoSupplier) { + if (targetSdk >= Build.VERSION_CODES.S) { + return false; + } + + synchronized (mLock) { + if (DEBUG) { + Slog.v(LOG_TAG, String.format(Locale.US, + "SplashScreen checking exception for package %s (target sdk:%d) -> %s", + packageName, targetSdk, + mDeviceConfigExcludedPackages.contains(packageName))); + } + if (!mDeviceConfigExcludedPackages.contains(packageName)) { + return false; + } + } + return !isOptedOut(infoSupplier); + } + + /** + * An application can manually opt-out of the exception list by setting the meta-data + * {@value OPT_OUT_METADATA_FLAG} = true in the <code>application</code> section of the manifest + */ + private static boolean isOptedOut(@Nullable Supplier<ApplicationInfo> infoProvider) { + if (infoProvider == null) { + return false; + } + ApplicationInfo info = infoProvider.get(); + return info != null && info.metaData != null && info.metaData.getBoolean( + OPT_OUT_METADATA_FLAG, false); + } + + private void parseDeviceConfigPackageList(String rawList) { + synchronized (mLock) { + mDeviceConfigExcludedPackages.clear(); + String[] packages = rawList.split(","); + for (String packageName : packages) { + String packageNameTrimmed = packageName.trim(); + if (!packageNameTrimmed.isEmpty()) { + mDeviceConfigExcludedPackages.add(packageNameTrimmed); + } + } + } + } +} diff --git a/services/core/java/com/android/server/wm/StartingSurfaceController.java b/services/core/java/com/android/server/wm/StartingSurfaceController.java index 39ff94038024..da257477b1d8 100644 --- a/services/core/java/com/android/server/wm/StartingSurfaceController.java +++ b/services/core/java/com/android/server/wm/StartingSurfaceController.java @@ -26,6 +26,9 @@ import static android.window.StartingWindowInfo.TYPE_PARAMETER_USE_EMPTY_SPLASH_ import static com.android.server.wm.WindowManagerDebugConfig.TAG_WITH_CLASS_NAME; import static com.android.server.wm.WindowManagerDebugConfig.TAG_WM; +import android.annotation.NonNull; +import android.annotation.Nullable; +import android.content.pm.ApplicationInfo; import android.content.res.CompatibilityInfo; import android.content.res.Configuration; import android.os.SystemProperties; @@ -34,6 +37,8 @@ import android.window.TaskSnapshot; import com.android.server.policy.WindowManagerPolicy.StartingSurface; +import java.util.function.Supplier; + /** * Managing to create and release a starting window surface. */ @@ -44,9 +49,11 @@ public class StartingSurfaceController { static final boolean DEBUG_ENABLE_SHELL_DRAWER = SystemProperties.getBoolean("persist.debug.shell_starting_surface", true); private final WindowManagerService mService; + private final SplashScreenExceptionList mSplashScreenExceptionsList; public StartingSurfaceController(WindowManagerService wm) { mService = wm; + mSplashScreenExceptionsList = new SplashScreenExceptionList(wm.mContext.getMainExecutor()); } StartingSurface createSplashScreenStartingSurface(ActivityRecord activity, String packageName, @@ -68,6 +75,14 @@ public class StartingSurfaceController { return null; } + /** + * @see SplashScreenExceptionList#isException(String, int, Supplier) + */ + boolean isExceptionApp(@NonNull String packageName, int targetSdk, + @Nullable Supplier<ApplicationInfo> infoProvider) { + return mSplashScreenExceptionsList.isException(packageName, targetSdk, infoProvider); + } + int makeStartingWindowTypeParameter(boolean newTask, boolean taskSwitch, boolean processRunning, boolean allowTaskSnapshot, boolean activityCreated, boolean useEmpty) { diff --git a/services/core/java/com/android/server/wm/TaskSnapshotController.java b/services/core/java/com/android/server/wm/TaskSnapshotController.java index 0000f95e7849..e74371036619 100644 --- a/services/core/java/com/android/server/wm/TaskSnapshotController.java +++ b/services/core/java/com/android/server/wm/TaskSnapshotController.java @@ -644,7 +644,7 @@ class TaskSnapshotController { /** Called when the device is going to sleep (e.g. screen off, AOD without screen off). */ void snapshotForSleeping(int displayId) { - if (shouldDisableSnapshots()) { + if (shouldDisableSnapshots() || !mService.mDisplayEnabled) { return; } final DisplayContent displayContent = mService.mRoot.getDisplayContent(displayId); diff --git a/services/core/java/com/android/server/wm/WindowManagerService.java b/services/core/java/com/android/server/wm/WindowManagerService.java index 147260b9e912..107580686b81 100644 --- a/services/core/java/com/android/server/wm/WindowManagerService.java +++ b/services/core/java/com/android/server/wm/WindowManagerService.java @@ -483,10 +483,7 @@ public class WindowManagerService extends IWindowManager.Stub private final DisplayAreaPolicy.Provider mDisplayAreaPolicyProvider; final private KeyguardDisableHandler mKeyguardDisableHandler; - // TODO: eventually unify all keyguard state in a common place instead of having it spread over - // AM's KeyguardController and the policy's KeyguardServiceDelegate. - boolean mKeyguardGoingAway; - boolean mKeyguardOrAodShowingOnDefaultDisplay; + // VR Vr2d Display Id. int mVr2dDisplayId = INVALID_DISPLAY; boolean mVrModeEnabled = false; @@ -3072,17 +3069,6 @@ public class WindowManagerService extends IWindowManager.Stub mAtmInternal.notifyKeyguardFlagsChanged(callback, displayId); } - public void setKeyguardGoingAway(boolean keyguardGoingAway) { - synchronized (mGlobalLock) { - mKeyguardGoingAway = keyguardGoingAway; - } - } - - public void setKeyguardOrAodShowingOnDefaultDisplay(boolean showing) { - synchronized (mGlobalLock) { - mKeyguardOrAodShowingOnDefaultDisplay = showing; - } - } // ------------------------------------------------------------- // Misc IWindowSession methods diff --git a/services/tests/PackageManagerServiceTests/host/src/com/android/server/pm/test/BootTest.kt b/services/tests/PackageManagerServiceTests/host/src/com/android/server/pm/test/BootTest.kt new file mode 100644 index 000000000000..7fb49078b2af --- /dev/null +++ b/services/tests/PackageManagerServiceTests/host/src/com/android/server/pm/test/BootTest.kt @@ -0,0 +1,77 @@ +/* + * 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.test + +import com.android.tradefed.testtype.DeviceJUnit4ClassRunner +import com.android.tradefed.testtype.junit4.BaseHostJUnit4Test +import com.google.common.truth.Truth +import org.junit.After +import org.junit.Before +import org.junit.Rule +import org.junit.Test +import org.junit.rules.TemporaryFolder +import org.junit.runner.RunWith + +@RunWith(DeviceJUnit4ClassRunner::class) +class BootTest : BaseHostJUnit4Test() { + companion object { + private const val TEST_PKG_NAME = "com.android.server.pm.test.test_app" + private const val TEST_APK_NAME = "PackageManagerTestAppStub.apk" + } + + @Rule + @JvmField + val tempFolder = TemporaryFolder() + + @Before + fun installApk() { + val testApkFile = HostUtils.copyResourceToHostFile(TEST_APK_NAME, tempFolder.newFile()) + device.installPackage(testApkFile, true) + } + + @After + fun removeApk() { + device.uninstallPackage(TEST_PKG_NAME) + } + + @Test + fun testUninstallPackageWithKeepDataAndReboot() { + Truth.assertThat(isPackageInstalled(TEST_PKG_NAME)).isTrue() + uninstallPackageWithKeepData(TEST_PKG_NAME) + Truth.assertThat(isPackageInstalled(TEST_PKG_NAME)).isFalse() + device.rebootUntilOnline() + waitForBootCompleted() + } + + private fun uninstallPackageWithKeepData(packageName: String) { + device.executeShellCommand("pm uninstall -k $packageName") + } + + private fun waitForBootCompleted() { + for (i in 0 until 45) { + if (isBootCompleted()) { + return + } + Thread.sleep(1000) + } + throw AssertionError("System failed to become ready!") + } + + private fun isBootCompleted(): Boolean { + return "1" == device.executeShellCommand("getprop sys.boot_completed").trim() + } +}
\ No newline at end of file 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 eab1afbd931e..583797e69995 100644 --- a/services/tests/mockingservicestests/src/com/android/server/alarm/AlarmManagerServiceTest.java +++ b/services/tests/mockingservicestests/src/com/android/server/alarm/AlarmManagerServiceTest.java @@ -1910,17 +1910,6 @@ public class AlarmManagerServiceTest { assertTrue(mBinder.hasScheduleExactAlarm(TEST_CALLING_PACKAGE, TEST_CALLING_USER)); } - @Test - public void hasScheduleExactAlarmBinderCallChangeDisabled() throws RemoteException { - mockChangeEnabled(AlarmManager.REQUIRE_EXACT_ALARM_PERMISSION, false); - - mockExactAlarmPermissionGrant(false, true, MODE_DEFAULT); - assertTrue(mBinder.hasScheduleExactAlarm(TEST_CALLING_PACKAGE, TEST_CALLING_USER)); - - mockExactAlarmPermissionGrant(true, false, MODE_ERRORED); - assertTrue(mBinder.hasScheduleExactAlarm(TEST_CALLING_PACKAGE, TEST_CALLING_USER)); - } - private void mockChangeEnabled(long changeId, boolean enabled) { doReturn(enabled).when(() -> CompatChanges.isChangeEnabled(eq(changeId), anyString(), any(UserHandle.class))); @@ -1941,6 +1930,53 @@ public class AlarmManagerServiceTest { } @Test + public void canScheduleExactAlarmsBinderCallChangeDisabled() throws RemoteException { + mockChangeEnabled(AlarmManager.REQUIRE_EXACT_ALARM_PERMISSION, false); + + mockExactAlarmPermissionGrant(false, true, MODE_DEFAULT); + assertTrue(mBinder.canScheduleExactAlarms(TEST_CALLING_PACKAGE)); + + mockExactAlarmPermissionGrant(true, false, MODE_ERRORED); + assertTrue(mBinder.canScheduleExactAlarms(TEST_CALLING_PACKAGE)); + } + + @Test + public void canScheduleExactAlarmsBinderCall() throws RemoteException { + mockChangeEnabled(AlarmManager.REQUIRE_EXACT_ALARM_PERMISSION, true); + + // No permission, no exemption. + mockExactAlarmPermissionGrant(true, true, MODE_DEFAULT); + assertFalse(mBinder.canScheduleExactAlarms(TEST_CALLING_PACKAGE)); + + // No permission, no exemption. + mockExactAlarmPermissionGrant(true, false, MODE_ERRORED); + assertFalse(mBinder.canScheduleExactAlarms(TEST_CALLING_PACKAGE)); + + // Permission, no exemption. + mockExactAlarmPermissionGrant(true, false, MODE_DEFAULT); + assertTrue(mBinder.canScheduleExactAlarms(TEST_CALLING_PACKAGE)); + + // Permission, no exemption. + mockExactAlarmPermissionGrant(true, true, MODE_ALLOWED); + assertTrue(mBinder.canScheduleExactAlarms(TEST_CALLING_PACKAGE)); + + // No permission, exemption. + mockExactAlarmPermissionGrant(true, false, MODE_ERRORED); + when(mDeviceIdleInternal.isAppOnWhitelist(TEST_CALLING_UID)).thenReturn(true); + assertTrue(mBinder.canScheduleExactAlarms(TEST_CALLING_PACKAGE)); + + // No permission, exemption. + mockExactAlarmPermissionGrant(true, false, MODE_ERRORED); + when(mDeviceIdleInternal.isAppOnWhitelist(TEST_CALLING_UID)).thenReturn(false); + doReturn(true).when(() -> UserHandle.isCore(TEST_CALLING_UID)); + assertTrue(mBinder.canScheduleExactAlarms(TEST_CALLING_PACKAGE)); + + // Both permission and exemption. + mockExactAlarmPermissionGrant(true, false, MODE_ALLOWED); + assertTrue(mBinder.canScheduleExactAlarms(TEST_CALLING_PACKAGE)); + } + + @Test public void noPermissionCheckWhenChangeDisabled() throws RemoteException { mockChangeEnabled(AlarmManager.REQUIRE_EXACT_ALARM_PERMISSION, false); @@ -2086,14 +2122,17 @@ public class AlarmManagerServiceTest { final PendingIntent alarmPi = getNewMockPendingIntent(); final AlarmManager.AlarmClockInfo alarmClock = mock(AlarmManager.AlarmClockInfo.class); - try { - mBinder.set(TEST_CALLING_PACKAGE, RTC_WAKEUP, 1234, WINDOW_EXACT, 0, 0, + mBinder.set(TEST_CALLING_PACKAGE, RTC_WAKEUP, 1234, WINDOW_EXACT, 0, 0, alarmPi, null, null, null, alarmClock); - fail("alarm clock binder call succeeded without permission"); - } catch (SecurityException se) { - // Expected. - } - verify(mDeviceIdleInternal, never()).isAppOnWhitelist(anyInt()); + + // Correct permission checks are invoked. + verify(mService).hasScheduleExactAlarmInternal(TEST_CALLING_PACKAGE, TEST_CALLING_UID); + verify(mDeviceIdleInternal).isAppOnWhitelist(UserHandle.getAppId(TEST_CALLING_UID)); + + verify(mService).setImpl(eq(RTC_WAKEUP), eq(1234L), eq(WINDOW_EXACT), eq(0L), + eq(alarmPi), isNull(), isNull(), eq(FLAG_STANDALONE | FLAG_WAKE_FROM_IDLE), + isNull(), eq(alarmClock), eq(TEST_CALLING_UID), eq(TEST_CALLING_PACKAGE), + isNull(), eq(EXACT_ALLOW_REASON_ALLOW_LIST)); } @Test diff --git a/services/tests/servicestests/src/com/android/server/pm/AppsFilterTest.java b/services/tests/servicestests/src/com/android/server/pm/AppsFilterTest.java index dc745cdc0c84..67dd0556e098 100644 --- a/services/tests/servicestests/src/com/android/server/pm/AppsFilterTest.java +++ b/services/tests/servicestests/src/com/android/server/pm/AppsFilterTest.java @@ -83,17 +83,9 @@ public class AppsFilterTest { private static final int DUMMY_OVERLAY_APPID = 10756; private static final int SYSTEM_USER = 0; private static final int SECONDARY_USER = 10; - private static final int ADDED_USER = 11; private static final int[] USER_ARRAY = {SYSTEM_USER, SECONDARY_USER}; - private static final int[] USER_ARRAY_WITH_ADDED = {SYSTEM_USER, SECONDARY_USER, ADDED_USER}; - private static final UserInfo[] USER_INFO_LIST = toUserInfos(USER_ARRAY); - private static final UserInfo[] USER_INFO_LIST_WITH_ADDED = toUserInfos(USER_ARRAY_WITH_ADDED); - - private static UserInfo[] toUserInfos(int[] userIds) { - return Arrays.stream(userIds) - .mapToObj(id -> new UserInfo(id, Integer.toString(id), 0)) - .toArray(UserInfo[]::new); - } + private static final UserInfo[] USER_INFO_LIST = Arrays.stream(USER_ARRAY).mapToObj( + id -> new UserInfo(id, Integer.toString(id), 0)).toArray(UserInfo[]::new); @Mock AppsFilter.FeatureConfig mFeatureConfigMock; @@ -327,47 +319,6 @@ public class AppsFilterTest { } @Test - public void testOnUserCreated_FilterMatches() throws Exception { - final AppsFilter appsFilter = - new AppsFilter(mStateProvider, mFeatureConfigMock, new String[]{}, false, null, - mMockExecutor); - simulateAddBasicAndroid(appsFilter); - - appsFilter.onSystemReady(); - - PackageSetting target = simulateAddPackage(appsFilter, - pkgWithProvider("com.some.package", "com.some.authority"), DUMMY_TARGET_APPID); - PackageSetting calling = simulateAddPackage(appsFilter, - pkgQueriesProvider("com.some.other.package", "com.some.authority"), - DUMMY_CALLING_APPID); - - for (int subjectUserId : USER_ARRAY) { - for (int otherUserId : USER_ARRAY) { - assertFalse(appsFilter.shouldFilterApplication( - UserHandle.getUid(DUMMY_CALLING_APPID, subjectUserId), calling, target, - otherUserId)); - } - } - - // adds new user - doAnswer(invocation -> { - ((AppsFilter.StateProvider.CurrentStateCallback) invocation.getArgument(0)) - .currentState(mExisting, USER_INFO_LIST_WITH_ADDED); - return new Object(); - }).when(mStateProvider) - .runWithState(any(AppsFilter.StateProvider.CurrentStateCallback.class)); - appsFilter.onUserCreated(ADDED_USER); - - for (int subjectUserId : USER_ARRAY_WITH_ADDED) { - for (int otherUserId : USER_ARRAY_WITH_ADDED) { - assertFalse(appsFilter.shouldFilterApplication( - UserHandle.getUid(DUMMY_CALLING_APPID, subjectUserId), calling, target, - otherUserId)); - } - } - } - - @Test public void testQueriesDifferentProvider_Filters() throws Exception { final AppsFilter appsFilter = new AppsFilter(mStateProvider, mFeatureConfigMock, new String[]{}, false, null, diff --git a/services/tests/wmtests/src/com/android/server/wm/AppTransitionControllerTest.java b/services/tests/wmtests/src/com/android/server/wm/AppTransitionControllerTest.java index 57cf865268ef..c555612fef23 100644 --- a/services/tests/wmtests/src/com/android/server/wm/AppTransitionControllerTest.java +++ b/services/tests/wmtests/src/com/android/server/wm/AppTransitionControllerTest.java @@ -78,6 +78,24 @@ public class AppTransitionControllerTest extends WindowTestsBase { } @Test + public void testSkipOccludedActivityCloseTransition() { + final ActivityRecord behind = createActivityRecord(mDisplayContent, + WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_STANDARD); + final ActivityRecord topOpening = createActivityRecord(behind.getTask()); + topOpening.setOccludesParent(true); + topOpening.setVisible(true); + + mDisplayContent.prepareAppTransition(TRANSIT_OPEN); + mDisplayContent.prepareAppTransition(TRANSIT_CLOSE); + mDisplayContent.mClosingApps.add(behind); + + assertEquals(WindowManager.TRANSIT_OLD_UNSET, + AppTransitionController.getTransitCompatType(mDisplayContent.mAppTransition, + mDisplayContent.mOpeningApps, mDisplayContent.mClosingApps, + null, null, false)); + } + + @Test @FlakyTest(bugId = 131005232) public void testTranslucentOpen() { final ActivityRecord behind = createActivityRecord(mDisplayContent, diff --git a/services/tests/wmtests/src/com/android/server/wm/DisplayContentTests.java b/services/tests/wmtests/src/com/android/server/wm/DisplayContentTests.java index 5bc4c82f8d43..d498d4663d78 100644 --- a/services/tests/wmtests/src/com/android/server/wm/DisplayContentTests.java +++ b/services/tests/wmtests/src/com/android/server/wm/DisplayContentTests.java @@ -100,7 +100,6 @@ import static org.mockito.Mockito.clearInvocations; import static org.mockito.Mockito.doAnswer; import static org.mockito.Mockito.doCallRealMethod; -import android.annotation.SuppressLint; import android.app.ActivityTaskManager; import android.app.WindowConfiguration; import android.app.servertransaction.FixedRotationAdjustmentsItem; @@ -792,15 +791,9 @@ public class DisplayContentTests extends WindowTestsBase { } @Test - @SuppressLint("InlinedApi") public void testOrientationDefinedByKeyguard() { - final DisplayContent dc = createNewDisplay(); - - // When display content is created its configuration is not yet initialized, which could - // cause unnecessary configuration propagation, so initialize it here. - final Configuration config = new Configuration(); - dc.computeScreenConfiguration(config); - dc.onRequestedOverrideConfigurationChanged(config); + final DisplayContent dc = mDisplayContent; + dc.getDisplayPolicy().setAwake(true); // Create a window that requests landscape orientation. It will define device orientation // by default. @@ -815,10 +808,12 @@ public class DisplayContentTests extends WindowTestsBase { SCREEN_ORIENTATION_LANDSCAPE, dc.getOrientation()); keyguard.mAttrs.screenOrientation = SCREEN_ORIENTATION_PORTRAIT; + mAtm.mKeyguardController.setKeyguardShown(true /* keyguardShowing */, + false /* aodShowing */); assertEquals("Visible keyguard must influence device orientation", SCREEN_ORIENTATION_PORTRAIT, dc.getOrientation()); - mWm.setKeyguardGoingAway(true); + mAtm.mKeyguardController.keyguardGoingAway(0 /* flags */); assertEquals("Keyguard that is going away must not influence device orientation", SCREEN_ORIENTATION_LANDSCAPE, dc.getOrientation()); } diff --git a/services/tests/wmtests/src/com/android/server/wm/SplashScreenExceptionListTest.java b/services/tests/wmtests/src/com/android/server/wm/SplashScreenExceptionListTest.java new file mode 100644 index 000000000000..3714d9984a0c --- /dev/null +++ b/services/tests/wmtests/src/com/android/server/wm/SplashScreenExceptionListTest.java @@ -0,0 +1,151 @@ +/* + * 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.wm; + +import static android.os.Build.VERSION_CODES; + +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertTrue; + +import android.content.pm.ApplicationInfo; +import android.os.Bundle; +import android.os.Handler; +import android.os.HandlerExecutor; +import android.os.Looper; +import android.platform.test.annotations.Presubmit; +import android.provider.DeviceConfig; + +import androidx.test.filters.MediumTest; + +import junit.framework.Assert; + +import org.junit.After; +import org.junit.Before; +import org.junit.Test; + +import java.util.concurrent.CountDownLatch; +import java.util.concurrent.TimeUnit; + +/** + * Test for the splash screen exception list + * atest WmTests:SplashScreenExceptionListTest + */ +@MediumTest +@Presubmit +public class SplashScreenExceptionListTest { + + // Constant copied on purpose so it's not refactored by accident. + // If the key needs to be modified, the server side key also needs to be changed. + private static final String KEY_SPLASH_SCREEN_EXCEPTION_LIST = "splash_screen_exception_list"; + + private DeviceConfig.Properties mInitialWindowManagerProperties; + private final HandlerExecutor mExecutor = new HandlerExecutor( + new Handler(Looper.getMainLooper())); + private final SplashScreenExceptionList mList = new SplashScreenExceptionList(mExecutor); + + @Before + public void setUp() throws Exception { + mInitialWindowManagerProperties = DeviceConfig.getProperties( + DeviceConfig.NAMESPACE_WINDOW_MANAGER); + clearConstrainDisplayApisFlags(); + } + + private void clearConstrainDisplayApisFlags() { + DeviceConfig.setProperty(DeviceConfig.NAMESPACE_WINDOW_MANAGER, + KEY_SPLASH_SCREEN_EXCEPTION_LIST, + null, /* makeDefault= */ false); + } + + @After + public void tearDown() throws Exception { + DeviceConfig.setProperties(mInitialWindowManagerProperties); + DeviceConfig.removeOnPropertiesChangedListener(mList.mOnPropertiesChangedListener); + } + + @Test + public void packageFromDeviceConfigIgnored() { + setExceptionListAndWaitForCallback("com.test.nosplashscreen1,com.test.nosplashscreen2"); + + assertIsException("com.test.nosplashscreen1", null); + assertIsException("com.test.nosplashscreen2", null); + + assertIsNotException("com.test.nosplashscreen1", VERSION_CODES.S, null); + assertIsNotException("com.test.nosplashscreen2", VERSION_CODES.S, null); + assertIsNotException("com.test.splashscreen", VERSION_CODES.S, null); + assertIsNotException("com.test.splashscreen", VERSION_CODES.R, null); + } + + private void setExceptionListAndWaitForCallback(String commaSeparatedList) { + CountDownLatch latch = new CountDownLatch(1); + DeviceConfig.OnPropertiesChangedListener onPropertiesChangedListener = (p) -> { + if (commaSeparatedList.equals(p.getString(KEY_SPLASH_SCREEN_EXCEPTION_LIST, null))) { + latch.countDown(); + } + }; + DeviceConfig.addOnPropertiesChangedListener(DeviceConfig.NAMESPACE_WINDOW_MANAGER, + mExecutor, onPropertiesChangedListener); + DeviceConfig.setProperty(DeviceConfig.NAMESPACE_WINDOW_MANAGER, + KEY_SPLASH_SCREEN_EXCEPTION_LIST, commaSeparatedList, false); + try { + assertTrue("Timed out waiting for DeviceConfig to be updated.", + latch.await(1, TimeUnit.SECONDS)); + } catch (InterruptedException e) { + Assert.fail(e.getMessage()); + } finally { + DeviceConfig.removeOnPropertiesChangedListener(onPropertiesChangedListener); + } + } + + @Test + public void metaDataOptOut() { + String packageName = "com.test.nosplashscreen_opt_out"; + setExceptionListAndWaitForCallback(packageName); + + Bundle metaData = new Bundle(); + ApplicationInfo activityInfo = new ApplicationInfo(); + activityInfo.metaData = metaData; + + // No Exceptions + metaData.putBoolean("android.splashscreen.exception_opt_out", true); + assertIsNotException(packageName, VERSION_CODES.R, activityInfo); + assertIsNotException(packageName, VERSION_CODES.S, activityInfo); + + // Exception Pre S + metaData.putBoolean("android.splashscreen.exception_opt_out", false); + assertIsException(packageName, activityInfo); + assertIsNotException(packageName, VERSION_CODES.S, activityInfo); + + // Edge Cases + activityInfo.metaData = null; + assertIsException(packageName, activityInfo); + assertIsException(packageName, null); + } + + private void assertIsNotException(String packageName, int targetSdk, + ApplicationInfo activityInfo) { + assertFalse(String.format("%s (sdk=%d) should have not been considered as an exception", + packageName, targetSdk), + mList.isException(packageName, targetSdk, () -> activityInfo)); + } + + private void assertIsException(String packageName, + ApplicationInfo activityInfo) { + assertTrue(String.format("%s (sdk=%d) should have been considered as an exception", + packageName, VERSION_CODES.R), + mList.isException(packageName, VERSION_CODES.R, () -> activityInfo)); + } +} diff --git a/services/voiceinteraction/java/com/android/server/voiceinteraction/HotwordDetectionConnection.java b/services/voiceinteraction/java/com/android/server/voiceinteraction/HotwordDetectionConnection.java index beaca68b9a37..fc10fb104262 100644 --- a/services/voiceinteraction/java/com/android/server/voiceinteraction/HotwordDetectionConnection.java +++ b/services/voiceinteraction/java/com/android/server/voiceinteraction/HotwordDetectionConnection.java @@ -79,7 +79,7 @@ import java.util.function.Function; final class HotwordDetectionConnection { private static final String TAG = "HotwordDetectionConnection"; // TODO (b/177502877): Set the Debug flag to false before shipping. - private static final boolean DEBUG = true; + static final boolean DEBUG = true; // TODO: These constants need to be refined. private static final long VALIDATION_TIMEOUT_MILLIS = 3000; diff --git a/services/voiceinteraction/java/com/android/server/voiceinteraction/SoundTriggerSessionBinderProxy.java b/services/voiceinteraction/java/com/android/server/voiceinteraction/SoundTriggerSessionBinderProxy.java new file mode 100644 index 000000000000..dd9fee3887cb --- /dev/null +++ b/services/voiceinteraction/java/com/android/server/voiceinteraction/SoundTriggerSessionBinderProxy.java @@ -0,0 +1,72 @@ +/* + * 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.voiceinteraction; + +import android.hardware.soundtrigger.SoundTrigger; +import android.os.RemoteException; + +import com.android.internal.app.IHotwordRecognitionStatusCallback; +import com.android.internal.app.IVoiceInteractionSoundTriggerSession; + +/** + * A remote object that simply proxies calls to a real {@link IVoiceInteractionSoundTriggerSession} + * implementation. This design pattern allows us to add decorators to the core implementation + * (simply wrapping a binder object does not work). + */ +final class SoundTriggerSessionBinderProxy extends IVoiceInteractionSoundTriggerSession.Stub { + + private final IVoiceInteractionSoundTriggerSession mDelegate; + + SoundTriggerSessionBinderProxy(IVoiceInteractionSoundTriggerSession delegate) { + mDelegate = delegate; + } + + @Override + public SoundTrigger.ModuleProperties getDspModuleProperties() throws RemoteException { + return mDelegate.getDspModuleProperties(); + } + + @Override + public int startRecognition(int i, String s, + IHotwordRecognitionStatusCallback iHotwordRecognitionStatusCallback, + SoundTrigger.RecognitionConfig recognitionConfig, boolean b) throws RemoteException { + return mDelegate.startRecognition(i, s, iHotwordRecognitionStatusCallback, + recognitionConfig, b); + } + + @Override + public int stopRecognition(int i, + IHotwordRecognitionStatusCallback iHotwordRecognitionStatusCallback) + throws RemoteException { + return mDelegate.stopRecognition(i, iHotwordRecognitionStatusCallback); + } + + @Override + public int setParameter(int i, int i1, int i2) throws RemoteException { + return mDelegate.setParameter(i, i1, i2); + } + + @Override + public int getParameter(int i, int i1) throws RemoteException { + return mDelegate.getParameter(i, i1); + } + + @Override + public SoundTrigger.ModelParamRange queryParameter(int i, int i1) throws RemoteException { + return mDelegate.queryParameter(i, i1); + } +} diff --git a/services/voiceinteraction/java/com/android/server/voiceinteraction/SoundTriggerSessionPermissionsDecorator.java b/services/voiceinteraction/java/com/android/server/voiceinteraction/SoundTriggerSessionPermissionsDecorator.java new file mode 100644 index 000000000000..bb7ca168fcaa --- /dev/null +++ b/services/voiceinteraction/java/com/android/server/voiceinteraction/SoundTriggerSessionPermissionsDecorator.java @@ -0,0 +1,158 @@ +/* + * 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.voiceinteraction; + +import static android.Manifest.permission.CAPTURE_AUDIO_HOTWORD; +import static android.Manifest.permission.RECORD_AUDIO; + +import static com.android.server.voiceinteraction.HotwordDetectionConnection.DEBUG; + +import android.annotation.NonNull; +import android.content.Context; +import android.content.PermissionChecker; +import android.hardware.soundtrigger.SoundTrigger; +import android.media.permission.Identity; +import android.media.permission.PermissionUtil; +import android.os.IBinder; +import android.os.RemoteException; +import android.os.ServiceSpecificException; +import android.text.TextUtils; +import android.util.Slog; + +import com.android.internal.app.IHotwordRecognitionStatusCallback; +import com.android.internal.app.IVoiceInteractionSoundTriggerSession; + +/** + * Decorates {@link IVoiceInteractionSoundTriggerSession} with permission checks for {@link + * android.Manifest.permission#RECORD_AUDIO} and + * {@link android.Manifest.permission#CAPTURE_AUDIO_HOTWORD}. + * <p> + * Does not implement {@link #asBinder()} as it's intended to be wrapped by an + * {@link IVoiceInteractionSoundTriggerSession.Stub} object. + */ +final class SoundTriggerSessionPermissionsDecorator implements + IVoiceInteractionSoundTriggerSession { + static final String TAG = "SoundTriggerSessionPermissionsDecorator"; + + private final IVoiceInteractionSoundTriggerSession mDelegate; + private final Context mContext; + private final Identity mOriginatorIdentity; + + SoundTriggerSessionPermissionsDecorator(IVoiceInteractionSoundTriggerSession delegate, + Context context, Identity originatorIdentity) { + mDelegate = delegate; + mContext = context; + mOriginatorIdentity = originatorIdentity; + } + + @Override + public SoundTrigger.ModuleProperties getDspModuleProperties() throws RemoteException { + // No permission needed. + return mDelegate.getDspModuleProperties(); + } + + @Override + public int startRecognition(int i, String s, + IHotwordRecognitionStatusCallback iHotwordRecognitionStatusCallback, + SoundTrigger.RecognitionConfig recognitionConfig, boolean b) throws RemoteException { + if (DEBUG) { + Slog.d(TAG, "startRecognition"); + } + enforcePermissions(); + return mDelegate.startRecognition(i, s, iHotwordRecognitionStatusCallback, + recognitionConfig, b); + } + + @Override + public int stopRecognition(int i, + IHotwordRecognitionStatusCallback iHotwordRecognitionStatusCallback) + throws RemoteException { + enforcePermissions(); + return mDelegate.stopRecognition(i, iHotwordRecognitionStatusCallback); + } + + @Override + public int setParameter(int i, int i1, int i2) throws RemoteException { + enforcePermissions(); + return mDelegate.setParameter(i, i1, i2); + } + + @Override + public int getParameter(int i, int i1) throws RemoteException { + enforcePermissions(); + return mDelegate.getParameter(i, i1); + } + + @Override + public SoundTrigger.ModelParamRange queryParameter(int i, int i1) throws RemoteException { + enforcePermissions(); + return mDelegate.queryParameter(i, i1); + } + + @Override + public IBinder asBinder() { + throw new UnsupportedOperationException( + "This object isn't intended to be used as a Binder."); + } + + // TODO: Share this code with SoundTriggerMiddlewarePermission. + private void enforcePermissions() { + enforcePermissionForPreflight(mContext, mOriginatorIdentity, RECORD_AUDIO); + enforcePermissionForPreflight(mContext, mOriginatorIdentity, CAPTURE_AUDIO_HOTWORD); + } + + /** + * Throws a {@link SecurityException} if originator permanently doesn't have the given + * permission, or a {@link ServiceSpecificException} with a {@link + * #TEMPORARY_PERMISSION_DENIED} if caller originator doesn't have the given permission. + * + * @param context A {@link Context}, used for permission checks. + * @param identity The identity to check. + * @param permission The identifier of the permission we want to check. + */ + private static void enforcePermissionForPreflight(@NonNull Context context, + @NonNull Identity identity, @NonNull String permission) { + final int status = PermissionUtil.checkPermissionForPreflight(context, identity, + permission); + switch (status) { + case PermissionChecker.PERMISSION_GRANTED: + return; + case PermissionChecker.PERMISSION_HARD_DENIED: + throw new SecurityException( + TextUtils.formatSimple("Failed to obtain permission %s for identity %s", + permission, toString(identity))); + case PermissionChecker.PERMISSION_SOFT_DENIED: + throw new ServiceSpecificException(TEMPORARY_PERMISSION_DENIED, + TextUtils.formatSimple("Failed to obtain permission %s for identity %s", + permission, toString(identity))); + default: + throw new RuntimeException("Unexpected permission check result."); + } + } + + private static String toString(Identity identity) { + return "{uid=" + identity.uid + + " pid=" + identity.pid + + " packageName=" + identity.packageName + + " attributionTag=" + identity.attributionTag + + "}"; + } + + // Temporary hack for using the same status code as SoundTrigger, so we don't change behavior. + // TODO: Reuse SoundTrigger code so we don't need to do this. + private static final int TEMPORARY_PERMISSION_DENIED = 3; +} diff --git a/services/voiceinteraction/java/com/android/server/voiceinteraction/VoiceInteractionManagerService.java b/services/voiceinteraction/java/com/android/server/voiceinteraction/VoiceInteractionManagerService.java index 162acba8002d..91d17f74c676 100644 --- a/services/voiceinteraction/java/com/android/server/voiceinteraction/VoiceInteractionManagerService.java +++ b/services/voiceinteraction/java/com/android/server/voiceinteraction/VoiceInteractionManagerService.java @@ -23,6 +23,7 @@ import android.annotation.Nullable; import android.annotation.UserIdInt; import android.app.ActivityManager; import android.app.ActivityManagerInternal; +import android.app.ActivityThread; import android.app.AppGlobals; import android.app.role.OnRoleHoldersChangedListener; import android.app.role.RoleManager; @@ -50,6 +51,7 @@ import android.hardware.soundtrigger.SoundTrigger.ModuleProperties; import android.hardware.soundtrigger.SoundTrigger.RecognitionConfig; import android.media.AudioFormat; import android.media.permission.Identity; +import android.media.permission.IdentityContext; import android.media.permission.PermissionUtil; import android.media.permission.SafeCloseable; import android.os.Binder; @@ -59,6 +61,7 @@ import android.os.IBinder; import android.os.Parcel; import android.os.ParcelFileDescriptor; import android.os.PersistableBundle; +import android.os.Process; import android.os.RemoteCallback; import android.os.RemoteCallbackList; import android.os.RemoteException; @@ -104,11 +107,8 @@ import com.android.server.wm.ActivityTaskManagerInternal; import java.io.FileDescriptor; import java.io.PrintWriter; -import java.lang.ref.WeakReference; import java.util.ArrayList; -import java.util.LinkedList; import java.util.List; -import java.util.ListIterator; import java.util.Locale; import java.util.Objects; import java.util.concurrent.Executor; @@ -288,7 +288,6 @@ public class VoiceInteractionManagerService extends SystemService { private boolean mTemporarilyDisabled; private final boolean mEnableService; - private final List<WeakReference<SoundTriggerSession>> mSessions = new LinkedList<>(); VoiceInteractionManagerServiceStub() { mEnableService = shouldEnableService(mContext); @@ -299,15 +298,49 @@ public class VoiceInteractionManagerService extends SystemService { public @NonNull IVoiceInteractionSoundTriggerSession createSoundTriggerSessionAsOriginator( @NonNull Identity originatorIdentity, IBinder client) { Objects.requireNonNull(originatorIdentity); - try (SafeCloseable ignored = PermissionUtil.establishIdentityDirect( - originatorIdentity)) { - SoundTriggerSession session = new SoundTriggerSession( - mSoundTriggerInternal.attach(client)); - synchronized (mSessions) { - mSessions.add(new WeakReference<>(session)); + boolean forHotwordDetectionService; + synchronized (VoiceInteractionManagerServiceStub.this) { + enforceIsCurrentVoiceInteractionService(); + forHotwordDetectionService = + mImpl != null && mImpl.mHotwordDetectionConnection != null; + } + IVoiceInteractionSoundTriggerSession session; + if (forHotwordDetectionService) { + // Use our own identity and handle the permission checks ourselves. This allows + // properly checking/noting against the voice interactor or hotword detection + // service as needed. + if (HotwordDetectionConnection.DEBUG) { + Slog.d(TAG, "Creating a SoundTriggerSession for a HotwordDetectionService"); + } + originatorIdentity.uid = Binder.getCallingUid(); + originatorIdentity.pid = Binder.getCallingPid(); + session = new SoundTriggerSessionPermissionsDecorator( + createSoundTriggerSessionForSelfIdentity(client), + mContext, + originatorIdentity); + } else { + if (HotwordDetectionConnection.DEBUG) { + Slog.d(TAG, "Creating a SoundTriggerSession"); + } + try (SafeCloseable ignored = PermissionUtil.establishIdentityDirect( + originatorIdentity)) { + session = new SoundTriggerSession(mSoundTriggerInternal.attach(client)); } - return session; } + return new SoundTriggerSessionBinderProxy(session); + } + + private IVoiceInteractionSoundTriggerSession createSoundTriggerSessionForSelfIdentity( + IBinder client) { + Identity identity = new Identity(); + identity.uid = Process.myUid(); + identity.pid = Process.myPid(); + identity.packageName = ActivityThread.currentOpPackageName(); + return Binder.withCleanCallingIdentity(() -> { + try (SafeCloseable ignored = IdentityContext.create(identity)) { + return new SoundTriggerSession(mSoundTriggerInternal.attach(client)); + } + }); } // TODO: VI Make sure the caller is the current user or profile @@ -1334,7 +1367,11 @@ public class VoiceInteractionManagerService extends SystemService { return null; } - class SoundTriggerSession extends IVoiceInteractionSoundTriggerSession.Stub { + /** + * Implementation of SoundTriggerSession. Does not implement {@link #asBinder()} as it's + * intended to be wrapped by an {@link IVoiceInteractionSoundTriggerSession.Stub} object. + */ + private class SoundTriggerSession implements IVoiceInteractionSoundTriggerSession { final SoundTriggerInternal.Session mSession; private IHotwordRecognitionStatusCallback mSessionExternalCallback; private IRecognitionStatusCallback mSessionInternalCallback; @@ -1481,6 +1518,12 @@ public class VoiceInteractionManagerService extends SystemService { } } + @Override + public IBinder asBinder() { + throw new UnsupportedOperationException( + "This object isn't intended to be used as a Binder."); + } + private int unloadKeyphraseModel(int keyphraseId) { final long caller = Binder.clearCallingIdentity(); try { @@ -1709,22 +1752,6 @@ public class VoiceInteractionManagerService extends SystemService { } mSoundTriggerInternal.dump(fd, pw, args); - - // Dump all sessions. - synchronized (mSessions) { - ListIterator<WeakReference<SoundTriggerSession>> iter = - mSessions.listIterator(); - while (iter.hasNext()) { - SoundTriggerSession session = iter.next().get(); - if (session != null) { - session.dump(fd, args); - } else { - // Session is obsolete, now is a good chance to remove it from the list. - iter.remove(); - } - } - } - } @Override diff --git a/telephony/java/android/telephony/CarrierConfigManager.java b/telephony/java/android/telephony/CarrierConfigManager.java index 7f1ea8fa9614..e03e74c7fdad 100644 --- a/telephony/java/android/telephony/CarrierConfigManager.java +++ b/telephony/java/android/telephony/CarrierConfigManager.java @@ -4370,7 +4370,7 @@ public class CarrierConfigManager { new String[] {}); defaults.putBoolean(KEY_ENABLE_PRESENCE_CAPABILITY_EXCHANGE_BOOL, false); defaults.putBoolean(KEY_RCS_BULK_CAPABILITY_EXCHANGE_BOOL, false); - defaults.putBoolean(KEY_ENABLE_PRESENCE_GROUP_SUBSCRIBE_BOOL, true); + defaults.putBoolean(KEY_ENABLE_PRESENCE_GROUP_SUBSCRIBE_BOOL, false); defaults.putInt(KEY_NON_RCS_CAPABILITIES_CACHE_EXPIRATION_SEC_INT, 30 * 24 * 60 * 60); defaults.putBoolean(KEY_RCS_REQUEST_FORBIDDEN_BY_SIP_489_BOOL, false); defaults.putLong(KEY_RCS_REQUEST_RETRY_INTERVAL_MILLIS_LONG, 20 * 60 * 1000); diff --git a/telephony/java/android/telephony/NetworkRegistrationInfo.java b/telephony/java/android/telephony/NetworkRegistrationInfo.java index 5fb60d7599ea..6a807665a103 100644 --- a/telephony/java/android/telephony/NetworkRegistrationInfo.java +++ b/telephony/java/android/telephony/NetworkRegistrationInfo.java @@ -506,6 +506,8 @@ public final class NetworkRegistrationInfo implements Parcelable { } /** + * Require {@link android.Manifest.permission#ACCESS_FINE_LOCATION}, otherwise return null. + * * @return The cell information. */ @Nullable diff --git a/telephony/java/android/telephony/ServiceState.java b/telephony/java/android/telephony/ServiceState.java index 6da61b712916..d745dc215f34 100644 --- a/telephony/java/android/telephony/ServiceState.java +++ b/telephony/java/android/telephony/ServiceState.java @@ -758,6 +758,11 @@ public class ServiceState implements Parcelable { * In GSM/UMTS, long format can be up to 16 characters long. * In CDMA, returns the ERI text, if set. Otherwise, returns the ONS. * + * Require at least {@link android.Manifest.permission#ACCESS_FINE_LOCATION} or + * {@link android.Manifest.permission#ACCESS_COARSE_LOCATION}. Otherwise return null if the + * caller does not hold neither {@link android.Manifest.permission#ACCESS_FINE_LOCATION} nor + * {@link android.Manifest.permission#ACCESS_COARSE_LOCATION}. + * * @return long name of operator, null if unregistered or unknown */ public String getOperatorAlphaLong() { @@ -766,6 +771,12 @@ public class ServiceState implements Parcelable { /** * Get current registered voice network operator name in long alphanumeric format. + * + * Require at least {@link android.Manifest.permission#ACCESS_FINE_LOCATION} or + * {@link android.Manifest.permission#ACCESS_COARSE_LOCATION}. Otherwise return null if the + * caller does not hold neither {@link android.Manifest.permission#ACCESS_FINE_LOCATION} nor + * {@link android.Manifest.permission#ACCESS_COARSE_LOCATION}. + * * @return long name of operator * @hide */ @@ -780,6 +791,11 @@ public class ServiceState implements Parcelable { * * In GSM/UMTS, short format can be up to 8 characters long. * + * Require at least {@link android.Manifest.permission#ACCESS_FINE_LOCATION} or + * {@link android.Manifest.permission#ACCESS_COARSE_LOCATION}. Otherwise return null if the + * caller does not hold neither {@link android.Manifest.permission#ACCESS_FINE_LOCATION} nor + * {@link android.Manifest.permission#ACCESS_COARSE_LOCATION}. + * * @return short name of operator, null if unregistered or unknown */ public String getOperatorAlphaShort() { @@ -788,6 +804,12 @@ public class ServiceState implements Parcelable { /** * Get current registered voice network operator name in short alphanumeric format. + * + * Require at least {@link android.Manifest.permission#ACCESS_FINE_LOCATION} or + * {@link android.Manifest.permission#ACCESS_COARSE_LOCATION}. Otherwise return null if the + * caller does not have neither {@link android.Manifest.permission#ACCESS_FINE_LOCATION} nor + * {@link android.Manifest.permission#ACCESS_COARSE_LOCATION}. + * * @return short name of operator, null if unregistered or unknown * @hide */ @@ -799,6 +821,12 @@ public class ServiceState implements Parcelable { /** * Get current registered data network operator name in short alphanumeric format. + * + * Require at least {@link android.Manifest.permission#ACCESS_FINE_LOCATION} or + * {@link android.Manifest.permission#ACCESS_COARSE_LOCATION}. Otherwise return null if the + * caller does not have neither {@link android.Manifest.permission#ACCESS_FINE_LOCATION} nor + * {@link android.Manifest.permission#ACCESS_COARSE_LOCATION}. + * * @return short name of operator, null if unregistered or unknown * @hide */ @@ -812,6 +840,11 @@ public class ServiceState implements Parcelable { * Get current registered operator name in long alphanumeric format if * available or short otherwise. * + * Require at least {@link android.Manifest.permission#ACCESS_FINE_LOCATION} or + * {@link android.Manifest.permission#ACCESS_COARSE_LOCATION}. Otherwise return null if the + * caller does not hold neither {@link android.Manifest.permission#ACCESS_FINE_LOCATION} nor + * {@link android.Manifest.permission#ACCESS_COARSE_LOCATION}. + * * @see #getOperatorAlphaLong * @see #getOperatorAlphaShort * @@ -832,6 +865,11 @@ public class ServiceState implements Parcelable { * In GSM/UMTS, numeric format is 3 digit country code plus 2 or 3 digit * network code. * + * Require at least {@link android.Manifest.permission#ACCESS_FINE_LOCATION} or + * {@link android.Manifest.permission#ACCESS_COARSE_LOCATION}. Otherwise return null if the + * caller does not hold neither {@link android.Manifest.permission#ACCESS_FINE_LOCATION} nor + * {@link android.Manifest.permission#ACCESS_COARSE_LOCATION}. + * * @return numeric format of operator, null if unregistered or unknown */ /* @@ -844,6 +882,12 @@ public class ServiceState implements Parcelable { /** * Get current registered voice network operator numeric id. + * + * Require at least {@link android.Manifest.permission#ACCESS_FINE_LOCATION} or + * {@link android.Manifest.permission#ACCESS_COARSE_LOCATION}. Otherwise return null if the + * caller does not hold neither {@link android.Manifest.permission#ACCESS_FINE_LOCATION} nor + * {@link android.Manifest.permission#ACCESS_COARSE_LOCATION}. + * * @return numeric format of operator, null if unregistered or unknown * @hide */ @@ -854,6 +898,12 @@ public class ServiceState implements Parcelable { /** * Get current registered data network operator numeric id. + * + * Require at least {@link android.Manifest.permission#ACCESS_FINE_LOCATION} or + * {@link android.Manifest.permission#ACCESS_COARSE_LOCATION}. Otherwise return null if the + * caller does not hold neither {@link android.Manifest.permission#ACCESS_FINE_LOCATION} nor + * {@link android.Manifest.permission#ACCESS_COARSE_LOCATION}. + * * @return numeric format of operator, null if unregistered or unknown * @hide */ diff --git a/telephony/java/android/telephony/TelephonyManager.java b/telephony/java/android/telephony/TelephonyManager.java index 64aa299411ff..e0ab0a368a4f 100644 --- a/telephony/java/android/telephony/TelephonyManager.java +++ b/telephony/java/android/telephony/TelephonyManager.java @@ -15104,8 +15104,8 @@ public class TelephonyManager { /** * Indicates that the thermal mitigation request could not power off the radio due to the device - * either being in an active voice call, device pending an emergency call, or any other state - * that would dissallow powering off of radio. + * either being in an active emergency voice call, device pending an emergency call, or any + * other state that would disallow powering off of radio. * * @hide */ diff --git a/tests/PlatformCompatGating/test-rules/src/android/compat/testing/PlatformCompatChangeRule.java b/tests/PlatformCompatGating/test-rules/src/android/compat/testing/PlatformCompatChangeRule.java index 039ba512e95b..0c5205c848da 100644 --- a/tests/PlatformCompatGating/test-rules/src/android/compat/testing/PlatformCompatChangeRule.java +++ b/tests/PlatformCompatGating/test-rules/src/android/compat/testing/PlatformCompatChangeRule.java @@ -118,7 +118,8 @@ public class PlatformCompatChangeRule extends CoreCompatChangeRule { Manifest.permission.LOG_COMPAT_CHANGE, Manifest.permission.OVERRIDE_COMPAT_CHANGE_CONFIG, Manifest.permission.OVERRIDE_COMPAT_CHANGE_CONFIG_ON_RELEASE_BUILD, - Manifest.permission.READ_COMPAT_CHANGE_CONFIG); + Manifest.permission.READ_COMPAT_CHANGE_CONFIG, + Manifest.permission.INTERACT_ACROSS_USERS_FULL); } } } |