diff options
160 files changed, 4293 insertions, 1646 deletions
diff --git a/apex/statsd/aidl/android/os/IStatsCompanionService.aidl b/apex/statsd/aidl/android/os/IStatsCompanionService.aidl index 21b7767e932d..99b9d398e30c 100644 --- a/apex/statsd/aidl/android/os/IStatsCompanionService.aidl +++ b/apex/statsd/aidl/android/os/IStatsCompanionService.aidl @@ -67,11 +67,4 @@ interface IStatsCompanionService { /** Tells StatsCompaionService to grab the uid map snapshot and send it to statsd. */ oneway void triggerUidSnapshot(); - - /** Tells StatsCompanionService to tell statsd to register a puller for the given atom id */ - oneway void registerPullAtomCallback(int atomTag, long coolDownNs, long timeoutNs, - in int[] additiveFields, IPullAtomCallback pullerCallback); - - /** Tells StatsCompanionService to tell statsd to unregister a puller for the given atom id */ - oneway void unregisterPullAtomCallback(int atomTag); } diff --git a/apex/statsd/aidl/android/os/IStatsManagerService.aidl b/apex/statsd/aidl/android/os/IStatsManagerService.aidl index dec56345ec2f..4a259f50d2f6 100644 --- a/apex/statsd/aidl/android/os/IStatsManagerService.aidl +++ b/apex/statsd/aidl/android/os/IStatsManagerService.aidl @@ -17,6 +17,7 @@ package android.os; import android.app.PendingIntent; +import android.os.IPullAtomCallback; /** * Binder interface to communicate with the Java-based statistics service helper. @@ -125,4 +126,11 @@ interface IStatsManagerService { * Requires Manifest.permission.DUMP and Manifest.permission.PACKAGE_USAGE_STATS. */ void removeConfiguration(in long configId, in String packageName); -}
\ No newline at end of file + + /** Tell StatsManagerService to register a puller for the given atom tag with statsd. */ + oneway void registerPullAtomCallback(int atomTag, long coolDownNs, long timeoutNs, + in int[] additiveFields, IPullAtomCallback pullerCallback); + + /** Tell StatsManagerService to unregister the pulller for the given atom tag from statsd. */ + oneway void unregisterPullAtomCallback(int atomTag); +} diff --git a/apex/statsd/service/java/com/android/server/stats/StatsCompanionService.java b/apex/statsd/service/java/com/android/server/stats/StatsCompanionService.java index 3fa4a8211ffc..0f981e25b37a 100644 --- a/apex/statsd/service/java/com/android/server/stats/StatsCompanionService.java +++ b/apex/statsd/service/java/com/android/server/stats/StatsCompanionService.java @@ -75,7 +75,6 @@ import android.os.FileUtils; import android.os.Handler; import android.os.HandlerThread; import android.os.IBinder; -import android.os.IPullAtomCallback; import android.os.IStatsCompanionService; import android.os.IStatsd; import android.os.IStoraged; @@ -263,71 +262,6 @@ public class StatsCompanionService extends IStatsCompanionService.Stub { private StatsManagerService mStatsManagerService; - private static final class PullerKey { - private final int mUid; - private final int mAtomTag; - - PullerKey(int uid, int atom) { - mUid = uid; - mAtomTag = atom; - } - - public int getUid() { - return mUid; - } - - public int getAtom() { - return mAtomTag; - } - - @Override - public int hashCode() { - return Objects.hash(mUid, mAtomTag); - } - - @Override - public boolean equals(Object obj) { - if (obj instanceof PullerKey) { - PullerKey other = (PullerKey) obj; - return this.mUid == other.getUid() && this.mAtomTag == other.getAtom(); - } - return false; - } - } - - private static final class PullerValue { - private final long mCoolDownNs; - private final long mTimeoutNs; - private int[] mAdditiveFields; - private IPullAtomCallback mCallback; - - PullerValue(long coolDownNs, long timeoutNs, int[] additiveFields, - IPullAtomCallback callback) { - mCoolDownNs = coolDownNs; - mTimeoutNs = timeoutNs; - mAdditiveFields = additiveFields; - mCallback = callback; - } - - public long getCoolDownNs() { - return mCoolDownNs; - } - - public long getTimeoutNs() { - return mTimeoutNs; - } - - public int[] getAdditiveFields() { - return mAdditiveFields; - } - - public IPullAtomCallback getCallback() { - return mCallback; - } - } - - private final HashMap<PullerKey, PullerValue> mPullers = new HashMap<>(); - private final KernelWakelockReader mKernelWakelockReader = new KernelWakelockReader(); private final KernelWakelockStats mTmpWakelockStats = new KernelWakelockStats(); private WifiManager mWifiManager = null; @@ -2657,57 +2591,6 @@ public class StatsCompanionService extends IStatsCompanionService.Stub { } } - @Override - public void registerPullAtomCallback(int atomTag, long coolDownNs, long timeoutNs, - int[] additiveFields, IPullAtomCallback pullerCallback) { - synchronized (sStatsdLock) { - // Always cache the puller in SCS. - // If statsd is down, we will register it when it comes back up. - int callingUid = Binder.getCallingUid(); - final long token = Binder.clearCallingIdentity(); - PullerKey key = new PullerKey(callingUid, atomTag); - PullerValue val = new PullerValue( - coolDownNs, timeoutNs, additiveFields, pullerCallback); - mPullers.put(key, val); - - if (sStatsd == null) { - Slog.w(TAG, "Could not access statsd for registering puller for atom " + atomTag); - return; - } - try { - sStatsd.registerPullAtomCallback( - callingUid, atomTag, coolDownNs, timeoutNs, additiveFields, pullerCallback); - } catch (RemoteException e) { - Slog.e(TAG, "Failed to access statsd to register puller for atom " + atomTag); - } finally { - Binder.restoreCallingIdentity(token); - } - } - } - - @Override - public void unregisterPullAtomCallback(int atomTag) { - synchronized (sStatsdLock) { - // Always remove the puller in SCS. - // If statsd is down, we will not register it when it comes back up. - int callingUid = Binder.getCallingUid(); - final long token = Binder.clearCallingIdentity(); - PullerKey key = new PullerKey(callingUid, atomTag); - mPullers.remove(key); - - if (sStatsd == null) { - Slog.w(TAG, "Could not access statsd for registering puller for atom " + atomTag); - return; - } - try { - sStatsd.unregisterPullAtomCallback(callingUid, atomTag); - } catch (RemoteException e) { - Slog.e(TAG, "Failed to access statsd to register puller for atom " + atomTag); - } finally { - Binder.restoreCallingIdentity(token); - } - } - } // Statsd related code @@ -2786,8 +2669,6 @@ public class StatsCompanionService extends IStatsCompanionService.Stub { // Pull the latest state of UID->app name, version mapping when // statsd starts. informAllUidsLocked(mContext); - // Register all pullers. If SCS has just started, this should be empty. - registerAllPullersLocked(); } finally { restoreCallingIdentity(token); } @@ -2799,17 +2680,6 @@ public class StatsCompanionService extends IStatsCompanionService.Stub { } } - @GuardedBy("sStatsdLock") - private void registerAllPullersLocked() throws RemoteException { - // TODO: pass in one call, using a file descriptor (similar to uidmap). - for (Map.Entry<PullerKey, PullerValue> entry : mPullers.entrySet()) { - PullerKey key = entry.getKey(); - PullerValue val = entry.getValue(); - sStatsd.registerPullAtomCallback(key.getUid(), key.getAtom(), val.getCoolDownNs(), - val.getTimeoutNs(), val.getAdditiveFields(), val.getCallback()); - } - } - private class StatsdDeathRecipient implements IBinder.DeathRecipient { @Override public void binderDied() { diff --git a/apex/statsd/service/java/com/android/server/stats/StatsManagerService.java b/apex/statsd/service/java/com/android/server/stats/StatsManagerService.java index b27d0f7699fc..04d8b006f51d 100644 --- a/apex/statsd/service/java/com/android/server/stats/StatsManagerService.java +++ b/apex/statsd/service/java/com/android/server/stats/StatsManagerService.java @@ -24,6 +24,7 @@ import android.app.AppOpsManager; import android.app.PendingIntent; import android.content.Context; import android.os.Binder; +import android.os.IPullAtomCallback; import android.os.IStatsManagerService; import android.os.IStatsd; import android.os.Process; @@ -60,8 +61,7 @@ public class StatsManagerService extends IStatsManagerService.Stub { @GuardedBy("mLock") private ArrayMap<ConfigKey, PendingIntentRef> mDataFetchPirMap = new ArrayMap<>(); @GuardedBy("mLock") - private ArrayMap<Integer, PendingIntentRef> mActiveConfigsPirMap = - new ArrayMap<>(); + private ArrayMap<Integer, PendingIntentRef> mActiveConfigsPirMap = new ArrayMap<>(); @GuardedBy("mLock") private ArrayMap<ConfigKey, ArrayMap<Long, PendingIntentRef>> mBroadcastSubscriberPirMap = new ArrayMap<>(); @@ -72,8 +72,8 @@ public class StatsManagerService extends IStatsManagerService.Stub { } private static class ConfigKey { - private int mUid; - private long mConfigId; + private final int mUid; + private final long mConfigId; ConfigKey(int uid, long configId) { mUid = uid; @@ -103,6 +103,126 @@ public class StatsManagerService extends IStatsManagerService.Stub { } } + private static class PullerKey { + private final int mUid; + private final int mAtomTag; + + PullerKey(int uid, int atom) { + mUid = uid; + mAtomTag = atom; + } + + public int getUid() { + return mUid; + } + + public int getAtom() { + return mAtomTag; + } + + @Override + public int hashCode() { + return Objects.hash(mUid, mAtomTag); + } + + @Override + public boolean equals(Object obj) { + if (obj instanceof PullerKey) { + PullerKey other = (PullerKey) obj; + return this.mUid == other.getUid() && this.mAtomTag == other.getAtom(); + } + return false; + } + } + + private static class PullerValue { + private final long mCoolDownNs; + private final long mTimeoutNs; + private final int[] mAdditiveFields; + private final IPullAtomCallback mCallback; + + PullerValue(long coolDownNs, long timeoutNs, int[] additiveFields, + IPullAtomCallback callback) { + mCoolDownNs = coolDownNs; + mTimeoutNs = timeoutNs; + mAdditiveFields = additiveFields; + mCallback = callback; + } + + public long getCoolDownNs() { + return mCoolDownNs; + } + + public long getTimeoutNs() { + return mTimeoutNs; + } + + public int[] getAdditiveFields() { + return mAdditiveFields; + } + + public IPullAtomCallback getCallback() { + return mCallback; + } + } + + private final ArrayMap<PullerKey, PullerValue> mPullers = new ArrayMap<>(); + + @Override + public void registerPullAtomCallback(int atomTag, long coolDownNs, long timeoutNs, + int[] additiveFields, IPullAtomCallback pullerCallback) { + int callingUid = Binder.getCallingUid(); + final long token = Binder.clearCallingIdentity(); + PullerKey key = new PullerKey(callingUid, atomTag); + PullerValue val = new PullerValue(coolDownNs, timeoutNs, additiveFields, pullerCallback); + + // Always cache the puller in StatsManagerService. If statsd is down, we will register the + // puller when statsd comes back up. + synchronized (mLock) { + mPullers.put(key, val); + } + + IStatsd statsd = getStatsdNonblocking(); + if (statsd == null) { + return; + } + + try { + statsd.registerPullAtomCallback( + callingUid, atomTag, coolDownNs, timeoutNs, additiveFields, pullerCallback); + } catch (RemoteException e) { + Slog.e(TAG, "Failed to access statsd to register puller for atom " + atomTag); + } finally { + Binder.restoreCallingIdentity(token); + } + } + + @Override + public void unregisterPullAtomCallback(int atomTag) { + int callingUid = Binder.getCallingUid(); + final long token = Binder.clearCallingIdentity(); + PullerKey key = new PullerKey(callingUid, atomTag); + + // Always remove the puller from StatsManagerService even if statsd is down. When statsd + // comes back up, we will not re-register the removed puller. + synchronized (mLock) { + mPullers.remove(key); + } + + IStatsd statsd = getStatsdNonblocking(); + if (statsd == null) { + return; + } + + try { + statsd.unregisterPullAtomCallback(callingUid, atomTag); + } catch (RemoteException e) { + Slog.e(TAG, "Failed to access statsd to unregister puller for atom " + atomTag); + } finally { + Binder.restoreCallingIdentity(token); + } + } + @Override public void setDataFetchOperation(long configId, PendingIntent pendingIntent, String packageName) { @@ -441,46 +561,85 @@ public class StatsManagerService extends IStatsManagerService.Stub { if (statsd == null) { return; } - // Since we do not want to make an IPC with the a lock held, we first create local deep - // copies of the data with the lock held before iterating through the maps. + + final long token = Binder.clearCallingIdentity(); + try { + registerAllPullers(statsd); + registerAllDataFetchOperations(statsd); + registerAllActiveConfigsChangedOperations(statsd); + registerAllBroadcastSubscribers(statsd); + } catch (RemoteException e) { + Slog.e(TAG, "StatsManager failed to (re-)register data with statsd"); + } finally { + Binder.restoreCallingIdentity(token); + } + } + + // Pre-condition: the Binder calling identity has already been cleared + private void registerAllPullers(IStatsd statsd) throws RemoteException { + // Since we do not want to make an IPC with the lock held, we first create a copy of the + // data with the lock held before iterating through the map. + ArrayMap<PullerKey, PullerValue> pullersCopy; + synchronized (mLock) { + pullersCopy = new ArrayMap<>(mPullers); + } + + for (Map.Entry<PullerKey, PullerValue> entry : pullersCopy.entrySet()) { + PullerKey key = entry.getKey(); + PullerValue value = entry.getValue(); + statsd.registerPullAtomCallback(key.getUid(), key.getAtom(), value.getCoolDownNs(), + value.getTimeoutNs(), value.getAdditiveFields(), value.getCallback()); + } + } + + // Pre-condition: the Binder calling identity has already been cleared + private void registerAllDataFetchOperations(IStatsd statsd) throws RemoteException { + // Since we do not want to make an IPC with the lock held, we first create a copy of the + // data with the lock held before iterating through the map. ArrayMap<ConfigKey, PendingIntentRef> dataFetchCopy; - ArrayMap<Integer, PendingIntentRef> activeConfigsChangedCopy; - ArrayMap<ConfigKey, ArrayMap<Long, PendingIntentRef>> broadcastSubscriberCopy; synchronized (mLock) { dataFetchCopy = new ArrayMap<>(mDataFetchPirMap); - activeConfigsChangedCopy = new ArrayMap<>(mActiveConfigsPirMap); - broadcastSubscriberCopy = new ArrayMap<>(); - for (Map.Entry<ConfigKey, ArrayMap<Long, PendingIntentRef>> entry - : mBroadcastSubscriberPirMap.entrySet()) { - broadcastSubscriberCopy.put(entry.getKey(), new ArrayMap<>(entry.getValue())); - } } + for (Map.Entry<ConfigKey, PendingIntentRef> entry : dataFetchCopy.entrySet()) { ConfigKey key = entry.getKey(); - try { - statsd.setDataFetchOperation(key.getConfigId(), entry.getValue(), key.getUid()); - } catch (RemoteException e) { - Slog.e(TAG, "Failed to setDataFetchOperation from pirMap"); - } + statsd.setDataFetchOperation(key.getConfigId(), entry.getValue(), key.getUid()); + } + } + + // Pre-condition: the Binder calling identity has already been cleared + private void registerAllActiveConfigsChangedOperations(IStatsd statsd) throws RemoteException { + // Since we do not want to make an IPC with the lock held, we first create a copy of the + // data with the lock held before iterating through the map. + ArrayMap<Integer, PendingIntentRef> activeConfigsChangedCopy; + synchronized (mLock) { + activeConfigsChangedCopy = new ArrayMap<>(mActiveConfigsPirMap); + } + + for (Map.Entry<Integer, PendingIntentRef> entry : activeConfigsChangedCopy.entrySet()) { + statsd.setActiveConfigsChangedOperation(entry.getValue(), entry.getKey()); } - for (Map.Entry<Integer, PendingIntentRef> entry - : activeConfigsChangedCopy.entrySet()) { - try { - statsd.setActiveConfigsChangedOperation(entry.getValue(), entry.getKey()); - } catch (RemoteException e) { - Slog.e(TAG, "Failed to setActiveConfigsChangedOperation from pirMap"); + } + + // Pre-condition: the Binder calling identity has already been cleared + private void registerAllBroadcastSubscribers(IStatsd statsd) throws RemoteException { + // Since we do not want to make an IPC with the lock held, we first create a deep copy of + // the data with the lock held before iterating through the map. + ArrayMap<ConfigKey, ArrayMap<Long, PendingIntentRef>> broadcastSubscriberCopy = + new ArrayMap<>(); + synchronized (mLock) { + for (Map.Entry<ConfigKey, ArrayMap<Long, PendingIntentRef>> entry : + mBroadcastSubscriberPirMap.entrySet()) { + broadcastSubscriberCopy.put(entry.getKey(), new ArrayMap(entry.getValue())); } } - for (Map.Entry<ConfigKey, ArrayMap<Long, PendingIntentRef>> entry - : broadcastSubscriberCopy.entrySet()) { + + for (Map.Entry<ConfigKey, ArrayMap<Long, PendingIntentRef>> entry : + mBroadcastSubscriberPirMap.entrySet()) { + ConfigKey configKey = entry.getKey(); for (Map.Entry<Long, PendingIntentRef> subscriberEntry : entry.getValue().entrySet()) { - ConfigKey configKey = entry.getKey(); - try { - statsd.setBroadcastSubscriber(configKey.getConfigId(), subscriberEntry.getKey(), - subscriberEntry.getValue(), configKey.getUid()); - } catch (RemoteException e) { - Slog.e(TAG, "Failed to setBroadcastSubscriber from pirMap"); - } + statsd.setBroadcastSubscriber(configKey.getConfigId(), subscriberEntry.getKey(), + subscriberEntry.getValue(), configKey.getUid()); } } } diff --git a/api/current.txt b/api/current.txt index 6fff2b9ce74a..df7301ff09b8 100644 --- a/api/current.txt +++ b/api/current.txt @@ -1144,6 +1144,7 @@ package android { field public static final int resizeable = 16843405; // 0x101028d field public static final int resizeableActivity = 16844022; // 0x10104f6 field public static final int resource = 16842789; // 0x1010025 + field public static final int resourcesMap = 16844297; // 0x1010609 field public static final int restoreAnyVersion = 16843450; // 0x10102ba field @Deprecated public static final int restoreNeedsApplication = 16843421; // 0x101029d field public static final int restrictedAccountType = 16843733; // 0x10103d5 @@ -9828,6 +9829,7 @@ package android.content { method public boolean bindIsolatedService(@NonNull @RequiresPermission android.content.Intent, int, @NonNull String, @NonNull java.util.concurrent.Executor, @NonNull android.content.ServiceConnection); method public abstract boolean bindService(@RequiresPermission android.content.Intent, @NonNull android.content.ServiceConnection, int); method public boolean bindService(@NonNull @RequiresPermission android.content.Intent, int, @NonNull java.util.concurrent.Executor, @NonNull android.content.ServiceConnection); + method @RequiresPermission(anyOf={"android.permission.INTERACT_ACROSS_USERS", android.Manifest.permission.INTERACT_ACROSS_PROFILES}) public boolean bindServiceAsUser(@NonNull @RequiresPermission android.content.Intent, @NonNull android.content.ServiceConnection, int, @NonNull android.os.UserHandle); method @CheckResult(suggest="#enforceCallingOrSelfPermission(String,String)") public abstract int checkCallingOrSelfPermission(@NonNull String); method @CheckResult(suggest="#enforceCallingOrSelfUriPermission(Uri,int,String)") public abstract int checkCallingOrSelfUriPermission(android.net.Uri, int); method @CheckResult(suggest="#enforceCallingPermission(String,String)") public abstract int checkCallingPermission(@NonNull String); @@ -10005,7 +10007,6 @@ package android.content { field public static final String MEDIA_ROUTER_SERVICE = "media_router"; field public static final String MEDIA_SESSION_SERVICE = "media_session"; field public static final String MIDI_SERVICE = "midi"; - field public static final String MMS_SERVICE = "mms"; field public static final int MODE_APPEND = 32768; // 0x8000 field public static final int MODE_ENABLE_WRITE_AHEAD_LOGGING = 8; // 0x8 field @Deprecated public static final int MODE_MULTI_PROCESS = 4; // 0x4 @@ -11363,6 +11364,7 @@ package android.content.pm { public class CrossProfileApps { method public boolean canInteractAcrossProfiles(); method public boolean canRequestInteractAcrossProfiles(); + method @Nullable public android.content.Intent createRequestInteractAcrossProfilesIntent(); method @NonNull public android.graphics.drawable.Drawable getProfileSwitchingIconDrawable(@NonNull android.os.UserHandle); method @NonNull public CharSequence getProfileSwitchingLabel(@NonNull android.os.UserHandle); method @NonNull public java.util.List<android.os.UserHandle> getTargetUserProfiles(); @@ -30662,11 +30664,11 @@ package android.net.wifi { ctor public WifiNetworkSuggestion.Builder(); method @NonNull public android.net.wifi.WifiNetworkSuggestion build(); method @NonNull public android.net.wifi.WifiNetworkSuggestion.Builder setBssid(@NonNull android.net.MacAddress); + method @NonNull public android.net.wifi.WifiNetworkSuggestion.Builder setCredentialSharedWithUser(boolean); method @NonNull public android.net.wifi.WifiNetworkSuggestion.Builder setIsAppInteractionRequired(boolean); method @NonNull public android.net.wifi.WifiNetworkSuggestion.Builder setIsEnhancedOpen(boolean); method @NonNull public android.net.wifi.WifiNetworkSuggestion.Builder setIsHiddenSsid(boolean); method @NonNull public android.net.wifi.WifiNetworkSuggestion.Builder setIsMetered(boolean); - method @NonNull public android.net.wifi.WifiNetworkSuggestion.Builder setIsUserAllowedToManuallyConnect(boolean); method @NonNull public android.net.wifi.WifiNetworkSuggestion.Builder setIsUserInteractionRequired(boolean); method @NonNull public android.net.wifi.WifiNetworkSuggestion.Builder setPasspointConfig(@NonNull android.net.wifi.hotspot2.PasspointConfiguration); method @NonNull public android.net.wifi.WifiNetworkSuggestion.Builder setPriority(@IntRange(from=0) int); @@ -36071,6 +36073,7 @@ package android.os { method @RequiresPermission(anyOf={"android.permission.MANAGE_USERS", "android.permission.INTERACT_ACROSS_USERS"}, conditional=true) public android.os.Bundle getUserRestrictions(android.os.UserHandle); method public boolean hasUserRestriction(String); method public boolean isDemoUser(); + method public boolean isManagedProfile(); method public boolean isQuietModeEnabled(android.os.UserHandle); method public boolean isSystemUser(); method public boolean isUserAGoat(); @@ -45538,11 +45541,6 @@ package android.telephony { method @Nullable public android.telephony.mbms.StreamingService startStreaming(android.telephony.mbms.StreamingServiceInfo, @NonNull java.util.concurrent.Executor, android.telephony.mbms.StreamingServiceCallback); } - public final class MmsManager { - method public void downloadMultimediaMessage(int, @NonNull String, @NonNull android.net.Uri, @Nullable android.os.Bundle, @Nullable android.app.PendingIntent); - method public void sendMultimediaMessage(int, @NonNull android.net.Uri, @Nullable String, @Nullable android.os.Bundle, @Nullable android.app.PendingIntent); - } - @Deprecated public class NeighboringCellInfo implements android.os.Parcelable { ctor @Deprecated public NeighboringCellInfo(); ctor @Deprecated public NeighboringCellInfo(int, int); @@ -45780,8 +45778,8 @@ package android.telephony { method public String createAppSpecificSmsToken(android.app.PendingIntent); method @Nullable public String createAppSpecificSmsTokenWithPackageInfo(@Nullable String, @NonNull android.app.PendingIntent); method public java.util.ArrayList<java.lang.String> divideMessage(String); - method @Deprecated public void downloadMultimediaMessage(android.content.Context, String, android.net.Uri, android.os.Bundle, android.app.PendingIntent); - method @NonNull public android.os.Bundle getCarrierConfigValues(); + method public void downloadMultimediaMessage(android.content.Context, String, android.net.Uri, android.os.Bundle, android.app.PendingIntent); + method @Nullable public android.os.Bundle getCarrierConfigValues(); method public static android.telephony.SmsManager getDefault(); method public static int getDefaultSmsSubscriptionId(); method public static android.telephony.SmsManager getSmsManagerForSubscriptionId(int); @@ -45790,7 +45788,7 @@ package android.telephony { method public int getSubscriptionId(); method public void injectSmsPdu(byte[], String, android.app.PendingIntent); method public void sendDataMessage(String, String, short, byte[], android.app.PendingIntent, android.app.PendingIntent); - method @Deprecated public void sendMultimediaMessage(android.content.Context, android.net.Uri, String, android.os.Bundle, android.app.PendingIntent); + method public void sendMultimediaMessage(android.content.Context, android.net.Uri, String, android.os.Bundle, android.app.PendingIntent); method public void sendMultipartTextMessage(String, String, java.util.ArrayList<java.lang.String>, java.util.ArrayList<android.app.PendingIntent>, java.util.ArrayList<android.app.PendingIntent>); method public void sendTextMessage(String, String, String, android.app.PendingIntent, android.app.PendingIntent); method @RequiresPermission(allOf={android.Manifest.permission.MODIFY_PHONE_STATE, android.Manifest.permission.SEND_SMS}) public void sendTextMessageWithoutPersisting(String, String, String, android.app.PendingIntent, android.app.PendingIntent); diff --git a/api/system-current.txt b/api/system-current.txt index b7ea0e617cef..e0f6422d0c1b 100755 --- a/api/system-current.txt +++ b/api/system-current.txt @@ -241,7 +241,6 @@ package android { field public static final int isVrOnly = 16844152; // 0x1010578 field public static final int requiredSystemPropertyName = 16844133; // 0x1010565 field public static final int requiredSystemPropertyValue = 16844134; // 0x1010566 - field public static final int resourcesMap = 16844297; // 0x1010609 field public static final int supportsAmbientMode = 16844173; // 0x101058d field public static final int userRestriction = 16844164; // 0x1010584 } @@ -1536,6 +1535,10 @@ package android.bluetooth { method @RequiresPermission(android.Manifest.permission.BLUETOOTH_ADMIN) public boolean setConnectionPolicy(@NonNull android.bluetooth.BluetoothDevice, int); } + public final class BluetoothHidDevice implements android.bluetooth.BluetoothProfile { + method @RequiresPermission(android.Manifest.permission.BLUETOOTH_ADMIN) public boolean setConnectionPolicy(@NonNull android.bluetooth.BluetoothDevice, int); + } + public final class BluetoothHidHost implements android.bluetooth.BluetoothProfile { method @NonNull public java.util.List<android.bluetooth.BluetoothDevice> getConnectedDevices(); method @RequiresPermission(android.Manifest.permission.BLUETOOTH) public int getConnectionPolicy(@Nullable android.bluetooth.BluetoothDevice); @@ -1544,12 +1547,20 @@ package android.bluetooth { field public static final String ACTION_CONNECTION_STATE_CHANGED = "android.bluetooth.input.profile.action.CONNECTION_STATE_CHANGED"; } + public final class BluetoothMap implements android.bluetooth.BluetoothProfile { + method @NonNull public java.util.List<android.bluetooth.BluetoothDevice> getConnectedDevices(); + method @RequiresPermission(android.Manifest.permission.BLUETOOTH) public int getConnectionPolicy(@Nullable android.bluetooth.BluetoothDevice); + method @RequiresPermission(android.Manifest.permission.BLUETOOTH_ADMIN) public boolean setConnectionPolicy(@Nullable android.bluetooth.BluetoothDevice, int); + field public static final String ACTION_CONNECTION_STATE_CHANGED = "android.bluetooth.map.profile.action.CONNECTION_STATE_CHANGED"; + } + public final class BluetoothPan implements android.bluetooth.BluetoothProfile { method protected void finalize(); method @NonNull public java.util.List<android.bluetooth.BluetoothDevice> getConnectedDevices(); method public int getConnectionState(@Nullable android.bluetooth.BluetoothDevice); method public boolean isTetheringOn(); method public void setBluetoothTethering(boolean); + method @RequiresPermission(android.Manifest.permission.BLUETOOTH_ADMIN) public boolean setConnectionPolicy(@NonNull android.bluetooth.BluetoothDevice, int); field public static final String ACTION_CONNECTION_STATE_CHANGED = "android.bluetooth.pan.profile.action.CONNECTION_STATE_CHANGED"; field public static final String EXTRA_LOCAL_ROLE = "android.bluetooth.pan.extra.LOCAL_ROLE"; field public static final int LOCAL_NAP_ROLE = 1; // 0x1 @@ -1672,7 +1683,6 @@ package android.content { } public abstract class Context { - method @RequiresPermission(android.Manifest.permission.INTERACT_ACROSS_USERS) public boolean bindServiceAsUser(@RequiresPermission android.content.Intent, android.content.ServiceConnection, int, android.os.UserHandle); method @NonNull public android.content.Context createContextAsUser(@NonNull android.os.UserHandle, int); method public abstract android.content.Context createCredentialProtectedStorageContext(); method @NonNull public android.content.Context createPackageContextAsUser(@NonNull String, int, @NonNull android.os.UserHandle) throws android.content.pm.PackageManager.NameNotFoundException; @@ -1781,6 +1791,7 @@ package android.content { field @Deprecated public static final String EXTRA_SIM_STATE = "ss"; field public static final String EXTRA_UNKNOWN_INSTANT_APP = "android.intent.extra.UNKNOWN_INSTANT_APP"; field public static final String EXTRA_VERIFICATION_BUNDLE = "android.intent.extra.VERIFICATION_BUNDLE"; + field public static final int FLAG_RECEIVER_REGISTERED_ONLY_BEFORE_BOOT = 67108864; // 0x4000000 field public static final String METADATA_SETUP_VERSION = "android.SETUP_VERSION"; field @Deprecated public static final String SIM_ABSENT_ON_PERM_DISABLED = "PERM_DISABLED"; field @Deprecated public static final String SIM_LOCKED_NETWORK = "NETWORK"; @@ -7273,7 +7284,6 @@ package android.os { method @RequiresPermission(anyOf={android.Manifest.permission.MANAGE_USERS, android.Manifest.permission.INTERACT_ACROSS_USERS}, conditional=true) public boolean hasUserRestrictionForUser(@NonNull String, @NonNull android.os.UserHandle); method @RequiresPermission(android.Manifest.permission.MANAGE_USERS) public boolean isAdminUser(); method @RequiresPermission(android.Manifest.permission.MANAGE_USERS) public boolean isGuestUser(); - method @RequiresPermission(android.Manifest.permission.MANAGE_USERS) public boolean isManagedProfile(); method @RequiresPermission(android.Manifest.permission.MANAGE_USERS) public boolean isManagedProfile(int); method @RequiresPermission(android.Manifest.permission.MANAGE_USERS) public boolean isPrimaryUser(); method @RequiresPermission(android.Manifest.permission.MANAGE_USERS) public boolean isRestrictedProfile(); @@ -8662,7 +8672,10 @@ package android.service.notification { method @Nullable public android.service.notification.Adjustment onNotificationEnqueued(@NonNull android.service.notification.StatusBarNotification, @NonNull android.app.NotificationChannel); method public void onNotificationExpansionChanged(@NonNull String, boolean, boolean); method public abstract void onNotificationSnoozedUntilContext(@NonNull android.service.notification.StatusBarNotification, @NonNull String); + method public void onNotificationVisibilityChanged(@NonNull String, boolean); method public void onNotificationsSeen(@NonNull java.util.List<java.lang.String>); + method public void onPanelHidden(); + method public void onPanelRevealed(int); method public void onSuggestedReplySent(@NonNull String, @NonNull CharSequence, int); method public final void unsnoozeNotification(@NonNull String); field public static final String SERVICE_INTERFACE = "android.service.notification.NotificationAssistantService"; @@ -10411,6 +10424,7 @@ package android.telephony { method @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) public boolean isApnMetered(int); method @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) public boolean isApplicationOnUicc(int); method public boolean isCurrentSimOperator(@NonNull String, int, @Nullable String); + method @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) public boolean isDataConnectionEnabled(); method public boolean isDataConnectivityPossible(); method @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) public boolean isDataEnabledForApn(int); method @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) public boolean isEmergencyAssistanceEnabled(); diff --git a/api/test-current.txt b/api/test-current.txt index d017dd63bdd3..00a2c29cd06f 100644 --- a/api/test-current.txt +++ b/api/test-current.txt @@ -435,6 +435,8 @@ package android.app { } public class StatusBarManager { + method public void collapsePanels(); + method public void expandNotificationsPanel(); method @NonNull @RequiresPermission(android.Manifest.permission.STATUS_BAR) public android.app.StatusBarManager.DisableInfo getDisableInfo(); method @RequiresPermission(android.Manifest.permission.STATUS_BAR) public void setDisabledForSetup(boolean); method @RequiresPermission(android.Manifest.permission.STATUS_BAR) public void setDisabledForSimNetworkLock(boolean); @@ -2929,7 +2931,10 @@ package android.service.notification { method @Nullable public android.service.notification.Adjustment onNotificationEnqueued(@NonNull android.service.notification.StatusBarNotification, @NonNull android.app.NotificationChannel); method public void onNotificationExpansionChanged(@NonNull String, boolean, boolean); method public abstract void onNotificationSnoozedUntilContext(@NonNull android.service.notification.StatusBarNotification, @NonNull String); + method public void onNotificationVisibilityChanged(@NonNull String, boolean); method public void onNotificationsSeen(@NonNull java.util.List<java.lang.String>); + method public void onPanelHidden(); + method public void onPanelRevealed(int); method public void onSuggestedReplySent(@NonNull String, @NonNull CharSequence, int); method public final void unsnoozeNotification(@NonNull String); field public static final String SERVICE_INTERFACE = "android.service.notification.NotificationAssistantService"; diff --git a/cmds/statsd/src/atoms.proto b/cmds/statsd/src/atoms.proto index f6680f3453f5..2bacfbc57395 100644 --- a/cmds/statsd/src/atoms.proto +++ b/cmds/statsd/src/atoms.proto @@ -333,6 +333,7 @@ message Atom { MediaProviderSchemaChange media_provider_schema_change = 236 [(module) = "mediaprovider"]; MediaProviderIdleMaintenance media_provider_idle_maintenance = 237 [(module) = "mediaprovider"]; + RebootEscrowRecoveryReported reboot_escrow_recovery_reported = 238; } // Pulled events will start at field 10000. @@ -7339,6 +7340,17 @@ message UpdateEngineSuccessfulUpdateReported { } /** + * Reported when the RebootEscrow HAL has attempted to recover the escrowed + * key to indicate whether it was successful or not. + * + * Logged from: + * frameworks/base/services/core/java/com/android/server/locksettings/RebootEscrowManager.java + */ +message RebootEscrowRecoveryReported { + optional bool successful = 1; +} + +/** * Global display pipeline metrics reported by SurfaceFlinger. * Pulled from: * frameworks/native/services/surfaceflinger/TimeStats/TimeStats.cpp diff --git a/config/boot-image-profile.txt b/config/boot-image-profile.txt index 4d0acb3c2330..4aaf72728cbf 100644 --- a/config/boot-image-profile.txt +++ b/config/boot-image-profile.txt @@ -22519,8 +22519,6 @@ HSPLcom/android/internal/telephony/PhoneFactory;->makeDefaultPhone(Landroid/cont HSPLcom/android/internal/telephony/PhoneFactory;->makeDefaultPhones(Landroid/content/Context;)V HSPLcom/android/internal/telephony/PhoneInternalInterface$DataActivityState;-><init>(Ljava/lang/String;I)V HSPLcom/android/internal/telephony/PhoneInternalInterface$DataActivityState;->values()[Lcom/android/internal/telephony/PhoneInternalInterface$DataActivityState; -HSPLcom/android/internal/telephony/PhoneStateIntentReceiver;-><init>(Landroid/content/Context;Landroid/os/Handler;)V -HSPLcom/android/internal/telephony/PhoneStateIntentReceiver;->notifyServiceState(I)V HSPLcom/android/internal/telephony/PhoneSubInfoController;->callPhoneMethodWithPermissionCheck(ILjava/lang/String;Ljava/lang/String;Lcom/android/internal/telephony/PhoneSubInfoController$CallPhoneMethodHelper;Lcom/android/internal/telephony/PhoneSubInfoController$PermissionCheckHelper;)Ljava/lang/Object; HSPLcom/android/internal/telephony/PhoneSubInfoController;->getCarrierInfoForImsiEncryption(IILjava/lang/String;)Landroid/telephony/ImsiEncryptionInfo; HSPLcom/android/internal/telephony/PhoneSubInfoController;->getGroupIdLevel1ForSubscriber(ILjava/lang/String;)Ljava/lang/String; @@ -37729,7 +37727,6 @@ Lcom/android/internal/telephony/PhoneFactory; Lcom/android/internal/telephony/PhoneInternalInterface$DataActivityState; Lcom/android/internal/telephony/PhoneInternalInterface; Lcom/android/internal/telephony/PhoneNotifier; -Lcom/android/internal/telephony/PhoneStateIntentReceiver; Lcom/android/internal/telephony/PhoneSubInfoController$CallPhoneMethodHelper; Lcom/android/internal/telephony/PhoneSubInfoController$PermissionCheckHelper; Lcom/android/internal/telephony/PhoneSubInfoController; diff --git a/config/preloaded-classes b/config/preloaded-classes index 97f009c09217..e53c74bdfc4b 100644 --- a/config/preloaded-classes +++ b/config/preloaded-classes @@ -4841,7 +4841,6 @@ com.android.internal.telephony.PhoneConstants$State com.android.internal.telephony.PhoneFactory com.android.internal.telephony.PhoneInternalInterface com.android.internal.telephony.PhoneNotifier -com.android.internal.telephony.PhoneStateIntentReceiver com.android.internal.telephony.PhoneSubInfoController$CallPhoneMethodHelper com.android.internal.telephony.PhoneSubInfoController$PermissionCheckHelper com.android.internal.telephony.PhoneSubInfoController diff --git a/core/java/android/app/ActivityManagerInternal.java b/core/java/android/app/ActivityManagerInternal.java index 032e8245ca42..4f3e8ec9fb51 100644 --- a/core/java/android/app/ActivityManagerInternal.java +++ b/core/java/android/app/ActivityManagerInternal.java @@ -45,8 +45,19 @@ public abstract class ActivityManagerInternal { // Access modes for handleIncomingUser. public static final int ALLOW_NON_FULL = 0; + /** + * Allows access to a caller with {@link android.Manifest.permission#INTERACT_ACROSS_USERS} + * if in the same profile group. + * Otherwise, {@link android.Manifest.permission#INTERACT_ACROSS_USERS_FULL} is required. + */ public static final int ALLOW_NON_FULL_IN_PROFILE = 1; public static final int ALLOW_FULL_ONLY = 2; + /** + * Allows access to a caller with {@link android.Manifest.permission#INTERACT_ACROSS_PROFILES} + * or {@link android.Manifest.permission#INTERACT_ACROSS_USERS} if in the same profile group. + * Otherwise, {@link android.Manifest.permission#INTERACT_ACROSS_USERS_FULL} is required. + */ + public static final int ALLOW_ALL_PROFILE_PERMISSIONS_IN_PROFILE = 3; /** * Verify that calling app has access to the given provider. diff --git a/core/java/android/app/IActivityTaskManager.aidl b/core/java/android/app/IActivityTaskManager.aidl index 700b3c1b620e..e5c046c2376c 100644 --- a/core/java/android/app/IActivityTaskManager.aidl +++ b/core/java/android/app/IActivityTaskManager.aidl @@ -68,6 +68,7 @@ import android.os.StrictMode; import android.os.WorkSource; import android.service.voice.IVoiceInteractionSession; import android.view.IRecentsAnimationRunner; +import android.view.ITaskOrganizer; import android.view.RemoteAnimationDefinition; import android.view.RemoteAnimationAdapter; import android.view.WindowContainerTransaction; @@ -121,6 +122,9 @@ interface IActivityTaskManager { in Intent intent, in String resolvedType, in IBinder resultTo, in String resultWho, int requestCode, int flags, in ProfilerInfo profilerInfo, in Bundle options, IBinder permissionToken, boolean ignoreTargetSecurity, int userId); + + void registerTaskOrganizer(in ITaskOrganizer organizer, int windowingMode); + boolean isActivityStartAllowedOnDisplay(int displayId, in Intent intent, in String resolvedType, int userId); diff --git a/core/java/android/app/PropertyInvalidatedCache.java b/core/java/android/app/PropertyInvalidatedCache.java index 844e72ecf07c..736efb6a5e69 100644 --- a/core/java/android/app/PropertyInvalidatedCache.java +++ b/core/java/android/app/PropertyInvalidatedCache.java @@ -15,6 +15,7 @@ */ package android.app; + import android.annotation.NonNull; import android.os.SystemProperties; import android.util.Log; @@ -23,6 +24,7 @@ import com.android.internal.annotations.GuardedBy; import java.util.LinkedHashMap; import java.util.Map; +import java.util.Objects; import java.util.Random; import java.util.concurrent.atomic.AtomicLong; @@ -164,6 +166,7 @@ public abstract class PropertyInvalidatedCache<Query, Result> { private static final String TAG = "PropertyInvalidatedCache"; private static final boolean DEBUG = false; private static final boolean ENABLE = true; + private static final boolean VERIFY = false; private final Object mLock = new Object(); @@ -228,6 +231,18 @@ public abstract class PropertyInvalidatedCache<Query, Result> { protected abstract Result recompute(Query query); /** + * Determines if a pair of responses are considered equal. Used to determine whether + * a cache is inadvertently returning stale results when VERIFY is set to true. + */ + protected boolean debugCompareQueryResults(Result cachedResult, Result fetchedResult) { + // If a service crashes and returns a null result, the cached value remains valid. + if (fetchedResult != null) { + return Objects.equals(cachedResult, fetchedResult); + } + return true; + } + + /** * Make result up-to-date on a cache hit. Called unlocked; * may block. * @@ -334,12 +349,12 @@ public abstract class PropertyInvalidatedCache<Query, Result> { mCache.put(query, refreshedResult); } } - return refreshedResult; + return maybeCheckConsistency(query, refreshedResult); } if (DEBUG) { Log.d(TAG, "cache hit for " + query); } - return cachedResult; + return maybeCheckConsistency(query, cachedResult); } // Cache miss: make the value from scratch. if (DEBUG) { @@ -353,7 +368,7 @@ public abstract class PropertyInvalidatedCache<Query, Result> { mCache.put(query, result); } } - return result; + return maybeCheckConsistency(query, result); } } @@ -425,4 +440,15 @@ public abstract class PropertyInvalidatedCache<Query, Result> { } SystemProperties.set(name, newValueString); } + + private Result maybeCheckConsistency(Query query, Result proposedResult) { + if (VERIFY) { + Result resultToCompare = recompute(query); + boolean nonceChanged = (getCurrentNonce() != mLastSeenNonce); + if (!nonceChanged && !debugCompareQueryResults(proposedResult, resultToCompare)) { + throw new AssertionError("cache returned out of date response for " + query); + } + } + return proposedResult; + } } diff --git a/core/java/android/app/StatsManager.java b/core/java/android/app/StatsManager.java index 84263749232d..dde6dda8e448 100644 --- a/core/java/android/app/StatsManager.java +++ b/core/java/android/app/StatsManager.java @@ -26,7 +26,6 @@ import android.content.Context; import android.os.IBinder; import android.os.IPullAtomCallback; import android.os.IPullAtomResultReceiver; -import android.os.IStatsCompanionService; import android.os.IStatsManagerService; import android.os.IStatsPullerCallback; import android.os.IStatsd; @@ -61,9 +60,6 @@ public final class StatsManager { private IStatsd mService; @GuardedBy("sLock") - private IStatsCompanionService mStatsCompanion; - - @GuardedBy("sLock") private IStatsManagerService mStatsManagerService; /** @@ -538,7 +534,7 @@ public final class StatsManager { } synchronized (sLock) { try { - IStatsCompanionService service = getIStatsCompanionServiceLocked(); + IStatsManagerService service = getIStatsManagerServiceLocked(); PullAtomCallbackInternal rec = new PullAtomCallbackInternal(atomTag, callback, executor); service.registerPullAtomCallback(atomTag, coolDownNs, timeoutNs, additiveFields, @@ -560,7 +556,7 @@ public final class StatsManager { public void unregisterPullAtomCallback(int atomTag) { synchronized (sLock) { try { - IStatsCompanionService service = getIStatsCompanionServiceLocked(); + IStatsManagerService service = getIStatsManagerServiceLocked(); service.unregisterPullAtomCallback(atomTag); } catch (RemoteException e) { throw new RuntimeException("Unable to unregister pull atom callback"); @@ -746,16 +742,6 @@ public final class StatsManager { } @GuardedBy("sLock") - private IStatsCompanionService getIStatsCompanionServiceLocked() { - if (mStatsCompanion != null) { - return mStatsCompanion; - } - mStatsCompanion = IStatsCompanionService.Stub.asInterface( - ServiceManager.getService("statscompanion")); - return mStatsCompanion; - } - - @GuardedBy("sLock") private IStatsManagerService getIStatsManagerServiceLocked() { if (mStatsManagerService != null) { return mStatsManagerService; diff --git a/core/java/android/app/StatusBarManager.java b/core/java/android/app/StatusBarManager.java index 078e4538c66b..42563b5a1561 100644 --- a/core/java/android/app/StatusBarManager.java +++ b/core/java/android/app/StatusBarManager.java @@ -267,6 +267,7 @@ public class StatusBarManager { * @hide */ @UnsupportedAppUsage + @TestApi public void expandNotificationsPanel() { try { final IStatusBarService svc = getService(); @@ -284,6 +285,7 @@ public class StatusBarManager { * @hide */ @UnsupportedAppUsage + @TestApi public void collapsePanels() { try { final IStatusBarService svc = getService(); diff --git a/core/java/android/bluetooth/BluetoothHidDevice.java b/core/java/android/bluetooth/BluetoothHidDevice.java index e9b0be2c4cd6..a923be62fbce 100644 --- a/core/java/android/bluetooth/BluetoothHidDevice.java +++ b/core/java/android/bluetooth/BluetoothHidDevice.java @@ -16,8 +16,12 @@ package android.bluetooth; +import android.Manifest; +import android.annotation.NonNull; +import android.annotation.RequiresPermission; import android.annotation.SdkConstant; import android.annotation.SdkConstant.SdkConstantType; +import android.annotation.SystemApi; import android.content.Context; import android.os.Binder; import android.os.IBinder; @@ -36,6 +40,7 @@ import java.util.concurrent.Executor; */ public final class BluetoothHidDevice implements BluetoothProfile { private static final String TAG = BluetoothHidDevice.class.getSimpleName(); + private static final boolean DBG = false; /** * Intent used to broadcast the change in connection state of the Input Host profile. @@ -682,4 +687,62 @@ public final class BluetoothHidDevice implements BluetoothProfile { return result; } + + /** + * Connects Hid Device if connectionPolicy is {@link BluetoothProfile#CONNECTION_POLICY_ALLOWED} + * and disconnects Hid device if connectionPolicy is + * {@link BluetoothProfile#CONNECTION_POLICY_FORBIDDEN}. + * + * <p> The device should already be paired. + * Connection policy can be one of: + * {@link BluetoothProfile#CONNECTION_POLICY_ALLOWED}, + * {@link BluetoothProfile#CONNECTION_POLICY_FORBIDDEN}, + * {@link BluetoothProfile#CONNECTION_POLICY_UNKNOWN} + * + * @param device Paired bluetooth device + * @param connectionPolicy determines whether hid device should be connected or disconnected + * @return true if hid device is connected or disconnected, false otherwise + * + * @hide + */ + @SystemApi + @RequiresPermission(Manifest.permission.BLUETOOTH_ADMIN) + public boolean setConnectionPolicy(@NonNull BluetoothDevice device, + @ConnectionPolicy int connectionPolicy) { + log("setConnectionPolicy(" + device + ", " + connectionPolicy + ")"); + try { + final IBluetoothHidDevice service = getService(); + if (service != null && isEnabled() + && isValidDevice(device)) { + if (connectionPolicy != BluetoothProfile.CONNECTION_POLICY_FORBIDDEN + && connectionPolicy != BluetoothProfile.CONNECTION_POLICY_ALLOWED) { + return false; + } + return service.setConnectionPolicy(device, connectionPolicy); + } + if (service == null) Log.w(TAG, "Proxy not attached to service"); + return false; + } catch (RemoteException e) { + Log.e(TAG, "Stack:" + Log.getStackTraceString(new Throwable())); + return false; + } + } + + private boolean isEnabled() { + if (mAdapter.getState() == BluetoothAdapter.STATE_ON) return true; + return false; + } + + private boolean isValidDevice(BluetoothDevice device) { + if (device == null) return false; + + if (BluetoothAdapter.checkBluetoothAddress(device.getAddress())) return true; + return false; + } + + private static void log(String msg) { + if (DBG) { + Log.d(TAG, msg); + } + } } diff --git a/core/java/android/bluetooth/BluetoothMap.java b/core/java/android/bluetooth/BluetoothMap.java index 917e7fa04e4c..467470674286 100644 --- a/core/java/android/bluetooth/BluetoothMap.java +++ b/core/java/android/bluetooth/BluetoothMap.java @@ -17,7 +17,10 @@ package android.bluetooth; import android.Manifest; +import android.annotation.NonNull; +import android.annotation.Nullable; import android.annotation.RequiresPermission; +import android.annotation.SuppressLint; import android.annotation.SystemApi; import android.compat.annotation.UnsupportedAppUsage; import android.content.Context; @@ -35,21 +38,35 @@ import java.util.List; * * @hide */ +@SystemApi public final class BluetoothMap implements BluetoothProfile { private static final String TAG = "BluetoothMap"; private static final boolean DBG = true; private static final boolean VDBG = false; + /** @hide */ + @SuppressLint("ActionValue") + @SystemApi public static final String ACTION_CONNECTION_STATE_CHANGED = "android.bluetooth.map.profile.action.CONNECTION_STATE_CHANGED"; - /** There was an error trying to obtain the state */ + /** + * There was an error trying to obtain the state + * + * @hide + */ public static final int STATE_ERROR = -1; + /** @hide */ public static final int RESULT_FAILURE = 0; + /** @hide */ public static final int RESULT_SUCCESS = 1; - /** Connection canceled before completion. */ + /** + * Connection canceled before completion. + * + * @hide + */ public static final int RESULT_CANCELED = 2; private BluetoothAdapter mAdapter; @@ -71,6 +88,7 @@ public final class BluetoothMap implements BluetoothProfile { mProfileConnector.connect(context, listener); } + @SuppressLint("GenericException") protected void finalize() throws Throwable { try { close(); @@ -84,6 +102,8 @@ public final class BluetoothMap implements BluetoothProfile { * Other public functions of BluetoothMap will return default error * results once close() has been called. Multiple invocations of close() * are ok. + * + * @hide */ public synchronized void close() { mProfileConnector.disconnect(); @@ -98,6 +118,8 @@ public final class BluetoothMap implements BluetoothProfile { * * @return One of the STATE_ return codes, or STATE_ERROR if this proxy object is currently not * connected to the Map service. + * + * @hide */ public int getState() { if (VDBG) log("getState()"); @@ -120,6 +142,8 @@ public final class BluetoothMap implements BluetoothProfile { * * @return The remote Bluetooth device, or null if not in connected or connecting state, or if * this proxy object is not connected to the Map service. + * + * @hide */ public BluetoothDevice getClient() { if (VDBG) log("getClient()"); @@ -141,6 +165,8 @@ public final class BluetoothMap implements BluetoothProfile { * Returns true if the specified Bluetooth device is connected. * Returns false if not connected, or if this proxy object is not * currently connected to the Map service. + * + * @hide */ public boolean isConnected(BluetoothDevice device) { if (VDBG) log("isConnected(" + device + ")"); @@ -161,6 +187,8 @@ public final class BluetoothMap implements BluetoothProfile { /** * Initiate connection. Initiation of outgoing connections is not * supported for MAP server. + * + * @hide */ public boolean connect(BluetoothDevice device) { if (DBG) log("connect(" + device + ")" + "not supported for MAPS"); @@ -172,6 +200,8 @@ public final class BluetoothMap implements BluetoothProfile { * * @param device Remote Bluetooth Device * @return false on error, true otherwise + * + * @hide */ @UnsupportedAppUsage public boolean disconnect(BluetoothDevice device) { @@ -196,6 +226,8 @@ public final class BluetoothMap implements BluetoothProfile { * devices. It tries to err on the side of false positives. * * @return True if this device might support Map. + * + * @hide */ public static boolean doesClassMatchSink(BluetoothClass btClass) { // TODO optimize the rule @@ -214,8 +246,11 @@ public final class BluetoothMap implements BluetoothProfile { * Get the list of connected devices. Currently at most one. * * @return list of connected devices + * + * @hide */ - public List<BluetoothDevice> getConnectedDevices() { + @SystemApi + public @NonNull List<BluetoothDevice> getConnectedDevices() { if (DBG) log("getConnectedDevices()"); final IBluetoothMap service = getService(); if (service != null && isEnabled()) { @@ -234,6 +269,8 @@ public final class BluetoothMap implements BluetoothProfile { * Get the list of devices matching specified states. Currently at most one. * * @return list of matching devices + * + * @hide */ public List<BluetoothDevice> getDevicesMatchingConnectionStates(int[] states) { if (DBG) log("getDevicesMatchingStates()"); @@ -254,6 +291,8 @@ public final class BluetoothMap implements BluetoothProfile { * Get connection state of device * * @return device connection state + * + * @hide */ public int getConnectionState(BluetoothDevice device) { if (DBG) log("getConnectionState(" + device + ")"); @@ -301,7 +340,7 @@ public final class BluetoothMap implements BluetoothProfile { */ @SystemApi @RequiresPermission(Manifest.permission.BLUETOOTH_ADMIN) - public boolean setConnectionPolicy(BluetoothDevice device, int connectionPolicy) { + public boolean setConnectionPolicy(@Nullable BluetoothDevice device, int connectionPolicy) { if (DBG) log("setConnectionPolicy(" + device + ", " + connectionPolicy + ")"); final IBluetoothMap service = getService(); if (service != null && isEnabled() && isValidDevice(device)) { @@ -349,7 +388,7 @@ public final class BluetoothMap implements BluetoothProfile { */ @SystemApi @RequiresPermission(Manifest.permission.BLUETOOTH) - public int getConnectionPolicy(BluetoothDevice device) { + public int getConnectionPolicy(@Nullable BluetoothDevice device) { if (VDBG) log("getConnectionPolicy(" + device + ")"); final IBluetoothMap service = getService(); if (service != null && isEnabled() && isValidDevice(device)) { diff --git a/core/java/android/bluetooth/BluetoothPan.java b/core/java/android/bluetooth/BluetoothPan.java index 42f27f2a9c98..024bb06098ab 100644 --- a/core/java/android/bluetooth/BluetoothPan.java +++ b/core/java/android/bluetooth/BluetoothPan.java @@ -16,9 +16,11 @@ package android.bluetooth; +import android.Manifest; import android.annotation.IntDef; import android.annotation.NonNull; import android.annotation.Nullable; +import android.annotation.RequiresPermission; import android.annotation.SdkConstant; import android.annotation.SdkConstant.SdkConstantType; import android.annotation.SuppressLint; @@ -256,6 +258,41 @@ public final class BluetoothPan implements BluetoothProfile { } /** + * Set connection policy of the profile + * + * <p> The device should already be paired. + * Connection policy can be one of {@link #CONNECTION_POLICY_ALLOWED}, + * {@link #CONNECTION_POLICY_FORBIDDEN}, {@link #CONNECTION_POLICY_UNKNOWN} + * + * @param device Paired bluetooth device + * @param connectionPolicy is the connection policy to set to for this profile + * @return true if connectionPolicy is set, false on error + * @hide + */ + @SystemApi + @RequiresPermission(Manifest.permission.BLUETOOTH_ADMIN) + public boolean setConnectionPolicy(@NonNull BluetoothDevice device, + @ConnectionPolicy int connectionPolicy) { + if (DBG) log("setConnectionPolicy(" + device + ", " + connectionPolicy + ")"); + try { + final IBluetoothPan service = getService(); + if (service != null && isEnabled() + && isValidDevice(device)) { + if (connectionPolicy != BluetoothProfile.CONNECTION_POLICY_FORBIDDEN + && connectionPolicy != BluetoothProfile.CONNECTION_POLICY_ALLOWED) { + return false; + } + return service.setConnectionPolicy(device, connectionPolicy); + } + if (service == null) Log.w(TAG, "Proxy not attached to service"); + return false; + } catch (RemoteException e) { + Log.e(TAG, "Stack:" + Log.getStackTraceString(new Throwable())); + return false; + } + } + + /** * {@inheritDoc} */ @Override diff --git a/core/java/android/bluetooth/BluetoothPbap.java b/core/java/android/bluetooth/BluetoothPbap.java index 948885ed5010..e07ca521e77d 100644 --- a/core/java/android/bluetooth/BluetoothPbap.java +++ b/core/java/android/bluetooth/BluetoothPbap.java @@ -274,15 +274,15 @@ public class BluetoothPbap implements BluetoothProfile { } /** - * Pbap does not store connection policy, so this function only disconnects Pbap if - * connectionPolicy is CONNECTION_POLICY_FORBIDDEN. + * Pbap does not store connection policy, so this function only disconnects pbap if + * connectionPolicy is {@link #CONNECTION_POLICY_FORBIDDEN}. * * <p> The device should already be paired. * Connection policy can be one of {@link #CONNECTION_POLICY_ALLOWED}, * {@link #CONNECTION_POLICY_FORBIDDEN}, {@link #CONNECTION_POLICY_UNKNOWN} * * @param device Paired bluetooth device - * @param connectionPolicy is the connection policy to set to for this profile + * @param connectionPolicy determines whether to disconnect the device * @return true if pbap is successfully disconnected, false otherwise * @hide */ diff --git a/core/java/android/content/Context.java b/core/java/android/content/Context.java index 5cb2907dfba4..44b2df691876 100644 --- a/core/java/android/content/Context.java +++ b/core/java/android/content/Context.java @@ -3240,15 +3240,40 @@ public abstract class Context { } /** - * Same as {@link #bindService(Intent, ServiceConnection, int)}, but with an explicit userHandle - * argument for use by system server and other multi-user aware code. - * @hide + * Binds to a service in the given {@code user} in the same manner as + * {@link #bindService(Intent, ServiceConnection, int)}. + * + * <p>If the given {@code user} is in the same profile group and the target package is the + * same as the caller, {@code android.Manifest.permission.INTERACT_ACROSS_PROFILES} is + * sufficient. Otherwise, requires {@code android.Manifest.permission.INTERACT_ACROSS_USERS} + * for interacting with other users. + * + * @param service Identifies the service to connect to. The Intent must + * specify an explicit component name. + * @param conn Receives information as the service is started and stopped. + * This must be a valid ServiceConnection object; it must not be null. + * @param flags Operation options for the binding. May be 0, + * {@link #BIND_AUTO_CREATE}, {@link #BIND_DEBUG_UNBIND}, + * {@link #BIND_NOT_FOREGROUND}, {@link #BIND_ABOVE_CLIENT}, + * {@link #BIND_ALLOW_OOM_MANAGEMENT}, {@link #BIND_WAIVE_PRIORITY}. + * {@link #BIND_IMPORTANT}, or + * {@link #BIND_ADJUST_WITH_ACTIVITY}. + * @return {@code true} if the system is in the process of bringing up a + * service that your client has permission to bind to; {@code false} + * if the system couldn't find the service. If this value is {@code true}, you + * should later call {@link #unbindService} to release the + * connection. + * + * @throws SecurityException if the client does not have the required permission to bind. */ - @SystemApi @SuppressWarnings("unused") - @RequiresPermission(android.Manifest.permission.INTERACT_ACROSS_USERS) - public boolean bindServiceAsUser(@RequiresPermission Intent service, ServiceConnection conn, - int flags, UserHandle user) { + @RequiresPermission(anyOf = { + android.Manifest.permission.INTERACT_ACROSS_USERS, + android.Manifest.permission.INTERACT_ACROSS_PROFILES + }) + public boolean bindServiceAsUser( + @NonNull @RequiresPermission Intent service, @NonNull ServiceConnection conn, int flags, + @NonNull UserHandle user) { throw new RuntimeException("Not implemented. Must override in a subclass."); } @@ -3391,7 +3416,6 @@ public abstract class Context { TELEPHONY_SUBSCRIPTION_SERVICE, CARRIER_CONFIG_SERVICE, EUICC_SERVICE, - MMS_SERVICE, TELECOM_SERVICE, CLIPBOARD_SERVICE, INPUT_METHOD_SERVICE, @@ -3588,8 +3612,6 @@ public abstract class Context { * @see android.telephony.CarrierConfigManager * @see #EUICC_SERVICE * @see android.telephony.euicc.EuiccManager - * @see #MMS_SERVICE - * @see android.telephony.MmsManager * @see #INPUT_METHOD_SERVICE * @see android.view.inputmethod.InputMethodManager * @see #UI_MODE_SERVICE @@ -4266,15 +4288,6 @@ public abstract class Context { /** * Use with {@link #getSystemService(String)} to retrieve a - * {@link android.telephony.MmsManager} to send/receive MMS messages. - * - * @see #getSystemService(String) - * @see android.telephony.MmsManager - */ - public static final String MMS_SERVICE = "mms"; - - /** - * Use with {@link #getSystemService(String)} to retrieve a * {@link android.content.ClipboardManager} for accessing and modifying * the contents of the global clipboard. * diff --git a/core/java/android/content/Intent.java b/core/java/android/content/Intent.java index 3bb0f9222237..c8f587f71bca 100644 --- a/core/java/android/content/Intent.java +++ b/core/java/android/content/Intent.java @@ -6440,19 +6440,22 @@ public class Intent implements Parcelable, Cloneable { */ public static final int FLAG_RECEIVER_NO_ABORT = 0x08000000; /** - * If set, when sending a broadcast <i>before boot has completed</i> only + * If set, when sending a broadcast <i>before the system has fully booted up + * (which is even before {@link #ACTION_LOCKED_BOOT_COMPLETED} has been sent)"</i> only * registered receivers will be called -- no BroadcastReceiver components * will be launched. Sticky intent state will be recorded properly even * if no receivers wind up being called. If {@link #FLAG_RECEIVER_REGISTERED_ONLY} * is specified in the broadcast intent, this flag is unnecessary. * - * <p>This flag is only for use by system sevices as a convenience to - * avoid having to implement a more complex mechanism around detection + * <p>This flag is only for use by system services (even services from mainline modules) as a + * convenience to avoid having to implement a more complex mechanism around detection * of boot completion. * + * <p>This is useful to system server mainline modules + * * @hide */ - @UnsupportedAppUsage + @SystemApi public static final int FLAG_RECEIVER_REGISTERED_ONLY_BEFORE_BOOT = 0x04000000; /** * Set when this broadcast is for a boot upgrade, a special mode that diff --git a/core/java/android/content/pm/CrossProfileApps.java b/core/java/android/content/pm/CrossProfileApps.java index abf32c5e0840..9d5751480a80 100644 --- a/core/java/android/content/pm/CrossProfileApps.java +++ b/core/java/android/content/pm/CrossProfileApps.java @@ -16,15 +16,19 @@ package android.content.pm; import android.annotation.NonNull; +import android.annotation.Nullable; import android.annotation.RequiresPermission; import android.annotation.SystemApi; import android.content.ComponentName; import android.content.Context; +import android.content.Intent; import android.content.res.Resources; import android.graphics.drawable.Drawable; +import android.net.Uri; import android.os.RemoteException; import android.os.UserHandle; import android.os.UserManager; +import android.provider.Settings; import com.android.internal.R; import com.android.internal.util.UserIcons; @@ -226,6 +230,30 @@ public class CrossProfileApps { } } + /** + * Returns an {@link Intent} to open the settings page that allows the user to decide whether + * the calling app can interact across profiles. The current state is given by + * {@link #canInteractAcrossProfiles()}. + * + * <p>Returns {@code null} if {@link #canRequestInteractAcrossProfiles()} is {@code false}. + * + * @return an {@link Intent} to open the settings page that allows the user to decide whether + * the app can interact across profiles + * + * @throws SecurityException if {@code mContext.getPackageName()} does not belong to the + * calling UID. + */ + public @Nullable Intent createRequestInteractAcrossProfilesIntent() { + if (!canRequestInteractAcrossProfiles()) { + return null; + } + final Intent settingsIntent = new Intent(); + settingsIntent.setAction(Settings.ACTION_MANAGE_CROSS_PROFILE_ACCESS); + final Uri packageUri = Uri.parse("package:" + mContext.getPackageName()); + settingsIntent.setData(packageUri); + return settingsIntent; + } + private void verifyCanAccessUser(UserHandle userHandle) { if (!getTargetUserProfiles().contains(userHandle)) { throw new SecurityException("Not allowed to access " + userHandle); diff --git a/core/java/android/content/rollback/PackageRollbackInfo.java b/core/java/android/content/rollback/PackageRollbackInfo.java index 6378db0ebbbd..b273cd67479c 100644 --- a/core/java/android/content/rollback/PackageRollbackInfo.java +++ b/core/java/android/content/rollback/PackageRollbackInfo.java @@ -76,6 +76,11 @@ public final class PackageRollbackInfo implements Parcelable { */ private final boolean mIsApex; + /** + * Whether this instance represents the PackageRollbackInfo for an APK in APEX. + */ + private final boolean mIsApkInApex; + /* * The list of users for which snapshots have been saved. */ @@ -157,6 +162,10 @@ public final class PackageRollbackInfo implements Parcelable { public @PackageManager.RollbackDataPolicy int getRollbackDataPolicy() { return mRollbackDataPolicy; } + /** @hide */ + public boolean isApkInApex() { + return mIsApkInApex; + } /** @hide */ public IntArray getSnapshottedUsers() { @@ -190,17 +199,18 @@ public final class PackageRollbackInfo implements Parcelable { public PackageRollbackInfo(VersionedPackage packageRolledBackFrom, VersionedPackage packageRolledBackTo, @NonNull IntArray pendingBackups, @NonNull ArrayList<RestoreInfo> pendingRestores, - boolean isApex, @NonNull IntArray snapshottedUsers, + boolean isApex, boolean isApkInApex, @NonNull IntArray snapshottedUsers, @NonNull SparseLongArray ceSnapshotInodes) { this(packageRolledBackFrom, packageRolledBackTo, pendingBackups, pendingRestores, isApex, - snapshottedUsers, ceSnapshotInodes, PackageManager.RollbackDataPolicy.RESTORE); + isApkInApex, snapshottedUsers, ceSnapshotInodes, + PackageManager.RollbackDataPolicy.RESTORE); } /** @hide */ public PackageRollbackInfo(VersionedPackage packageRolledBackFrom, VersionedPackage packageRolledBackTo, @NonNull IntArray pendingBackups, @NonNull ArrayList<RestoreInfo> pendingRestores, - boolean isApex, @NonNull IntArray snapshottedUsers, + boolean isApex, boolean isApkInApex, @NonNull IntArray snapshottedUsers, @NonNull SparseLongArray ceSnapshotInodes, @PackageManager.RollbackDataPolicy int rollbackDataPolicy) { this.mVersionRolledBackFrom = packageRolledBackFrom; @@ -209,6 +219,7 @@ public final class PackageRollbackInfo implements Parcelable { this.mPendingRestores = pendingRestores; this.mIsApex = isApex; this.mRollbackDataPolicy = rollbackDataPolicy; + this.mIsApkInApex = isApkInApex; this.mSnapshottedUsers = snapshottedUsers; this.mCeSnapshotInodes = ceSnapshotInodes; } @@ -217,6 +228,7 @@ public final class PackageRollbackInfo implements Parcelable { this.mVersionRolledBackFrom = VersionedPackage.CREATOR.createFromParcel(in); this.mVersionRolledBackTo = VersionedPackage.CREATOR.createFromParcel(in); this.mIsApex = in.readBoolean(); + this.mIsApkInApex = in.readBoolean(); this.mPendingRestores = null; this.mPendingBackups = null; this.mSnapshottedUsers = null; @@ -234,6 +246,7 @@ public final class PackageRollbackInfo implements Parcelable { mVersionRolledBackFrom.writeToParcel(out, flags); mVersionRolledBackTo.writeToParcel(out, flags); out.writeBoolean(mIsApex); + out.writeBoolean(mIsApkInApex); } public static final @NonNull Parcelable.Creator<PackageRollbackInfo> CREATOR = diff --git a/core/java/android/os/UserManager.java b/core/java/android/os/UserManager.java index 6e199ce3a73f..d8fadfb41189 100644 --- a/core/java/android/os/UserManager.java +++ b/core/java/android/os/UserManager.java @@ -1850,10 +1850,7 @@ public class UserManager { * Checks if the calling app is running in a managed profile. * * @return whether the caller is in a managed profile. - * @hide */ - @SystemApi - @RequiresPermission(android.Manifest.permission.MANAGE_USERS) public boolean isManagedProfile() { // No need for synchronization. Once it becomes non-null, it'll be non-null forever. // Worst case we might end up calling the AIDL method multiple times but that's fine. diff --git a/core/java/android/provider/Settings.java b/core/java/android/provider/Settings.java index 06805238e6f5..a31c3d1749c6 100644 --- a/core/java/android/provider/Settings.java +++ b/core/java/android/provider/Settings.java @@ -389,6 +389,21 @@ public final class Settings { "android.settings.MANAGE_UNKNOWN_APP_SOURCES"; /** + * Activity Action: Show settings to allow configuration of cross-profile access for apps + * + * Input: Optionally, the Intent's data URI can specify the application package name to + * directly invoke the management GUI specific to the package name. For example + * "package:com.my.app". + * <p> + * Output: Nothing. + * + * @hide + */ + @SdkConstant(SdkConstantType.ACTIVITY_INTENT_ACTION) + public static final String ACTION_MANAGE_CROSS_PROFILE_ACCESS = + "android.settings.MANAGE_CROSS_PROFILE_ACCESS"; + + /** * Activity Action: Show the "Open by Default" page in a particular application's details page. * <p> * In some cases, a matching Activity may not exist, so ensure you safeguard against this. diff --git a/core/java/android/service/notification/INotificationListener.aidl b/core/java/android/service/notification/INotificationListener.aidl index 5977bafd7cf1..4ead3fc67260 100644 --- a/core/java/android/service/notification/INotificationListener.aidl +++ b/core/java/android/service/notification/INotificationListener.aidl @@ -49,6 +49,9 @@ oneway interface INotificationListener void onNotificationEnqueuedWithChannel(in IStatusBarNotificationHolder notificationHolder, in NotificationChannel channel); void onNotificationSnoozedUntilContext(in IStatusBarNotificationHolder notificationHolder, String snoozeCriterionId); void onNotificationsSeen(in List<String> keys); + void onPanelRevealed(int items); + void onPanelHidden(); + void onNotificationVisibilityChanged(String key, boolean isVisible); void onNotificationExpansionChanged(String key, boolean userAction, boolean expanded); void onNotificationDirectReply(String key); void onSuggestedReplySent(String key, in CharSequence reply, int source); diff --git a/core/java/android/service/notification/NotificationAssistantService.java b/core/java/android/service/notification/NotificationAssistantService.java index da4025419656..e976e18602c1 100644 --- a/core/java/android/service/notification/NotificationAssistantService.java +++ b/core/java/android/service/notification/NotificationAssistantService.java @@ -182,6 +182,32 @@ public abstract class NotificationAssistantService extends NotificationListenerS } /** + * Implement this to know when the notification panel is revealed + * + * @param items Number of items on the panel at time of opening + */ + public void onPanelRevealed(int items) { + + } + + /** + * Implement this to know when the notification panel is hidden + */ + public void onPanelHidden() { + + } + + /** + * Implement this to know when a notification becomes visible or hidden from the user. + * + * @param key the notification key + * @param isVisible whether the notification is visible. + */ + public void onNotificationVisibilityChanged(@NonNull String key, boolean isVisible) { + + } + + /** * Implement this to know when a notification change (expanded / collapsed) is visible to user. * * @param key the notification key @@ -337,6 +363,30 @@ public abstract class NotificationAssistantService extends NotificationListenerS } @Override + public void onPanelRevealed(int items) { + SomeArgs args = SomeArgs.obtain(); + args.argi1 = items; + mHandler.obtainMessage(MyHandler.MSG_ON_PANEL_REVEALED, + args).sendToTarget(); + } + + @Override + public void onPanelHidden() { + SomeArgs args = SomeArgs.obtain(); + mHandler.obtainMessage(MyHandler.MSG_ON_PANEL_HIDDEN, + args).sendToTarget(); + } + + @Override + public void onNotificationVisibilityChanged(String key, boolean isVisible) { + SomeArgs args = SomeArgs.obtain(); + args.arg1 = key; + args.argi1 = isVisible ? 1 : 0; + mHandler.obtainMessage(MyHandler.MSG_ON_NOTIFICATION_VISIBILITY_CHANGED, + args).sendToTarget(); + } + + @Override public void onNotificationExpansionChanged(String key, boolean isUserAction, boolean isExpanded) { SomeArgs args = SomeArgs.obtain(); @@ -394,6 +444,9 @@ public abstract class NotificationAssistantService extends NotificationListenerS public static final int MSG_ON_SUGGESTED_REPLY_SENT = 6; public static final int MSG_ON_ACTION_INVOKED = 7; public static final int MSG_ON_ALLOWED_ADJUSTMENTS_CHANGED = 8; + public static final int MSG_ON_PANEL_REVEALED = 9; + public static final int MSG_ON_PANEL_HIDDEN = 10; + public static final int MSG_ON_NOTIFICATION_VISIBILITY_CHANGED = 11; public MyHandler(Looper looper) { super(looper, null, false); @@ -480,6 +533,25 @@ public abstract class NotificationAssistantService extends NotificationListenerS onAllowedAdjustmentsChanged(); break; } + case MSG_ON_PANEL_REVEALED: { + SomeArgs args = (SomeArgs) msg.obj; + int items = args.argi1; + args.recycle(); + onPanelRevealed(items); + break; + } + case MSG_ON_PANEL_HIDDEN: { + onPanelHidden(); + break; + } + case MSG_ON_NOTIFICATION_VISIBILITY_CHANGED: { + SomeArgs args = (SomeArgs) msg.obj; + String key = (String) args.arg1; + boolean isVisible = args.argi1 == 1; + args.recycle(); + onNotificationVisibilityChanged(key, isVisible); + break; + } } } } diff --git a/core/java/android/service/notification/NotificationListenerService.java b/core/java/android/service/notification/NotificationListenerService.java index 80d054b700f8..fd04f499a432 100644 --- a/core/java/android/service/notification/NotificationListenerService.java +++ b/core/java/android/service/notification/NotificationListenerService.java @@ -1383,6 +1383,22 @@ public abstract class NotificationListenerService extends Service { } @Override + public void onPanelRevealed(int items) throws RemoteException { + // no-op in the listener + } + + @Override + public void onPanelHidden() throws RemoteException { + // no-op in the listener + } + + @Override + public void onNotificationVisibilityChanged( + String key, boolean isVisible) { + // no-op in the listener + } + + @Override public void onNotificationSnoozedUntilContext( IStatusBarNotificationHolder notificationHolder, String snoozeCriterionId) throws RemoteException { diff --git a/core/java/android/view/ITaskOrganizer.aidl b/core/java/android/view/ITaskOrganizer.aidl new file mode 100644 index 000000000000..e92aafed6f22 --- /dev/null +++ b/core/java/android/view/ITaskOrganizer.aidl @@ -0,0 +1,38 @@ +/* //device/java/android/android/view/ITaskOrganizer.aidl +** +** Copyright 2019, The Android Open Source Project +** +** Licensed under the Apache License, Version 2.0 (the "License"); +** you may not use this file except in compliance with the License. +** You may obtain a copy of the License at +** +** http://www.apache.org/licenses/LICENSE-2.0 +** +** Unless required by applicable law or agreed to in writing, software +** distributed under the License is distributed on an "AS IS" BASIS, +** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +** See the License for the specific language governing permissions and +** limitations under the License. +*/ + +package android.view; + +import android.view.IWindowContainer; +import android.view.SurfaceControl; +import android.app.ActivityManager; + +/** + * Interface for ActivityTaskManager/WindowManager to delegate control of tasks. + * {@hide} + */ +oneway interface ITaskOrganizer { + void taskAppeared(in IWindowContainer container, + in ActivityManager.RunningTaskInfo taskInfo); + void taskVanished(in IWindowContainer container); + + /** + * Called upon completion of + * ActivityTaskManagerService#applyTaskOrganizerTransaction + */ + void transactionReady(int id, in SurfaceControl.Transaction t); +}
\ No newline at end of file diff --git a/core/java/android/view/WindowContainerTransaction.java b/core/java/android/view/WindowContainerTransaction.java index 607a87047733..253794f70ef2 100644 --- a/core/java/android/view/WindowContainerTransaction.java +++ b/core/java/android/view/WindowContainerTransaction.java @@ -62,6 +62,18 @@ public class WindowContainerTransaction implements Parcelable { return this; } + /** + * Notify activies within the hiearchy of a container that they have entered picture-in-picture + * mode with the given bounds. + */ + public WindowContainerTransaction scheduleFinishEnterPip(IWindowContainer container, + Rect bounds) { + Change chg = getOrCreateChange(container.asBinder()); + chg.mSchedulePipCallback = true; + chg.mPinnedBounds = new Rect(bounds); + return this; + } + public Map<IBinder, Change> getChanges() { return mChanges; } @@ -104,12 +116,20 @@ public class WindowContainerTransaction implements Parcelable { private @ActivityInfo.Config int mConfigSetMask = 0; private @WindowConfiguration.WindowConfig int mWindowSetMask = 0; + private boolean mSchedulePipCallback = false; + private Rect mPinnedBounds = null; + public Change() {} protected Change(Parcel in) { mConfiguration.readFromParcel(in); mConfigSetMask = in.readInt(); mWindowSetMask = in.readInt(); + mSchedulePipCallback = (in.readInt() != 0); + if (mSchedulePipCallback ) { + mPinnedBounds = new Rect(); + mPinnedBounds.readFromParcel(in); + } } public Configuration getConfiguration() { @@ -126,6 +146,14 @@ public class WindowContainerTransaction implements Parcelable { return mWindowSetMask; } + /** + * Returns the bounds to be used for scheduling the enter pip callback + * or null if no callback is to be scheduled. + */ + public Rect getEnterPipBounds() { + return mPinnedBounds; + } + @Override public String toString() { final boolean changesBounds = @@ -151,6 +179,11 @@ public class WindowContainerTransaction implements Parcelable { mConfiguration.writeToParcel(dest, flags); dest.writeInt(mConfigSetMask); dest.writeInt(mWindowSetMask); + + dest.writeInt(mSchedulePipCallback ? 1 : 0); + if (mSchedulePipCallback ) { + mPinnedBounds.writeToParcel(dest, flags); + } } @Override diff --git a/core/java/android/widget/RemoteViews.java b/core/java/android/widget/RemoteViews.java index d86766ebdf58..01a0e9b15463 100644 --- a/core/java/android/widget/RemoteViews.java +++ b/core/java/android/widget/RemoteViews.java @@ -565,7 +565,8 @@ public class RemoteViews implements Parcelable, Filter { } private static void visitIconUri(Icon icon, @NonNull Consumer<Uri> visitor) { - if (icon != null && icon.getType() == Icon.TYPE_URI) { + if (icon != null && (icon.getType() == Icon.TYPE_URI + || icon.getType() == Icon.TYPE_URI_ADAPTIVE_BITMAP)) { visitor.accept(icon.getUri()); } } diff --git a/core/java/com/android/internal/logging/UiEventLogger.java b/core/java/com/android/internal/logging/UiEventLogger.java index cca97f689f4e..3a450de6c8e6 100644 --- a/core/java/com/android/internal/logging/UiEventLogger.java +++ b/core/java/com/android/internal/logging/UiEventLogger.java @@ -16,6 +16,9 @@ package com.android.internal.logging; +import android.annotation.NonNull; +import android.annotation.Nullable; + /** * Logging interface for UI events. Normal implementation is UiEventLoggerImpl. * For testing, use fake implementation UiEventLoggerFake. @@ -26,13 +29,24 @@ public interface UiEventLogger { /** Put your Event IDs in enums that implement this interface, and document them using the * UiEventEnum annotation. * Event IDs must be globally unique. This will be enforced by tooling (forthcoming). - * OEMs should use event IDs above 100000. + * OEMs should use event IDs above 100000 and below 1000000 (1 million). */ interface UiEventEnum { int getId(); } + + /** + * Log a simple event, with no package information. Does nothing if event.getId() <= 0. + * @param event an enum implementing UiEventEnum interface. + */ + void log(@NonNull UiEventEnum event); + /** - * Log a simple event, with no package or instance ID. + * Log an event with package information. Does nothing if event.getId() <= 0. + * Give both uid and packageName if both are known, but one may be omitted if unknown. + * @param event an enum implementing UiEventEnum interface. + * @param uid the uid of the relevant app, if known (0 otherwise). + * @param packageName the package name of the relevant app, if known (null otherwise). */ - void log(UiEventEnum eventID); + void log(@NonNull UiEventEnum event, int uid, @Nullable String packageName); } diff --git a/core/java/com/android/internal/logging/UiEventLoggerImpl.java b/core/java/com/android/internal/logging/UiEventLoggerImpl.java index e64fba2d2a31..bdf460c710c3 100644 --- a/core/java/com/android/internal/logging/UiEventLoggerImpl.java +++ b/core/java/com/android/internal/logging/UiEventLoggerImpl.java @@ -24,14 +24,16 @@ import android.util.StatsLog; * See UiEventReported atom in atoms.proto for more context. */ public class UiEventLoggerImpl implements UiEventLogger { - /** - * Log a simple event, with no package or instance ID. - */ @Override public void log(UiEventEnum event) { + log(event, 0, null); + } + + @Override + public void log(UiEventEnum event, int uid, String packageName) { final int eventID = event.getId(); if (eventID > 0) { - StatsLog.write(StatsLog.UI_EVENT_REPORTED, eventID, 0, null); + StatsLog.write(StatsLog.UI_EVENT_REPORTED, eventID, uid, packageName); } } } diff --git a/core/java/com/android/internal/logging/testing/UiEventLoggerFake.java b/core/java/com/android/internal/logging/testing/UiEventLoggerFake.java index 92e9bbb77bd3..6be5b81afee2 100644 --- a/core/java/com/android/internal/logging/testing/UiEventLoggerFake.java +++ b/core/java/com/android/internal/logging/testing/UiEventLoggerFake.java @@ -30,7 +30,7 @@ public class UiEventLoggerFake implements UiEventLogger { /** * Immutable data class used to record fake log events. */ - public class FakeUiEvent { + public static class FakeUiEvent { public final int eventId; public final int uid; public final String packageName; @@ -44,15 +44,20 @@ public class UiEventLoggerFake implements UiEventLogger { private Queue<FakeUiEvent> mLogs = new LinkedList<FakeUiEvent>(); + public Queue<FakeUiEvent> getLogs() { + return mLogs; + } + @Override public void log(UiEventEnum event) { + log(event, 0, null); + } + + @Override + public void log(UiEventEnum event, int uid, String packageName) { final int eventId = event.getId(); if (eventId > 0) { - mLogs.offer(new FakeUiEvent(eventId, 0, null)); + mLogs.offer(new FakeUiEvent(eventId, uid, packageName)); } } - - public Queue<FakeUiEvent> getLogs() { - return mLogs; - } } diff --git a/core/java/com/android/internal/util/Preconditions.java b/core/java/com/android/internal/util/Preconditions.java index 5bc96d8ee1d3..408a7a8e139a 100644 --- a/core/java/com/android/internal/util/Preconditions.java +++ b/core/java/com/android/internal/util/Preconditions.java @@ -126,7 +126,9 @@ public class Preconditions { * @param reference an object reference * @return the non-null reference that was validated * @throws NullPointerException if {@code reference} is null + * @deprecated - use {@link java.util.Objects.requireNonNull} instead. */ + @Deprecated @UnsupportedAppUsage public static @NonNull <T> T checkNotNull(final T reference) { if (reference == null) { @@ -144,7 +146,9 @@ public class Preconditions { * be converted to a string using {@link String#valueOf(Object)} * @return the non-null reference that was validated * @throws NullPointerException if {@code reference} is null + * @deprecated - use {@link java.util.Objects.requireNonNull} instead. */ + @Deprecated @UnsupportedAppUsage public static @NonNull <T> T checkNotNull(final T reference, final Object errorMessage) { if (reference == null) { @@ -154,26 +158,6 @@ public class Preconditions { } /** - * Ensures that an object reference passed as a parameter to the calling - * method is not null. - * - * @param reference an object reference - * @param messageTemplate a printf-style message template to use if the check fails; will - * be converted to a string using {@link String#format(String, Object...)} - * @param messageArgs arguments for {@code messageTemplate} - * @return the non-null reference that was validated - * @throws NullPointerException if {@code reference} is null - */ - public static @NonNull <T> T checkNotNull(final T reference, - final String messageTemplate, - final Object... messageArgs) { - if (reference == null) { - throw new NullPointerException(String.format(messageTemplate, messageArgs)); - } - return reference; - } - - /** * Ensures the truth of an expression involving the state of the calling * instance, but not involving any parameters to the calling method. * diff --git a/core/res/AndroidManifest.xml b/core/res/AndroidManifest.xml index 03d34b595e4a..d887032dbdbc 100644 --- a/core/res/AndroidManifest.xml +++ b/core/res/AndroidManifest.xml @@ -370,6 +370,7 @@ <protected-broadcast android:name="android.net.wifi.STATE_CHANGE" /> <protected-broadcast android:name="android.net.wifi.LINK_CONFIGURATION_CHANGED" /> <protected-broadcast android:name="android.net.wifi.CONFIGURED_NETWORKS_CHANGE" /> + <protected-broadcast android:name="android.net.wifi.action.NETWORK_SETTINGS_RESET" /> <protected-broadcast android:name="android.net.wifi.action.PASSPOINT_DEAUTH_IMMINENT" /> <protected-broadcast android:name="android.net.wifi.action.PASSPOINT_ICON" /> <protected-broadcast android:name="android.net.wifi.action.PASSPOINT_OSU_PROVIDERS_LIST" /> diff --git a/core/res/res/values/public.xml b/core/res/res/values/public.xml index 817ccde98504..37c971021b2e 100644 --- a/core/res/res/values/public.xml +++ b/core/res/res/values/public.xml @@ -3000,7 +3000,6 @@ <public-group type="attr" first-id="0x01010607"> <public name="importantForContentCapture" /> <public name="forceQueryable" /> - <!-- @hide @SystemApi --> <public name="resourcesMap" /> <public name="animatedImageDrawable"/> <public name="htmlDescription"/> diff --git a/core/tests/coretests/src/android/net/NetworkKeyTest.java b/core/tests/coretests/src/android/net/NetworkKeyTest.java index c6c0b46d0505..b13bcd1311f6 100644 --- a/core/tests/coretests/src/android/net/NetworkKeyTest.java +++ b/core/tests/coretests/src/android/net/NetworkKeyTest.java @@ -18,6 +18,7 @@ package android.net; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertNull; +import static org.mockito.Mockito.mock; import static org.mockito.Mockito.when; import android.net.wifi.ScanResult; @@ -107,7 +108,7 @@ public class NetworkKeyTest { @Test public void createFromScanResult_nullSsid() { - ScanResult scanResult = new ScanResult(); + ScanResult scanResult = mock(ScanResult.class); scanResult.BSSID = VALID_BSSID; assertNull(NetworkKey.createFromScanResult(scanResult)); @@ -115,7 +116,7 @@ public class NetworkKeyTest { @Test public void createFromScanResult_emptySsid() { - ScanResult scanResult = new ScanResult(); + ScanResult scanResult = mock(ScanResult.class); scanResult.SSID = ""; scanResult.BSSID = VALID_BSSID; @@ -124,7 +125,7 @@ public class NetworkKeyTest { @Test public void createFromScanResult_noneSsid() { - ScanResult scanResult = new ScanResult(); + ScanResult scanResult = mock(ScanResult.class); scanResult.SSID = WifiManager.UNKNOWN_SSID; scanResult.BSSID = VALID_BSSID; @@ -133,7 +134,7 @@ public class NetworkKeyTest { @Test public void createFromScanResult_nullBssid() { - ScanResult scanResult = new ScanResult(); + ScanResult scanResult = mock(ScanResult.class); scanResult.SSID = VALID_UNQUOTED_SSID; assertNull(NetworkKey.createFromScanResult(scanResult)); @@ -141,7 +142,7 @@ public class NetworkKeyTest { @Test public void createFromScanResult_emptyBssid() { - ScanResult scanResult = new ScanResult(); + ScanResult scanResult = mock(ScanResult.class); scanResult.SSID = VALID_UNQUOTED_SSID; scanResult.BSSID = ""; @@ -150,7 +151,7 @@ public class NetworkKeyTest { @Test public void createFromScanResult_invalidBssid() { - ScanResult scanResult = new ScanResult(); + ScanResult scanResult = mock(ScanResult.class); scanResult.SSID = VALID_UNQUOTED_SSID; scanResult.BSSID = INVALID_BSSID; @@ -159,7 +160,7 @@ public class NetworkKeyTest { @Test public void createFromScanResult_validSsid() { - ScanResult scanResult = new ScanResult(); + ScanResult scanResult = mock(ScanResult.class); scanResult.SSID = VALID_UNQUOTED_SSID; scanResult.BSSID = VALID_BSSID; diff --git a/data/etc/OWNERS b/data/etc/OWNERS index ea66ee373785..70d467829269 100644 --- a/data/etc/OWNERS +++ b/data/etc/OWNERS @@ -1 +1 @@ -per-file privapp-permissions-platform.xml = hackbod@android.com, jsharkey@android.com, svetoslavganov@google.com, toddke@google.com, yamasani@google.com, cbrubaker@google.com, jeffv@google.com, moltmann@google.com +per-file privapp-permissions-platform.xml = hackbod@android.com, jsharkey@android.com, svetoslavganov@google.com, toddke@google.com, yamasani@google.com, cbrubaker@google.com, jeffv@google.com, moltmann@google.com, lorenzo@google.com diff --git a/libs/hwui/Android.bp b/libs/hwui/Android.bp index d945fc49ec88..a7f8cc4688ef 100644 --- a/libs/hwui/Android.bp +++ b/libs/hwui/Android.bp @@ -215,6 +215,7 @@ cc_defaults { android: { srcs: [ + "pipeline/skia/ATraceMemoryDump.cpp", "pipeline/skia/GLFunctorDrawable.cpp", "pipeline/skia/LayerDrawable.cpp", "pipeline/skia/ShaderCache.cpp", @@ -244,7 +245,6 @@ cc_defaults { "DeviceInfo.cpp", "FrameInfo.cpp", "FrameInfoVisualizer.cpp", - "GpuMemoryTracker.cpp", "HardwareBitmapUploader.cpp", "HWUIProperties.sysprop", "JankTracker.cpp", @@ -325,7 +325,6 @@ cc_test { "tests/unit/DamageAccumulatorTests.cpp", "tests/unit/DeferredLayerUpdaterTests.cpp", "tests/unit/FatVectorTests.cpp", - "tests/unit/GpuMemoryTrackerTests.cpp", "tests/unit/GraphicsStatsServiceTests.cpp", "tests/unit/LayerUpdateQueueTests.cpp", "tests/unit/LinearAllocatorTests.cpp", diff --git a/libs/hwui/GpuMemoryTracker.cpp b/libs/hwui/GpuMemoryTracker.cpp deleted file mode 100644 index a9a7af8f22f3..000000000000 --- a/libs/hwui/GpuMemoryTracker.cpp +++ /dev/null @@ -1,122 +0,0 @@ -/* - * Copyright (C) 2016 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. - */ - -#include "utils/StringUtils.h" - -#include <GpuMemoryTracker.h> -#include <cutils/compiler.h> -#include <utils/Trace.h> -#include <array> -#include <sstream> -#include <unordered_set> -#include <vector> - -namespace android { -namespace uirenderer { - -pthread_t gGpuThread = 0; - -#define NUM_TYPES static_cast<int>(GpuObjectType::TypeCount) - -const char* TYPE_NAMES[] = { - "Texture", "OffscreenBuffer", "Layer", -}; - -struct TypeStats { - int totalSize = 0; - int count = 0; -}; - -static std::array<TypeStats, NUM_TYPES> gObjectStats; -static std::unordered_set<GpuMemoryTracker*> gObjectSet; - -void GpuMemoryTracker::notifySizeChanged(int newSize) { - int delta = newSize - mSize; - mSize = newSize; - gObjectStats[static_cast<int>(mType)].totalSize += delta; -} - -void GpuMemoryTracker::startTrackingObject() { - auto result = gObjectSet.insert(this); - LOG_ALWAYS_FATAL_IF(!result.second, - "startTrackingObject() on %p failed, already being tracked!", this); - gObjectStats[static_cast<int>(mType)].count++; -} - -void GpuMemoryTracker::stopTrackingObject() { - size_t removed = gObjectSet.erase(this); - LOG_ALWAYS_FATAL_IF(removed != 1, "stopTrackingObject removed %zd, is %p not being tracked?", - removed, this); - gObjectStats[static_cast<int>(mType)].count--; -} - -void GpuMemoryTracker::onGpuContextCreated() { - LOG_ALWAYS_FATAL_IF(gGpuThread != 0, - "We already have a gpu thread? " - "current = %lu, gpu thread = %lu", - pthread_self(), gGpuThread); - gGpuThread = pthread_self(); -} - -void GpuMemoryTracker::onGpuContextDestroyed() { - gGpuThread = 0; - if (CC_UNLIKELY(gObjectSet.size() > 0)) { - std::stringstream os; - dump(os); - ALOGE("%s", os.str().c_str()); - LOG_ALWAYS_FATAL("Leaked %zd GPU objects!", gObjectSet.size()); - } -} - -void GpuMemoryTracker::dump() { - std::stringstream strout; - dump(strout); - ALOGD("%s", strout.str().c_str()); -} - -void GpuMemoryTracker::dump(std::ostream& stream) { - for (int type = 0; type < NUM_TYPES; type++) { - const TypeStats& stats = gObjectStats[type]; - stream << TYPE_NAMES[type]; - stream << " is using " << SizePrinter{stats.totalSize}; - stream << ", count = " << stats.count; - stream << std::endl; - } -} - -int GpuMemoryTracker::getInstanceCount(GpuObjectType type) { - return gObjectStats[static_cast<int>(type)].count; -} - -int GpuMemoryTracker::getTotalSize(GpuObjectType type) { - return gObjectStats[static_cast<int>(type)].totalSize; -} - -void GpuMemoryTracker::onFrameCompleted() { - if (ATRACE_ENABLED()) { - char buf[128]; - for (int type = 0; type < NUM_TYPES; type++) { - snprintf(buf, 128, "hwui_%s", TYPE_NAMES[type]); - const TypeStats& stats = gObjectStats[type]; - ATRACE_INT(buf, stats.totalSize); - snprintf(buf, 128, "hwui_%s_count", TYPE_NAMES[type]); - ATRACE_INT(buf, stats.count); - } - } -} - -} // namespace uirenderer -} // namespace android; diff --git a/libs/hwui/GpuMemoryTracker.h b/libs/hwui/GpuMemoryTracker.h deleted file mode 100644 index de3ca99ef14b..000000000000 --- a/libs/hwui/GpuMemoryTracker.h +++ /dev/null @@ -1,77 +0,0 @@ -/* - * Copyright (C) 2016 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. - */ -#pragma once - -#include <pthread.h> -#include <ostream> - -#include <log/log.h> - -namespace android { -namespace uirenderer { - -extern pthread_t gGpuThread; - -#define ASSERT_GPU_THREAD() \ - LOG_ALWAYS_FATAL_IF(!pthread_equal(gGpuThread, pthread_self()), \ - "Error, %p of type %d (size=%d) used on wrong thread! cur thread %lu " \ - "!= gpu thread %lu", \ - this, static_cast<int>(mType), mSize, pthread_self(), gGpuThread) - -enum class GpuObjectType { - Texture = 0, - OffscreenBuffer, - Layer, - - TypeCount, -}; - -class GpuMemoryTracker { -public: - GpuObjectType objectType() { return mType; } - int objectSize() { return mSize; } - - static void onGpuContextCreated(); - static void onGpuContextDestroyed(); - static void dump(); - static void dump(std::ostream& stream); - static int getInstanceCount(GpuObjectType type); - static int getTotalSize(GpuObjectType type); - static void onFrameCompleted(); - -protected: - explicit GpuMemoryTracker(GpuObjectType type) : mType(type) { - ASSERT_GPU_THREAD(); - startTrackingObject(); - } - - ~GpuMemoryTracker() { - notifySizeChanged(0); - stopTrackingObject(); - } - - void notifySizeChanged(int newSize); - -private: - void startTrackingObject(); - void stopTrackingObject(); - - int mSize = 0; - GpuObjectType mType; -}; - -} // namespace uirenderer -} // namespace android; diff --git a/libs/hwui/pipeline/skia/ATraceMemoryDump.cpp b/libs/hwui/pipeline/skia/ATraceMemoryDump.cpp new file mode 100644 index 000000000000..2c78b024536b --- /dev/null +++ b/libs/hwui/pipeline/skia/ATraceMemoryDump.cpp @@ -0,0 +1,175 @@ +/* + * Copyright (C) 2020 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "ATraceMemoryDump.h" + +#include <utils/Trace.h> + +#include <cstring> + +namespace android { +namespace uirenderer { +namespace skiapipeline { + +// When purgeable is INVALID_TIME it won't be logged at all. +#define INVALID_TIME -1 + +/** + * Skia invokes the following SkTraceMemoryDump functions: + * 1. dumpNumericValue (dumpName, units="bytes", valueName="size") + * 2. dumpStringValue (dumpName, valueName="type") [optional -> for example CPU memory does not + * invoke dumpStringValue] + * 3. dumpNumericValue (dumpName, units="bytes", valueName="purgeable_size") [optional] + * 4. setMemoryBacking(dumpName, backingType) [optional -> for example Vulkan GPU resources do not + * invoke setMemoryBacking] + * + * ATraceMemoryDump calculates memory category first by looking at the "type" string passed to + * dumpStringValue and then by looking at "backingType" passed to setMemoryBacking. + * Only GPU Texture memory is tracked separately and everything else is grouped as one + * "GPU Memory" category. + */ +static std::unordered_map<const char*, const char*> sResourceMap = { + {"malloc", "Graphics CPU Memory"}, // taken from setMemoryBacking(backingType) + {"gl_texture", "Graphics Texture Memory"}, // taken from setMemoryBacking(backingType) + {"Texture", + "Graphics Texture Memory"}, // taken from dumpStringValue(value, valueName="type") + // Uncomment categories below to split "GPU Memory" into more brackets for debugging. + /*{"vk_buffer", "vk_buffer"}, + {"gl_renderbuffer", "gl_renderbuffer"}, + {"gl_buffer", "gl_buffer"}, + {"RenderTarget", "RenderTarget"}, + {"Stencil", "Stencil"}, + {"Path Data", "Path Data"}, + {"Buffer Object", "Buffer Object"}, + {"Surface", "Surface"},*/ +}; + +ATraceMemoryDump::ATraceMemoryDump() { + mLastDumpName.reserve(100); + mCategory.reserve(100); +} + +void ATraceMemoryDump::dumpNumericValue(const char* dumpName, const char* valueName, + const char* units, uint64_t value) { + if (!strcmp(units, "bytes")) { + recordAndResetCountersIfNeeded(dumpName); + if (!strcmp(valueName, "size")) { + mLastDumpValue = value; + } else if (!strcmp(valueName, "purgeable_size")) { + mLastPurgeableDumpValue = value; + } + } +} + +void ATraceMemoryDump::dumpStringValue(const char* dumpName, const char* valueName, + const char* value) { + if (!strcmp(valueName, "type")) { + recordAndResetCountersIfNeeded(dumpName); + auto categoryIt = sResourceMap.find(value); + if (categoryIt != sResourceMap.end()) { + mCategory = categoryIt->second; + } + } +} + +void ATraceMemoryDump::setMemoryBacking(const char* dumpName, const char* backingType, + const char* backingObjectId) { + recordAndResetCountersIfNeeded(dumpName); + auto categoryIt = sResourceMap.find(backingType); + if (categoryIt != sResourceMap.end()) { + mCategory = categoryIt->second; + } +} + +/** + * startFrame is invoked before dumping anything. It resets counters from the previous frame. + * This is important, because if there is no new data for a given category trace would assume + * usage has not changed (instead of reporting 0). + */ +void ATraceMemoryDump::startFrame() { + resetCurrentCounter(""); + for (auto& it : mCurrentValues) { + // Once a category is observed in at least one frame, it is always reported in subsequent + // frames (even if it is 0). Not logging a category to ATRACE would mean its value has not + // changed since the previous frame, which is not what we want. + it.second.time = 0; + // If purgeableTime is INVALID_TIME, then logTraces won't log it at all. + if (it.second.purgeableTime != INVALID_TIME) { + it.second.purgeableTime = 0; + } + } +} + +/** + * logTraces reads from mCurrentValues and logs the counters with ATRACE. + */ +void ATraceMemoryDump::logTraces() { + // Accumulate data from last dumpName + recordAndResetCountersIfNeeded(""); + for (auto& it : mCurrentValues) { + ATRACE_INT64(it.first.c_str(), it.second.time); + if (it.second.purgeableTime != INVALID_TIME) { + ATRACE_INT64((std::string("Purgeable ") + it.first).c_str(), it.second.purgeableTime); + } + } +} + +/** + * recordAndResetCountersIfNeeded reads memory usage from mLastDumpValue/mLastPurgeableDumpValue and + * accumulates in mCurrentValues[category]. It makes provision to create a new category and track + * purgeable memory only if there is at least one observation. + * recordAndResetCountersIfNeeded won't do anything until all the information for a given dumpName + * is received. + */ +void ATraceMemoryDump::recordAndResetCountersIfNeeded(const char* dumpName) { + if (!mLastDumpName.compare(dumpName)) { + // Still waiting for more data for current dumpName. + return; + } + + // First invocation will have an empty mLastDumpName. + if (!mLastDumpName.empty()) { + // A new dumpName observed -> store the data already collected. + auto memoryCounter = mCurrentValues.find(mCategory); + if (memoryCounter != mCurrentValues.end()) { + memoryCounter->second.time += mLastDumpValue; + if (mLastPurgeableDumpValue != INVALID_TIME) { + if (memoryCounter->second.purgeableTime == INVALID_TIME) { + memoryCounter->second.purgeableTime = mLastPurgeableDumpValue; + } else { + memoryCounter->second.purgeableTime += mLastPurgeableDumpValue; + } + } + } else { + mCurrentValues[mCategory] = {mLastDumpValue, mLastPurgeableDumpValue}; + } + } + + // Reset counters and default category for the newly observed "dumpName". + resetCurrentCounter(dumpName); +} + +void ATraceMemoryDump::resetCurrentCounter(const char* dumpName) { + mLastDumpValue = 0; + mLastPurgeableDumpValue = INVALID_TIME; + mLastDumpName = dumpName; + // Categories not listed in sResourceMap are reported as "GPU memory" + mCategory = "GPU Memory"; +} + +} /* namespace skiapipeline */ +} /* namespace uirenderer */ +} /* namespace android */ diff --git a/libs/hwui/pipeline/skia/ATraceMemoryDump.h b/libs/hwui/pipeline/skia/ATraceMemoryDump.h new file mode 100644 index 000000000000..aa5c401ad1ca --- /dev/null +++ b/libs/hwui/pipeline/skia/ATraceMemoryDump.h @@ -0,0 +1,79 @@ +/* + * Copyright (C) 2020 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#pragma once + +#include <SkString.h> +#include <SkTraceMemoryDump.h> + +#include <string> +#include <unordered_map> +#include <utility> + +namespace android { +namespace uirenderer { +namespace skiapipeline { + +class ATraceMemoryDump : public SkTraceMemoryDump { +public: + ATraceMemoryDump(); + ~ATraceMemoryDump() override {} + + void dumpNumericValue(const char* dumpName, const char* valueName, const char* units, + uint64_t value) override; + + void dumpStringValue(const char* dumpName, const char* valueName, const char* value) override; + + LevelOfDetail getRequestedDetails() const override { + return SkTraceMemoryDump::kLight_LevelOfDetail; + } + + bool shouldDumpWrappedObjects() const override { return false; } + + void setMemoryBacking(const char* dumpName, const char* backingType, + const char* backingObjectId) override; + + void setDiscardableMemoryBacking(const char*, const SkDiscardableMemory&) override {} + + void startFrame(); + + void logTraces(); + +private: + std::string mLastDumpName; + + uint64_t mLastDumpValue; + + uint64_t mLastPurgeableDumpValue; + + std::string mCategory; + + struct TraceValue { + uint64_t time; + uint64_t purgeableTime; + }; + + // keys are define in sResourceMap + std::unordered_map<std::string, TraceValue> mCurrentValues; + + void recordAndResetCountersIfNeeded(const char* dumpName); + + void resetCurrentCounter(const char* dumpName); +}; + +} /* namespace skiapipeline */ +} /* namespace uirenderer */ +} /* namespace android */
\ No newline at end of file diff --git a/libs/hwui/renderstate/RenderState.cpp b/libs/hwui/renderstate/RenderState.cpp index fad9440be73f..7e8c96d96860 100644 --- a/libs/hwui/renderstate/RenderState.cpp +++ b/libs/hwui/renderstate/RenderState.cpp @@ -16,7 +16,6 @@ #include "renderstate/RenderState.h" #include "renderthread/RenderThread.h" -#include "GpuMemoryTracker.h" namespace android { namespace uirenderer { @@ -25,15 +24,10 @@ RenderState::RenderState(renderthread::RenderThread& thread) : mRenderThread(thr mThreadId = pthread_self(); } -void RenderState::onContextCreated() { - GpuMemoryTracker::onGpuContextCreated(); -} - void RenderState::onContextDestroyed() { for(auto callback : mContextCallbacks) { callback->onContextDestroyed(); } - GpuMemoryTracker::onGpuContextDestroyed(); } void RenderState::postDecStrong(VirtualLightRefBase* object) { diff --git a/libs/hwui/renderstate/RenderState.h b/libs/hwui/renderstate/RenderState.h index ff5d02fe359a..e08d32a7735c 100644 --- a/libs/hwui/renderstate/RenderState.h +++ b/libs/hwui/renderstate/RenderState.h @@ -62,7 +62,6 @@ private: ~RenderState() {} // Context notifications are only to be triggered by renderthread::RenderThread - void onContextCreated(); void onContextDestroyed(); std::set<IGpuContextCallback*> mContextCallbacks; diff --git a/libs/hwui/renderthread/CacheManager.cpp b/libs/hwui/renderthread/CacheManager.cpp index eaed46c44e5d..d177855e5a7d 100644 --- a/libs/hwui/renderthread/CacheManager.cpp +++ b/libs/hwui/renderthread/CacheManager.cpp @@ -20,10 +20,12 @@ #include "Layer.h" #include "Properties.h" #include "RenderThread.h" +#include "pipeline/skia/ATraceMemoryDump.h" #include "pipeline/skia/ShaderCache.h" #include "pipeline/skia/SkiaMemoryTracer.h" #include "renderstate/RenderState.h" #include "thread/CommonPool.h" +#include <utils/Trace.h> #include <GrContextOptions.h> #include <SkExecutor.h> @@ -184,6 +186,18 @@ void CacheManager::dumpMemoryUsage(String8& log, const RenderState* renderState) gpuTracer.logTotals(log); } +void CacheManager::onFrameCompleted() { + if (ATRACE_ENABLED()) { + static skiapipeline::ATraceMemoryDump tracer; + tracer.startFrame(); + SkGraphics::DumpMemoryStatistics(&tracer); + if (mGrContext) { + mGrContext->dumpMemoryStatistics(&tracer); + } + tracer.logTraces(); + } +} + } /* namespace renderthread */ } /* namespace uirenderer */ } /* namespace android */ diff --git a/libs/hwui/renderthread/CacheManager.h b/libs/hwui/renderthread/CacheManager.h index 968251e9f467..b009cc4f48f2 100644 --- a/libs/hwui/renderthread/CacheManager.h +++ b/libs/hwui/renderthread/CacheManager.h @@ -50,6 +50,7 @@ public: size_t getCacheSize() const { return mMaxResourceBytes; } size_t getBackgroundCacheSize() const { return mBackgroundResourceBytes; } + void onFrameCompleted(); private: friend class RenderThread; diff --git a/libs/hwui/renderthread/CanvasContext.cpp b/libs/hwui/renderthread/CanvasContext.cpp index 84902210a751..5993e176f0b8 100644 --- a/libs/hwui/renderthread/CanvasContext.cpp +++ b/libs/hwui/renderthread/CanvasContext.cpp @@ -16,7 +16,6 @@ #include "CanvasContext.h" -#include <GpuMemoryTracker.h> #include <apex/window.h> #include <fcntl.h> #include <strings.h> @@ -558,7 +557,7 @@ void CanvasContext::draw() { mJankTracker.finishGpuDraw(*forthBehind); } - GpuMemoryTracker::onFrameCompleted(); + mRenderThread.cacheManager().onFrameCompleted(); } // Called by choreographer to do an RT-driven animation diff --git a/libs/hwui/renderthread/RenderThread.cpp b/libs/hwui/renderthread/RenderThread.cpp index d78f641d45b9..cae3e3b5188c 100644 --- a/libs/hwui/renderthread/RenderThread.cpp +++ b/libs/hwui/renderthread/RenderThread.cpp @@ -270,7 +270,6 @@ void RenderThread::setGrContext(sk_sp<GrContext> context) { } mGrContext = std::move(context); if (mGrContext) { - mRenderState->onContextCreated(); DeviceInfo::setMaxTextureSize(mGrContext->maxRenderTargetSize()); } } diff --git a/libs/hwui/tests/unit/GpuMemoryTrackerTests.cpp b/libs/hwui/tests/unit/GpuMemoryTrackerTests.cpp deleted file mode 100644 index dac888cd79ca..000000000000 --- a/libs/hwui/tests/unit/GpuMemoryTrackerTests.cpp +++ /dev/null @@ -1,65 +0,0 @@ -/* - * Copyright (C) 2016 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. - */ - -#include <GpuMemoryTracker.h> -#include <gtest/gtest.h> - -#include "renderthread/EglManager.h" -#include "renderthread/RenderThread.h" -#include "tests/common/TestUtils.h" - -#include <utils/StrongPointer.h> - -using namespace android; -using namespace android::uirenderer; -using namespace android::uirenderer::renderthread; - -class TestGPUObject : public GpuMemoryTracker { -public: - TestGPUObject() : GpuMemoryTracker(GpuObjectType::Texture) {} - - void changeSize(int newSize) { notifySizeChanged(newSize); } -}; - -// Other tests may have created a renderthread and EGL context. -// This will destroy the EGLContext on RenderThread if it exists so that the -// current thread can spoof being a GPU thread -static void destroyEglContext() { - if (TestUtils::isRenderThreadRunning()) { - TestUtils::runOnRenderThread([](RenderThread& thread) { thread.destroyRenderingContext(); }); - } -} - -TEST(GpuMemoryTracker, sizeCheck) { - destroyEglContext(); - - GpuMemoryTracker::onGpuContextCreated(); - ASSERT_EQ(0, GpuMemoryTracker::getTotalSize(GpuObjectType::Texture)); - ASSERT_EQ(0, GpuMemoryTracker::getInstanceCount(GpuObjectType::Texture)); - { - TestGPUObject myObj; - ASSERT_EQ(1, GpuMemoryTracker::getInstanceCount(GpuObjectType::Texture)); - myObj.changeSize(500); - ASSERT_EQ(500, GpuMemoryTracker::getTotalSize(GpuObjectType::Texture)); - myObj.changeSize(1000); - ASSERT_EQ(1000, GpuMemoryTracker::getTotalSize(GpuObjectType::Texture)); - myObj.changeSize(300); - ASSERT_EQ(300, GpuMemoryTracker::getTotalSize(GpuObjectType::Texture)); - } - ASSERT_EQ(0, GpuMemoryTracker::getTotalSize(GpuObjectType::Texture)); - ASSERT_EQ(0, GpuMemoryTracker::getInstanceCount(GpuObjectType::Texture)); - GpuMemoryTracker::onGpuContextDestroyed(); -} diff --git a/media/java/android/media/soundtrigger_middleware/Status.aidl b/media/java/android/media/soundtrigger_middleware/Status.aidl index d8f9d8f7e891..5d082e272295 100644 --- a/media/java/android/media/soundtrigger_middleware/Status.aidl +++ b/media/java/android/media/soundtrigger_middleware/Status.aidl @@ -26,4 +26,6 @@ enum Status { RESOURCE_CONTENTION = 1, /** Operation is not supported in this implementation. This is a permanent condition. */ OPERATION_NOT_SUPPORTED = 2, + /** Temporary lack of permission. */ + TEMPORARY_PERMISSION_DENIED = 3, } diff --git a/media/java/android/media/tv/tuner/FrontendSettings.java b/media/java/android/media/tv/tuner/FrontendSettings.java index e2e9910f53ee..ad8422caaa02 100644 --- a/media/java/android/media/tv/tuner/FrontendSettings.java +++ b/media/java/android/media/tv/tuner/FrontendSettings.java @@ -17,7 +17,6 @@ package android.media.tv.tuner; import android.annotation.SystemApi; -import android.media.tv.tuner.TunerConstants.FrontendSettingsType; /** * Frontend settings for tune and scan operations. @@ -35,7 +34,6 @@ public abstract class FrontendSettings { /** * Returns the frontend type. */ - @FrontendSettingsType public abstract int getType(); /** diff --git a/media/java/android/media/tv/tuner/Lnb.java b/media/java/android/media/tv/tuner/Lnb.java new file mode 100644 index 000000000000..f181b49239d7 --- /dev/null +++ b/media/java/android/media/tv/tuner/Lnb.java @@ -0,0 +1,202 @@ +/* + * Copyright 2019 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package android.media.tv.tuner; + +import android.annotation.IntDef; +import android.annotation.NonNull; +import android.annotation.Nullable; +import android.annotation.RequiresPermission; +import android.content.Context; +import android.hardware.tv.tuner.V1_0.Constants; +import android.media.tv.tuner.Tuner.LnbCallback; +import android.media.tv.tuner.TunerConstants.Result; + +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; + +/** + * LNB (low-noise block downconverter) for satellite tuner. + * + * A Tuner LNB (low-noise block downconverter) is used by satellite frontend to receive the + * microwave signal from the satellite, amplify it, and downconvert the frequency to a lower + * frequency. + * + * @hide + */ +public class Lnb implements AutoCloseable { + /** @hide */ + @IntDef({VOLTAGE_NONE, VOLTAGE_5V, VOLTAGE_11V, VOLTAGE_12V, VOLTAGE_13V, VOLTAGE_14V, + VOLTAGE_15V, VOLTAGE_18V, VOLTAGE_19V}) + @Retention(RetentionPolicy.SOURCE) + public @interface Voltage {} + + /** + * LNB power voltage not set. + */ + public static final int VOLTAGE_NONE = Constants.LnbVoltage.NONE; + /** + * LNB power voltage 5V. + */ + public static final int VOLTAGE_5V = Constants.LnbVoltage.VOLTAGE_5V; + /** + * LNB power voltage 11V. + */ + public static final int VOLTAGE_11V = Constants.LnbVoltage.VOLTAGE_11V; + /** + * LNB power voltage 12V. + */ + public static final int VOLTAGE_12V = Constants.LnbVoltage.VOLTAGE_12V; + /** + * LNB power voltage 13V. + */ + public static final int VOLTAGE_13V = Constants.LnbVoltage.VOLTAGE_13V; + /** + * LNB power voltage 14V. + */ + public static final int VOLTAGE_14V = Constants.LnbVoltage.VOLTAGE_14V; + /** + * LNB power voltage 15V. + */ + public static final int VOLTAGE_15V = Constants.LnbVoltage.VOLTAGE_15V; + /** + * LNB power voltage 18V. + */ + public static final int VOLTAGE_18V = Constants.LnbVoltage.VOLTAGE_18V; + /** + * LNB power voltage 19V. + */ + public static final int VOLTAGE_19V = Constants.LnbVoltage.VOLTAGE_19V; + + /** @hide */ + @IntDef({TONE_NONE, TONE_CONTINUOUS}) + @Retention(RetentionPolicy.SOURCE) + public @interface Tone {} + + /** + * LNB tone mode not set. + */ + public static final int TONE_NONE = Constants.LnbTone.NONE; + /** + * LNB continuous tone mode. + */ + public static final int TONE_CONTINUOUS = Constants.LnbTone.CONTINUOUS; + + /** @hide */ + @IntDef({POSITION_UNDEFINED, POSITION_A, POSITION_B}) + @Retention(RetentionPolicy.SOURCE) + public @interface Position {} + + /** + * LNB position is not defined. + */ + public static final int POSITION_UNDEFINED = Constants.LnbPosition.UNDEFINED; + /** + * Position A of two-band LNBs + */ + public static final int POSITION_A = Constants.LnbPosition.POSITION_A; + /** + * Position B of two-band LNBs + */ + public static final int POSITION_B = Constants.LnbPosition.POSITION_B; + + int mId; + LnbCallback mCallback; + Context mContext; + + private native int nativeSetVoltage(int voltage); + private native int nativeSetTone(int tone); + private native int nativeSetSatellitePosition(int position); + private native int nativeSendDiseqcMessage(byte[] message); + private native int nativeClose(); + + Lnb(int id) { + mId = id; + } + + /** @hide */ + public void setCallback(@Nullable LnbCallback callback) { + mCallback = callback; + if (mCallback == null) { + return; + } + } + + /** + * Sets the LNB's power voltage. + * + * @param voltage the power voltage constant the Lnb to use. + * @return result status of the operation. + */ + @RequiresPermission(android.Manifest.permission.ACCESS_TV_TUNER) + @Result + public int setVoltage(@Voltage int voltage) { + TunerUtils.checkTunerPermission(mContext); + return nativeSetVoltage(voltage); + } + + /** + * Sets the LNB's tone mode. + * + * @param tone the tone mode the Lnb to use. + * @return result status of the operation. + */ + @RequiresPermission(android.Manifest.permission.ACCESS_TV_TUNER) + @Result + public int setTone(@Tone int tone) { + TunerUtils.checkTunerPermission(mContext); + return nativeSetTone(tone); + } + + /** + * Selects the LNB's position. + * + * @param position the position the Lnb to use. + * @return result status of the operation. + */ + @RequiresPermission(android.Manifest.permission.ACCESS_TV_TUNER) + @Result + public int setSatellitePosition(@Position int position) { + TunerUtils.checkTunerPermission(mContext); + return nativeSetSatellitePosition(position); + } + + /** + * Sends DiSEqC (Digital Satellite Equipment Control) message. + * + * The response message from the device comes back through callback onDiseqcMessage. + * + * @param message a byte array of data for DiSEqC message which is specified by EUTELSAT Bus + * Functional Specification Version 4.2. + * + * @return result status of the operation. + */ + @RequiresPermission(android.Manifest.permission.ACCESS_TV_TUNER) + @Result + public int sendDiseqcMessage(@NonNull byte[] message) { + TunerUtils.checkTunerPermission(mContext); + return nativeSendDiseqcMessage(message); + } + + /** + * Releases the LNB instance. + */ + @RequiresPermission(android.Manifest.permission.ACCESS_TV_TUNER) + public void close() { + TunerUtils.checkTunerPermission(mContext); + nativeClose(); + } +} diff --git a/media/java/android/media/tv/tuner/Tuner.java b/media/java/android/media/tv/tuner/Tuner.java index 962a7f6d58f6..f8b46b305216 100644 --- a/media/java/android/media/tv/tuner/Tuner.java +++ b/media/java/android/media/tv/tuner/Tuner.java @@ -24,9 +24,6 @@ import android.content.Context; import android.media.tv.tuner.TunerConstants.FilterStatus; import android.media.tv.tuner.TunerConstants.FilterSubtype; import android.media.tv.tuner.TunerConstants.FrontendScanType; -import android.media.tv.tuner.TunerConstants.LnbPosition; -import android.media.tv.tuner.TunerConstants.LnbTone; -import android.media.tv.tuner.TunerConstants.LnbVoltage; import android.media.tv.tuner.TunerConstants.Result; import android.media.tv.tuner.filter.FilterConfiguration.FilterType; import android.media.tv.tuner.filter.FilterEvent; @@ -219,11 +216,6 @@ public final class Tuner implements AutoCloseable { } break; } - case MSG_ON_LNB_EVENT: { - if (mLnb != null && mLnb.mCallback != null) { - mLnb.mCallback.onEvent(msg.arg1); - } - } default: // fall through } @@ -474,102 +466,6 @@ public final class Tuner implements AutoCloseable { return filter; } - /** - * Open a time filter instance. - * - * It is used to open time filter of demux. - * - * @return a time filter instance. - * @hide - */ - public TimeFilter openTimeFilter() { - return nativeOpenTimeFilter(); - } - - /** @hide */ - public class Lnb { - private int mId; - private LnbCallback mCallback; - - private native int nativeSetVoltage(int voltage); - private native int nativeSetTone(int tone); - private native int nativeSetSatellitePosition(int position); - private native int nativeSendDiseqcMessage(byte[] message); - private native int nativeClose(); - - private Lnb(int id) { - mId = id; - } - - public void setCallback(@Nullable LnbCallback callback) { - mCallback = callback; - if (mCallback == null) { - return; - } - if (mHandler == null) { - mHandler = createEventHandler(); - } - } - - /** - * Sets the LNB's power voltage. - * - * @param voltage the power voltage the Lnb to use. - * @return result status of the operation. - */ - @Result - public int setVoltage(@LnbVoltage int voltage) { - return nativeSetVoltage(voltage); - } - - /** - * Sets the LNB's tone mode. - * - * @param tone the tone mode the Lnb to use. - * @return result status of the operation. - */ - @Result - public int setTone(@LnbTone int tone) { - return nativeSetTone(tone); - } - - /** - * Selects the LNB's position. - * - * @param position the position the Lnb to use. - * @return result status of the operation. - */ - @Result - public int setSatellitePosition(@LnbPosition int position) { - return nativeSetSatellitePosition(position); - } - - /** - * Sends DiSEqC (Digital Satellite Equipment Control) message. - * - * The response message from the device comes back through callback onDiseqcMessage. - * - * @param message a byte array of data for DiSEqC message which is specified by EUTELSAT Bus - * Functional Specification Version 4.2. - * - * @return result status of the operation. - */ - @Result - public int sendDiseqcMessage(byte[] message) { - return nativeSendDiseqcMessage(message); - } - - /** - * Releases the LNB instance - * - * @return result status of the operation. - */ - @Result - public int close() { - return nativeClose(); - } - } - private List<Integer> getLnbIds() { mLnbIds = nativeGetLnbIds(); return mLnbIds; diff --git a/media/java/android/media/tv/tuner/TunerConstants.java b/media/java/android/media/tv/tuner/TunerConstants.java index bbaa5180aece..a55c4856245c 100644 --- a/media/java/android/media/tv/tuner/TunerConstants.java +++ b/media/java/android/media/tv/tuner/TunerConstants.java @@ -38,35 +38,6 @@ public final class TunerConstants { /** @hide */ - @IntDef({FRONTEND_TYPE_UNDEFINED, FRONTEND_TYPE_ANALOG, FRONTEND_TYPE_ATSC, FRONTEND_TYPE_ATSC3, - FRONTEND_TYPE_DVBC, FRONTEND_TYPE_DVBS, FRONTEND_TYPE_DVBT, FRONTEND_TYPE_ISDBS, - FRONTEND_TYPE_ISDBS3, FRONTEND_TYPE_ISDBT}) - @Retention(RetentionPolicy.SOURCE) - public @interface FrontendType {} - - /** @hide */ - public static final int FRONTEND_TYPE_UNDEFINED = Constants.FrontendType.UNDEFINED; - /** @hide */ - public static final int FRONTEND_TYPE_ANALOG = Constants.FrontendType.ANALOG; - /** @hide */ - public static final int FRONTEND_TYPE_ATSC = Constants.FrontendType.ATSC; - /** @hide */ - public static final int FRONTEND_TYPE_ATSC3 = Constants.FrontendType.ATSC3; - /** @hide */ - public static final int FRONTEND_TYPE_DVBC = Constants.FrontendType.DVBC; - /** @hide */ - public static final int FRONTEND_TYPE_DVBS = Constants.FrontendType.DVBS; - /** @hide */ - public static final int FRONTEND_TYPE_DVBT = Constants.FrontendType.DVBT; - /** @hide */ - public static final int FRONTEND_TYPE_ISDBS = Constants.FrontendType.ISDBS; - /** @hide */ - public static final int FRONTEND_TYPE_ISDBS3 = Constants.FrontendType.ISDBS3; - /** @hide */ - public static final int FRONTEND_TYPE_ISDBT = Constants.FrontendType.ISDBT; - - - /** @hide */ @IntDef({FRONTEND_EVENT_TYPE_LOCKED, FRONTEND_EVENT_TYPE_NO_SIGNAL, FRONTEND_EVENT_TYPE_LOST_LOCK}) @Retention(RetentionPolicy.SOURCE) @@ -102,30 +73,6 @@ public final class TunerConstants { /** @hide */ public static final int DEMUX_MMPT_PID = 2; - /** @hide */ - @IntDef({FRONTEND_SETTINGS_ANALOG, FRONTEND_SETTINGS_ATSC, FRONTEND_SETTINGS_ATSC3, - FRONTEND_SETTINGS_DVBS, FRONTEND_SETTINGS_DVBC, FRONTEND_SETTINGS_DVBT, - FRONTEND_SETTINGS_ISDBS, FRONTEND_SETTINGS_ISDBS3, FRONTEND_SETTINGS_ISDBT}) - @Retention(RetentionPolicy.SOURCE) - public @interface FrontendSettingsType {} - /** @hide */ - public static final int FRONTEND_SETTINGS_ANALOG = 1; - /** @hide */ - public static final int FRONTEND_SETTINGS_ATSC = 2; - /** @hide */ - public static final int FRONTEND_SETTINGS_ATSC3 = 3; - /** @hide */ - public static final int FRONTEND_SETTINGS_DVBS = 4; - /** @hide */ - public static final int FRONTEND_SETTINGS_DVBC = 5; - /** @hide */ - public static final int FRONTEND_SETTINGS_DVBT = 6; - /** @hide */ - public static final int FRONTEND_SETTINGS_ISDBS = 7; - /** @hide */ - public static final int FRONTEND_SETTINGS_ISDBS3 = 8; - /** @hide */ - public static final int FRONTEND_SETTINGS_ISDBT = 9; /** @hide */ @IntDef({FILTER_SUBTYPE_UNDEFINED, FILTER_SUBTYPE_SECTION, FILTER_SUBTYPE_PES, @@ -727,73 +674,6 @@ public final class TunerConstants { Constants.FrontendDvbtHierarchy.HIERARCHY_4_INDEPTH; /** @hide */ - @IntDef({FRONTEND_ANALOG_TYPE_UNDEFINED, FRONTEND_ANALOG_TYPE_PAL, FRONTEND_ANALOG_TYPE_SECAM, - FRONTEND_ANALOG_TYPE_NTSC}) - @Retention(RetentionPolicy.SOURCE) - public @interface FrontendAnalogType {} - /** @hide */ - public static final int FRONTEND_ANALOG_TYPE_UNDEFINED = Constants.FrontendAnalogType.UNDEFINED; - /** @hide */ - public static final int FRONTEND_ANALOG_TYPE_PAL = Constants.FrontendAnalogType.PAL; - /** @hide */ - public static final int FRONTEND_ANALOG_TYPE_SECAM = Constants.FrontendAnalogType.SECAM; - /** @hide */ - public static final int FRONTEND_ANALOG_TYPE_NTSC = Constants.FrontendAnalogType.NTSC; - - /** @hide */ - @IntDef({FRONTEND_ANALOG_SIF_UNDEFINED, FRONTEND_ANALOG_SIF_BG, FRONTEND_ANALOG_SIF_BG_A2, - FRONTEND_ANALOG_SIF_BG_NICAM, FRONTEND_ANALOG_SIF_I, FRONTEND_ANALOG_SIF_DK, - FRONTEND_ANALOG_SIF_DK1, FRONTEND_ANALOG_SIF_DK2, FRONTEND_ANALOG_SIF_DK3, - FRONTEND_ANALOG_SIF_DK_NICAM, FRONTEND_ANALOG_SIF_L, FRONTEND_ANALOG_SIF_M, - FRONTEND_ANALOG_SIF_M_BTSC, FRONTEND_ANALOG_SIF_M_A2, FRONTEND_ANALOG_SIF_M_EIA_J, - FRONTEND_ANALOG_SIF_I_NICAM, FRONTEND_ANALOG_SIF_L_NICAM, FRONTEND_ANALOG_SIF_L_PRIME}) - @Retention(RetentionPolicy.SOURCE) - public @interface FrontendAnalogSifStandard {} - /** @hide */ - public static final int FRONTEND_ANALOG_SIF_UNDEFINED = - Constants.FrontendAnalogSifStandard.UNDEFINED; - /** @hide */ - public static final int FRONTEND_ANALOG_SIF_BG = Constants.FrontendAnalogSifStandard.BG; - /** @hide */ - public static final int FRONTEND_ANALOG_SIF_BG_A2 = Constants.FrontendAnalogSifStandard.BG_A2; - /** @hide */ - public static final int FRONTEND_ANALOG_SIF_BG_NICAM = - Constants.FrontendAnalogSifStandard.BG_NICAM; - /** @hide */ - public static final int FRONTEND_ANALOG_SIF_I = Constants.FrontendAnalogSifStandard.I; - /** @hide */ - public static final int FRONTEND_ANALOG_SIF_DK = Constants.FrontendAnalogSifStandard.DK; - /** @hide */ - public static final int FRONTEND_ANALOG_SIF_DK1 = Constants.FrontendAnalogSifStandard.DK1; - /** @hide */ - public static final int FRONTEND_ANALOG_SIF_DK2 = Constants.FrontendAnalogSifStandard.DK2; - /** @hide */ - public static final int FRONTEND_ANALOG_SIF_DK3 = Constants.FrontendAnalogSifStandard.DK3; - /** @hide */ - public static final int FRONTEND_ANALOG_SIF_DK_NICAM = - Constants.FrontendAnalogSifStandard.DK_NICAM; - /** @hide */ - public static final int FRONTEND_ANALOG_SIF_L = Constants.FrontendAnalogSifStandard.L; - /** @hide */ - public static final int FRONTEND_ANALOG_SIF_M = Constants.FrontendAnalogSifStandard.M; - /** @hide */ - public static final int FRONTEND_ANALOG_SIF_M_BTSC = Constants.FrontendAnalogSifStandard.M_BTSC; - /** @hide */ - public static final int FRONTEND_ANALOG_SIF_M_A2 = Constants.FrontendAnalogSifStandard.M_A2; - /** @hide */ - public static final int FRONTEND_ANALOG_SIF_M_EIA_J = - Constants.FrontendAnalogSifStandard.M_EIA_J; - /** @hide */ - public static final int FRONTEND_ANALOG_SIF_I_NICAM = - Constants.FrontendAnalogSifStandard.I_NICAM; - /** @hide */ - public static final int FRONTEND_ANALOG_SIF_L_NICAM = - Constants.FrontendAnalogSifStandard.L_NICAM; - /** @hide */ - public static final int FRONTEND_ANALOG_SIF_L_PRIME = - Constants.FrontendAnalogSifStandard.L_PRIME; - - /** @hide */ @IntDef({FRONTEND_ATSC_MODULATION_UNDEFINED, FRONTEND_ATSC_MODULATION_AUTO, FRONTEND_ATSC_MODULATION_MOD_8VSB, FRONTEND_ATSC_MODULATION_MOD_16VSB}) @Retention(RetentionPolicy.SOURCE) @@ -1263,68 +1143,45 @@ public final class TunerConstants { /** @hide */ - @IntDef({LNB_VOLTAGE_NONE, LNB_VOLTAGE_5V, LNB_VOLTAGE_11V, LNB_VOLTAGE_12V, LNB_VOLTAGE_13V, - LNB_VOLTAGE_14V, LNB_VOLTAGE_15V, LNB_VOLTAGE_18V, LNB_VOLTAGE_19V}) - @Retention(RetentionPolicy.SOURCE) - public @interface LnbVoltage {} - /** @hide */ - public static final int LNB_VOLTAGE_NONE = Constants.LnbVoltage.NONE; - /** @hide */ - public static final int LNB_VOLTAGE_5V = Constants.LnbVoltage.VOLTAGE_5V; - /** @hide */ - public static final int LNB_VOLTAGE_11V = Constants.LnbVoltage.VOLTAGE_11V; - /** @hide */ - public static final int LNB_VOLTAGE_12V = Constants.LnbVoltage.VOLTAGE_12V; - /** @hide */ - public static final int LNB_VOLTAGE_13V = Constants.LnbVoltage.VOLTAGE_13V; - /** @hide */ - public static final int LNB_VOLTAGE_14V = Constants.LnbVoltage.VOLTAGE_14V; - /** @hide */ - public static final int LNB_VOLTAGE_15V = Constants.LnbVoltage.VOLTAGE_15V; - /** @hide */ - public static final int LNB_VOLTAGE_18V = Constants.LnbVoltage.VOLTAGE_18V; - /** @hide */ - public static final int LNB_VOLTAGE_19V = Constants.LnbVoltage.VOLTAGE_19V; - - /** @hide */ - @IntDef({LNB_TONE_NONE, LNB_TONE_CONTINUOUS}) - @Retention(RetentionPolicy.SOURCE) - public @interface LnbTone {} - /** @hide */ - public static final int LNB_TONE_NONE = Constants.LnbTone.NONE; - /** @hide */ - public static final int LNB_TONE_CONTINUOUS = Constants.LnbTone.CONTINUOUS; - - /** @hide */ - @IntDef({LNB_POSITION_UNDEFINED, LNB_POSITION_A, LNB_POSITION_B}) - @Retention(RetentionPolicy.SOURCE) - public @interface LnbPosition {} - /** @hide */ - public static final int LNB_POSITION_UNDEFINED = Constants.LnbPosition.UNDEFINED; - /** @hide */ - public static final int LNB_POSITION_A = Constants.LnbPosition.POSITION_A; - /** @hide */ - public static final int LNB_POSITION_B = Constants.LnbPosition.POSITION_B; - - - /** @hide */ @IntDef({RESULT_SUCCESS, RESULT_UNAVAILABLE, RESULT_NOT_INITIALIZED, RESULT_INVALID_STATE, RESULT_INVALID_ARGUMENT, RESULT_OUT_OF_MEMORY, RESULT_UNKNOWN_ERROR}) @Retention(RetentionPolicy.SOURCE) public @interface Result {} - /** @hide */ + + /** + * Operation succeeded. + * @hide + */ public static final int RESULT_SUCCESS = Constants.Result.SUCCESS; - /** @hide */ + /** + * Operation failed because the corresponding resources are not available. + * @hide + */ public static final int RESULT_UNAVAILABLE = Constants.Result.UNAVAILABLE; - /** @hide */ + /** + * Operation failed because the corresponding resources are not initialized. + * @hide + */ public static final int RESULT_NOT_INITIALIZED = Constants.Result.NOT_INITIALIZED; - /** @hide */ + /** + * Operation failed because it's not in a valid state. + * @hide + */ public static final int RESULT_INVALID_STATE = Constants.Result.INVALID_STATE; - /** @hide */ + /** + * Operation failed because there are invalid arguments. + * @hide + */ public static final int RESULT_INVALID_ARGUMENT = Constants.Result.INVALID_ARGUMENT; - /** @hide */ + /** + * Memory allocation failed. + * @hide + */ public static final int RESULT_OUT_OF_MEMORY = Constants.Result.OUT_OF_MEMORY; - /** @hide */ + /** + * Operation failed due to unknown errors. + * @hide + */ public static final int RESULT_UNKNOWN_ERROR = Constants.Result.UNKNOWN_ERROR; private TunerConstants() { diff --git a/media/java/android/media/tv/tuner/frontend/AnalogFrontendSettings.java b/media/java/android/media/tv/tuner/frontend/AnalogFrontendSettings.java index 16308ced2300..aec8ce895ab8 100644 --- a/media/java/android/media/tv/tuner/frontend/AnalogFrontendSettings.java +++ b/media/java/android/media/tv/tuner/frontend/AnalogFrontendSettings.java @@ -16,33 +16,153 @@ package android.media.tv.tuner.frontend; -import android.media.tv.tuner.FrontendSettings; -import android.media.tv.tuner.TunerConstants; +import android.annotation.IntDef; +import android.annotation.NonNull; +import android.hardware.tv.tuner.V1_0.Constants; + +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; /** - * Frontend settings for analog. + * Frontend settings for analog tuner. + * * @hide */ public class AnalogFrontendSettings extends FrontendSettings { - private int mAnalogType; - private int mSifStandard; + /** @hide */ + @IntDef(flag = true, value = {SIGNAL_TYPE_UNDEFINED, SIGNAL_TYPE_PAL, SIGNAL_TYPE_SECAM, + SIGNAL_TYPE_NTSC}) + @Retention(RetentionPolicy.SOURCE) + public @interface SignalType {} + + /** + * Undefined analog signal type. + */ + public static final int SIGNAL_TYPE_UNDEFINED = Constants.FrontendAnalogType.UNDEFINED; + /** + * PAL analog signal type. + */ + public static final int SIGNAL_TYPE_PAL = Constants.FrontendAnalogType.PAL; + /** + * SECM analog signal type. + */ + public static final int SIGNAL_TYPE_SECAM = Constants.FrontendAnalogType.SECAM; + /** + * NTSC analog signal type. + */ + public static final int SIGNAL_TYPE_NTSC = Constants.FrontendAnalogType.NTSC; + + + /** @hide */ + @IntDef(flag = true, value = {SIF_UNDEFINED, SIF_BG, SIF_BG_A2, SIF_BG_NICAM, SIF_I, SIF_DK, + SIF_DK1, SIF_DK2, SIF_DK3, SIF_DK_NICAM, SIF_L, SIF_M, SIF_M_BTSC, SIF_M_A2, + SIF_M_EIA_J, SIF_I_NICAM, SIF_L_NICAM, SIF_L_PRIME}) + @Retention(RetentionPolicy.SOURCE) + public @interface SifStandard {} + + /** + * Undefined Analog Standard Interchange Format (SIF). + */ + public static final int SIF_UNDEFINED = Constants.FrontendAnalogSifStandard.UNDEFINED; + /** + * BG Analog Standard Interchange Format (SIF). + */ + public static final int SIF_BG = Constants.FrontendAnalogSifStandard.BG; + /** + * BG-A2 Analog Standard Interchange Format (SIF). + */ + public static final int SIF_BG_A2 = Constants.FrontendAnalogSifStandard.BG_A2; + /** + * BG-NICAM Analog Standard Interchange Format (SIF). + */ + public static final int SIF_BG_NICAM = Constants.FrontendAnalogSifStandard.BG_NICAM; + /** + * I Analog Standard Interchange Format (SIF). + */ + public static final int SIF_I = Constants.FrontendAnalogSifStandard.I; + /** + * DK Analog Standard Interchange Format (SIF). + */ + public static final int SIF_DK = Constants.FrontendAnalogSifStandard.DK; + /** + * DK1 Analog Standard Interchange Format (SIF). + */ + public static final int SIF_DK1 = Constants.FrontendAnalogSifStandard.DK1; + /** + * DK2 Analog Standard Interchange Format (SIF). + */ + public static final int SIF_DK2 = Constants.FrontendAnalogSifStandard.DK2; + /** + * DK3 Analog Standard Interchange Format (SIF). + */ + public static final int SIF_DK3 = Constants.FrontendAnalogSifStandard.DK3; + /** + * DK-NICAM Analog Standard Interchange Format (SIF). + */ + public static final int SIF_DK_NICAM = Constants.FrontendAnalogSifStandard.DK_NICAM; + /** + * L Analog Standard Interchange Format (SIF). + */ + public static final int SIF_L = Constants.FrontendAnalogSifStandard.L; + /** + * M Analog Standard Interchange Format (SIF). + */ + public static final int SIF_M = Constants.FrontendAnalogSifStandard.M; + /** + * M-BTSC Analog Standard Interchange Format (SIF). + */ + public static final int SIF_M_BTSC = Constants.FrontendAnalogSifStandard.M_BTSC; + /** + * M-A2 Analog Standard Interchange Format (SIF). + */ + public static final int SIF_M_A2 = Constants.FrontendAnalogSifStandard.M_A2; + /** + * M-EIA-J Analog Standard Interchange Format (SIF). + */ + public static final int SIF_M_EIA_J = Constants.FrontendAnalogSifStandard.M_EIA_J; + /** + * I-NICAM Analog Standard Interchange Format (SIF). + */ + public static final int SIF_I_NICAM = Constants.FrontendAnalogSifStandard.I_NICAM; + /** + * L-NICAM Analog Standard Interchange Format (SIF). + */ + public static final int SIF_L_NICAM = Constants.FrontendAnalogSifStandard.L_NICAM; + /** + * L-PRIME Analog Standard Interchange Format (SIF). + */ + public static final int SIF_L_PRIME = Constants.FrontendAnalogSifStandard.L_PRIME; + + + private final int mAnalogType; + private final int mSifStandard; @Override public int getType() { - return TunerConstants.FRONTEND_TYPE_ANALOG; + return FrontendSettings.TYPE_ANALOG; } + + /** + * Gets analog signal type. + */ + @SignalType public int getAnalogType() { return mAnalogType; } + /** + * Gets Standard Interchange Format (SIF). + */ + @SifStandard public int getSifStandard() { return mSifStandard; } /** - * Creates a new builder object. + * Creates a builder for {@link AnalogFrontendSettings}. */ + @NonNull public static Builder newBuilder() { return new Builder(); } @@ -54,7 +174,7 @@ public class AnalogFrontendSettings extends FrontendSettings { } /** - * Builder for FrontendAnalogSettings. + * Builder for {@link AnalogFrontendSettings}. */ public static class Builder { private int mFrequency; @@ -64,8 +184,9 @@ public class AnalogFrontendSettings extends FrontendSettings { private Builder() {} /** - * Sets frequency. + * Sets frequency in Hz. */ + @NonNull public Builder setFrequency(int frequency) { mFrequency = frequency; return this; @@ -74,22 +195,25 @@ public class AnalogFrontendSettings extends FrontendSettings { /** * Sets analog type. */ - public Builder setAnalogType(int analogType) { + @NonNull + public Builder setAnalogType(@SignalType int analogType) { mAnalogType = analogType; return this; } /** - * Sets sif standard. + * Sets Standard Interchange Format (SIF). */ - public Builder setSifStandard(int sifStandard) { + @NonNull + public Builder setSifStandard(@SifStandard int sifStandard) { mSifStandard = sifStandard; return this; } /** - * Builds a FrontendAnalogSettings instance. + * Builds a {@link AnalogFrontendSettings} object. */ + @NonNull public AnalogFrontendSettings build() { return new AnalogFrontendSettings(mFrequency, mAnalogType, mSifStandard); } diff --git a/media/java/android/media/tv/tuner/frontend/Atsc3FrontendSettings.java b/media/java/android/media/tv/tuner/frontend/Atsc3FrontendSettings.java index bce8a640c7f2..5b09e36ea113 100644 --- a/media/java/android/media/tv/tuner/frontend/Atsc3FrontendSettings.java +++ b/media/java/android/media/tv/tuner/frontend/Atsc3FrontendSettings.java @@ -16,10 +16,6 @@ package android.media.tv.tuner.frontend; - -import android.media.tv.tuner.FrontendSettings; -import android.media.tv.tuner.TunerConstants; - import java.util.List; /** @@ -37,6 +33,6 @@ public class Atsc3FrontendSettings extends FrontendSettings { @Override public int getType() { - return TunerConstants.FRONTEND_TYPE_ATSC3; + return FrontendSettings.TYPE_ATSC3; } } diff --git a/media/java/android/media/tv/tuner/frontend/AtscFrontendSettings.java b/media/java/android/media/tv/tuner/frontend/AtscFrontendSettings.java index 14c5cdd112e1..19e18d017e67 100644 --- a/media/java/android/media/tv/tuner/frontend/AtscFrontendSettings.java +++ b/media/java/android/media/tv/tuner/frontend/AtscFrontendSettings.java @@ -16,9 +16,6 @@ package android.media.tv.tuner.frontend; -import android.media.tv.tuner.FrontendSettings; -import android.media.tv.tuner.TunerConstants; - /** * Frontend settings for ATSC. * @hide @@ -32,6 +29,6 @@ public class AtscFrontendSettings extends FrontendSettings { @Override public int getType() { - return TunerConstants.FRONTEND_TYPE_ATSC; + return FrontendSettings.TYPE_ATSC; } } diff --git a/media/java/android/media/tv/tuner/frontend/DvbcFrontendSettings.java b/media/java/android/media/tv/tuner/frontend/DvbcFrontendSettings.java index 07e49ff24de1..60618f6f6896 100644 --- a/media/java/android/media/tv/tuner/frontend/DvbcFrontendSettings.java +++ b/media/java/android/media/tv/tuner/frontend/DvbcFrontendSettings.java @@ -16,9 +16,6 @@ package android.media.tv.tuner.frontend; -import android.media.tv.tuner.FrontendSettings; -import android.media.tv.tuner.TunerConstants; - /** * Frontend settings for DVBC. * @hide @@ -37,6 +34,6 @@ public class DvbcFrontendSettings extends FrontendSettings { @Override public int getType() { - return TunerConstants.FRONTEND_TYPE_DVBC; + return FrontendSettings.TYPE_DVBC; } } diff --git a/media/java/android/media/tv/tuner/frontend/DvbsFrontendSettings.java b/media/java/android/media/tv/tuner/frontend/DvbsFrontendSettings.java index 23c0a7b15e52..586787f9eb73 100644 --- a/media/java/android/media/tv/tuner/frontend/DvbsFrontendSettings.java +++ b/media/java/android/media/tv/tuner/frontend/DvbsFrontendSettings.java @@ -16,9 +16,6 @@ package android.media.tv.tuner.frontend; -import android.media.tv.tuner.FrontendSettings; -import android.media.tv.tuner.TunerConstants; - /** * Frontend settings for DVBS. * @hide @@ -38,6 +35,6 @@ public class DvbsFrontendSettings extends FrontendSettings { @Override public int getType() { - return TunerConstants.FRONTEND_TYPE_DVBS; + return FrontendSettings.TYPE_DVBS; } } diff --git a/media/java/android/media/tv/tuner/frontend/DvbtFrontendSettings.java b/media/java/android/media/tv/tuner/frontend/DvbtFrontendSettings.java index eec00f3fab80..6b350a7865a2 100644 --- a/media/java/android/media/tv/tuner/frontend/DvbtFrontendSettings.java +++ b/media/java/android/media/tv/tuner/frontend/DvbtFrontendSettings.java @@ -16,10 +16,6 @@ package android.media.tv.tuner.frontend; - -import android.media.tv.tuner.FrontendSettings; -import android.media.tv.tuner.TunerConstants; - /** * Frontend settings for DVBT. * @hide @@ -45,6 +41,6 @@ public class DvbtFrontendSettings extends FrontendSettings { @Override public int getType() { - return TunerConstants.FRONTEND_TYPE_DVBT; + return FrontendSettings.TYPE_DVBT; } } diff --git a/media/java/android/media/tv/tuner/frontend/FrontendInfo.java b/media/java/android/media/tv/tuner/frontend/FrontendInfo.java index 5d03570eea80..99e8dd2fe31f 100644 --- a/media/java/android/media/tv/tuner/frontend/FrontendInfo.java +++ b/media/java/android/media/tv/tuner/frontend/FrontendInfo.java @@ -16,7 +16,7 @@ package android.media.tv.tuner.frontend; -import android.media.tv.tuner.TunerConstants.FrontendType; +import android.media.tv.tuner.frontend.FrontendSettings.Type; /** * Frontend info. @@ -54,7 +54,7 @@ public class FrontendInfo { return mId; } /** Gets frontend type. */ - @FrontendType + @Type public int getType() { return mType; } diff --git a/media/java/android/media/tv/tuner/frontend/FrontendSettings.java b/media/java/android/media/tv/tuner/frontend/FrontendSettings.java new file mode 100644 index 000000000000..210aef4163e5 --- /dev/null +++ b/media/java/android/media/tv/tuner/frontend/FrontendSettings.java @@ -0,0 +1,100 @@ +/* + * Copyright 2019 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package android.media.tv.tuner.frontend; + +import android.annotation.IntDef; +import android.hardware.tv.tuner.V1_0.Constants; + +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; + +/** + * Frontend settings for tune and scan operations. + * + * @hide + */ +public abstract class FrontendSettings { + /** @hide */ + @IntDef({TYPE_UNDEFINED, TYPE_ANALOG, TYPE_ATSC, TYPE_ATSC3, TYPE_DVBC, TYPE_DVBS, TYPE_DVBT, + TYPE_ISDBS, TYPE_ISDBS3, TYPE_ISDBT}) + @Retention(RetentionPolicy.SOURCE) + public @interface Type {} + + /** + * Undefined frontend type. + */ + public static final int TYPE_UNDEFINED = Constants.FrontendType.UNDEFINED; + /** + * Analog frontend type. + */ + public static final int TYPE_ANALOG = Constants.FrontendType.ANALOG; + /** + * Advanced Television Systems Committee (ATSC) frontend type. + */ + public static final int TYPE_ATSC = Constants.FrontendType.ATSC; + /** + * Advanced Television Systems Committee 3.0 (ATSC-3) frontend type. + */ + public static final int TYPE_ATSC3 = Constants.FrontendType.ATSC3; + /** + * Digital Video Broadcasting-Cable (DVB-C) frontend type. + */ + public static final int TYPE_DVBC = Constants.FrontendType.DVBC; + /** + * Digital Video Broadcasting-Satellite (DVB-S) frontend type. + */ + public static final int TYPE_DVBS = Constants.FrontendType.DVBS; + /** + * Digital Video Broadcasting-Terrestrial (DVB-T) frontend type. + */ + public static final int TYPE_DVBT = Constants.FrontendType.DVBT; + /** + * Integrated Services Digital Broadcasting-Satellite (ISDB-S) frontend type. + */ + public static final int TYPE_ISDBS = Constants.FrontendType.ISDBS; + /** + * Integrated Services Digital Broadcasting-Satellite 3 (ISDB-S3) frontend type. + */ + public static final int TYPE_ISDBS3 = Constants.FrontendType.ISDBS3; + /** + * Integrated Services Digital Broadcasting-Terrestrial (ISDB-T) frontend type. + */ + public static final int TYPE_ISDBT = Constants.FrontendType.ISDBT; + + private final int mFrequency; + + /** @hide */ + public FrontendSettings(int frequency) { + mFrequency = frequency; + } + + /** + * Returns the frontend type. + */ + @Type + public abstract int getType(); + + /** + * Gets the frequency. + * + * @return the frequency in Hz. + */ + public int getFrequency() { + return mFrequency; + } + +} diff --git a/media/java/android/media/tv/tuner/frontend/FrontendStatus.java b/media/java/android/media/tv/tuner/frontend/FrontendStatus.java index 89ec536a1d6c..fb5d62afd2f2 100644 --- a/media/java/android/media/tv/tuner/frontend/FrontendStatus.java +++ b/media/java/android/media/tv/tuner/frontend/FrontendStatus.java @@ -16,13 +16,13 @@ package android.media.tv.tuner.frontend; +import android.media.tv.tuner.Lnb; import android.media.tv.tuner.TunerConstants; import android.media.tv.tuner.TunerConstants.FrontendDvbcSpectralInversion; import android.media.tv.tuner.TunerConstants.FrontendDvbtHierarchy; import android.media.tv.tuner.TunerConstants.FrontendInnerFec; import android.media.tv.tuner.TunerConstants.FrontendModulation; import android.media.tv.tuner.TunerConstants.FrontendStatusType; -import android.media.tv.tuner.TunerConstants.LnbVoltage; /** * Frontend status @@ -128,7 +128,7 @@ public class FrontendStatus { return (int) mValue; } /** Power Voltage Type for LNB. */ - @LnbVoltage + @Lnb.Voltage public int getLnbVoltage() { if (mType != TunerConstants.FRONTEND_STATUS_TYPE_LNB_VOLTAGE) { throw new IllegalStateException(); diff --git a/media/java/android/media/tv/tuner/frontend/Isdbs3FrontendSettings.java b/media/java/android/media/tv/tuner/frontend/Isdbs3FrontendSettings.java index 736d0b199d29..45932a74a946 100644 --- a/media/java/android/media/tv/tuner/frontend/Isdbs3FrontendSettings.java +++ b/media/java/android/media/tv/tuner/frontend/Isdbs3FrontendSettings.java @@ -16,9 +16,6 @@ package android.media.tv.tuner.frontend; -import android.media.tv.tuner.FrontendSettings; -import android.media.tv.tuner.TunerConstants; - /** * Frontend settings for ISDBS-3. * @hide @@ -37,6 +34,6 @@ public class Isdbs3FrontendSettings extends FrontendSettings { @Override public int getType() { - return TunerConstants.FRONTEND_TYPE_ISDBS3; + return FrontendSettings.TYPE_ISDBS3; } } diff --git a/media/java/android/media/tv/tuner/frontend/IsdbsFrontendSettings.java b/media/java/android/media/tv/tuner/frontend/IsdbsFrontendSettings.java index 7fd5da78c600..e726a9a701f0 100644 --- a/media/java/android/media/tv/tuner/frontend/IsdbsFrontendSettings.java +++ b/media/java/android/media/tv/tuner/frontend/IsdbsFrontendSettings.java @@ -16,9 +16,6 @@ package android.media.tv.tuner.frontend; -import android.media.tv.tuner.FrontendSettings; -import android.media.tv.tuner.TunerConstants; - /** * Frontend settings for ISDBS. * @hide @@ -37,6 +34,6 @@ public class IsdbsFrontendSettings extends FrontendSettings { @Override public int getType() { - return TunerConstants.FRONTEND_TYPE_ISDBS; + return FrontendSettings.TYPE_ISDBS; } } diff --git a/media/java/android/media/tv/tuner/frontend/IsdbtFrontendSettings.java b/media/java/android/media/tv/tuner/frontend/IsdbtFrontendSettings.java index 3f83267fe5b4..f2b7d2413911 100644 --- a/media/java/android/media/tv/tuner/frontend/IsdbtFrontendSettings.java +++ b/media/java/android/media/tv/tuner/frontend/IsdbtFrontendSettings.java @@ -16,10 +16,6 @@ package android.media.tv.tuner.frontend; - -import android.media.tv.tuner.FrontendSettings; -import android.media.tv.tuner.TunerConstants; - /** * Frontend settings for ISDBT. * @hide @@ -37,6 +33,6 @@ public class IsdbtFrontendSettings extends FrontendSettings { @Override public int getType() { - return TunerConstants.FRONTEND_TYPE_ISDBT; + return FrontendSettings.TYPE_ISDBT; } } diff --git a/mms/java/android/telephony/MmsManager.java b/mms/java/android/telephony/MmsManager.java index 24ea3cfc5a75..65542673a607 100644 --- a/mms/java/android/telephony/MmsManager.java +++ b/mms/java/android/telephony/MmsManager.java @@ -16,11 +16,8 @@ package android.telephony; -import android.annotation.NonNull; -import android.annotation.Nullable; import android.app.ActivityThread; import android.app.PendingIntent; -import android.content.Context; import android.net.Uri; import android.os.Bundle; import android.os.RemoteException; @@ -30,16 +27,22 @@ import com.android.internal.telephony.IMms; /** * Manages MMS operations such as sending multimedia messages. + * Get this object by calling the static method {@link #getInstance()}. + * @hide */ -public final class MmsManager { +public class MmsManager { private static final String TAG = "MmsManager"; - private final Context mContext; + + /** Singleton object constructed during class initialization. */ + private static final MmsManager sInstance = new MmsManager(); /** - * @hide + * Get the MmsManager singleton instance. + * + * @return the {@link MmsManager} singleton instance. */ - public MmsManager(@NonNull Context context) { - mContext = context; + public static MmsManager getInstance() { + return sInstance; } /** @@ -53,9 +56,8 @@ public final class MmsManager { * @param sentIntent if not NULL this <code>PendingIntent</code> is broadcast when the message * is successfully sent, or failed */ - public void sendMultimediaMessage(int subId, @NonNull Uri contentUri, - @Nullable String locationUrl, @Nullable Bundle configOverrides, - @Nullable PendingIntent sentIntent) { + public void sendMultimediaMessage(int subId, Uri contentUri, String locationUrl, + Bundle configOverrides, PendingIntent sentIntent) { try { final IMms iMms = IMms.Stub.asInterface(ServiceManager.getService("imms")); if (iMms == null) { @@ -82,9 +84,8 @@ public final class MmsManager { * broadcast when the message is downloaded, or the download is failed * @throws IllegalArgumentException if locationUrl or contentUri is empty */ - public void downloadMultimediaMessage(int subId, @NonNull String locationUrl, - @NonNull Uri contentUri, @Nullable Bundle configOverrides, - @Nullable PendingIntent downloadedIntent) { + public void downloadMultimediaMessage(int subId, String locationUrl, Uri contentUri, + Bundle configOverrides, PendingIntent downloadedIntent) { try { final IMms iMms = IMms.Stub.asInterface(ServiceManager.getService("imms")); if (iMms == null) { diff --git a/packages/CarSystemUI/src/com/android/systemui/car/CarNotificationEntryManager.java b/packages/CarSystemUI/src/com/android/systemui/car/CarNotificationEntryManager.java index f7802d205a3a..6ec385438e23 100644 --- a/packages/CarSystemUI/src/com/android/systemui/car/CarNotificationEntryManager.java +++ b/packages/CarSystemUI/src/com/android/systemui/car/CarNotificationEntryManager.java @@ -19,14 +19,19 @@ import android.service.notification.NotificationListenerService; import android.service.notification.StatusBarNotification; import com.android.systemui.statusbar.FeatureFlags; +import com.android.systemui.statusbar.NotificationRemoteInputManager; import com.android.systemui.statusbar.notification.NotificationEntryManager; import com.android.systemui.statusbar.notification.collection.NotificationRankingManager; +import com.android.systemui.statusbar.notification.collection.NotificationRowBinder; import com.android.systemui.statusbar.notification.logging.NotifLog; import com.android.systemui.statusbar.phone.NotificationGroupManager; +import com.android.systemui.util.leak.LeakDetector; import javax.inject.Inject; import javax.inject.Singleton; +import dagger.Lazy; + /** * Car specific notification entry manager that does nothing when adding a notification. * @@ -42,8 +47,12 @@ public class CarNotificationEntryManager extends NotificationEntryManager { NotificationGroupManager groupManager, NotificationRankingManager rankingManager, KeyguardEnvironment keyguardEnvironment, - FeatureFlags featureFlags) { - super(notifLog, groupManager, rankingManager, keyguardEnvironment, featureFlags); + FeatureFlags featureFlags, + Lazy<NotificationRowBinder> notificationRowBinderLazy, + Lazy<NotificationRemoteInputManager> notificationRemoteInputManagerLazy, + LeakDetector leakDetector) { + super(notifLog, groupManager, rankingManager, keyguardEnvironment, featureFlags, + notificationRowBinderLazy, notificationRemoteInputManagerLazy, leakDetector); } @Override diff --git a/packages/CarSystemUI/src/com/android/systemui/statusbar/car/CarStatusBar.java b/packages/CarSystemUI/src/com/android/systemui/statusbar/car/CarStatusBar.java index 3c3ebe2fe228..4521f5fb470e 100644 --- a/packages/CarSystemUI/src/com/android/systemui/statusbar/car/CarStatusBar.java +++ b/packages/CarSystemUI/src/com/android/systemui/statusbar/car/CarStatusBar.java @@ -16,7 +16,6 @@ package com.android.systemui.statusbar.car; -import static com.android.systemui.Dependency.ALLOW_NOTIFICATION_LONG_PRESS_NAME; import static com.android.systemui.Dependency.TIME_TICK_HANDLER_NAME; import android.animation.Animator; @@ -107,6 +106,7 @@ import com.android.systemui.statusbar.notification.NotificationEntryManager; import com.android.systemui.statusbar.notification.NotificationInterruptionStateProvider; import com.android.systemui.statusbar.notification.NotificationWakeUpCoordinator; import com.android.systemui.statusbar.notification.VisualStabilityManager; +import com.android.systemui.statusbar.notification.collection.NotificationRowBinderImpl; import com.android.systemui.statusbar.notification.collection.init.NewNotifPipeline; import com.android.systemui.statusbar.notification.logging.NotificationLogger; import com.android.systemui.statusbar.notification.row.NotificationGutsManager; @@ -268,7 +268,6 @@ public class CarStatusBar extends StatusBar implements CarBatteryController.Batt HeadsUpManagerPhone headsUpManagerPhone, DynamicPrivacyController dynamicPrivacyController, BypassHeadsUpNotifier bypassHeadsUpNotifier, - @Named(ALLOW_NOTIFICATION_LONG_PRESS_NAME) boolean allowNotificationLongPress, Lazy<NewNotifPipeline> newNotifPipeline, FalsingManager falsingManager, BroadcastDispatcher broadcastDispatcher, @@ -334,6 +333,7 @@ public class CarStatusBar extends StatusBar implements CarBatteryController.Batt KeyguardDismissUtil keyguardDismissUtil, ExtensionController extensionController, UserInfoControllerImpl userInfoControllerImpl, + NotificationRowBinderImpl notificationRowBinder, DismissCallbackRegistry dismissCallbackRegistry, /* Car Settings injected components. */ CarServiceProvider carServiceProvider, @@ -355,7 +355,6 @@ public class CarStatusBar extends StatusBar implements CarBatteryController.Batt headsUpManagerPhone, dynamicPrivacyController, bypassHeadsUpNotifier, - allowNotificationLongPress, newNotifPipeline, falsingManager, broadcastDispatcher, @@ -421,6 +420,7 @@ public class CarStatusBar extends StatusBar implements CarBatteryController.Batt keyguardDismissUtil, extensionController, userInfoControllerImpl, + notificationRowBinder, dismissCallbackRegistry); mScrimController = scrimController; mLockscreenLockIconController = lockscreenLockIconController; diff --git a/packages/CarSystemUI/src/com/android/systemui/statusbar/car/CarStatusBarModule.java b/packages/CarSystemUI/src/com/android/systemui/statusbar/car/CarStatusBarModule.java index a1eccceea771..e5a091f94077 100644 --- a/packages/CarSystemUI/src/com/android/systemui/statusbar/car/CarStatusBarModule.java +++ b/packages/CarSystemUI/src/com/android/systemui/statusbar/car/CarStatusBarModule.java @@ -16,7 +16,6 @@ package com.android.systemui.statusbar.car; -import static com.android.systemui.Dependency.ALLOW_NOTIFICATION_LONG_PRESS_NAME; import static com.android.systemui.Dependency.TIME_TICK_HANDLER_NAME; import android.content.Context; @@ -67,6 +66,7 @@ import com.android.systemui.statusbar.notification.NotificationEntryManager; import com.android.systemui.statusbar.notification.NotificationInterruptionStateProvider; import com.android.systemui.statusbar.notification.NotificationWakeUpCoordinator; import com.android.systemui.statusbar.notification.VisualStabilityManager; +import com.android.systemui.statusbar.notification.collection.NotificationRowBinderImpl; import com.android.systemui.statusbar.notification.collection.init.NewNotifPipeline; import com.android.systemui.statusbar.notification.logging.NotificationLogger; import com.android.systemui.statusbar.notification.row.NotificationGutsManager; @@ -138,7 +138,6 @@ public class CarStatusBarModule { HeadsUpManagerPhone headsUpManagerPhone, DynamicPrivacyController dynamicPrivacyController, BypassHeadsUpNotifier bypassHeadsUpNotifier, - @Named(ALLOW_NOTIFICATION_LONG_PRESS_NAME) boolean allowNotificationLongPress, Lazy<NewNotifPipeline> newNotifPipeline, FalsingManager falsingManager, BroadcastDispatcher broadcastDispatcher, @@ -204,6 +203,7 @@ public class CarStatusBarModule { KeyguardDismissUtil keyguardDismissUtil, ExtensionController extensionController, UserInfoControllerImpl userInfoControllerImpl, + NotificationRowBinderImpl notificationRowBinder, DismissCallbackRegistry dismissCallbackRegistry, CarServiceProvider carServiceProvider, Lazy<PowerManagerHelper> powerManagerHelperLazy, @@ -224,7 +224,6 @@ public class CarStatusBarModule { headsUpManagerPhone, dynamicPrivacyController, bypassHeadsUpNotifier, - allowNotificationLongPress, newNotifPipeline, falsingManager, broadcastDispatcher, @@ -289,6 +288,7 @@ public class CarStatusBarModule { keyguardDismissUtil, extensionController, userInfoControllerImpl, + notificationRowBinder, dismissCallbackRegistry, carServiceProvider, powerManagerHelperLazy, diff --git a/packages/Shell/AndroidManifest.xml b/packages/Shell/AndroidManifest.xml index 347d6c2264eb..1c63efc3b5bc 100644 --- a/packages/Shell/AndroidManifest.xml +++ b/packages/Shell/AndroidManifest.xml @@ -194,6 +194,9 @@ <uses-permission android:name="android.permission.MANAGE_APPOPS" /> + <!-- Permission required for storage tests - FuseDaemonHostTest --> + <uses-permission android:name="android.permission.MANAGE_EXTERNAL_STORAGE"/> + <!-- Permission needed to run network tests in CTS --> <uses-permission android:name="android.permission.MANAGE_TEST_NETWORKS" /> <!-- Permission needed to test tcp keepalive offload. --> diff --git a/packages/SystemUI/src/com/android/systemui/bubbles/BubbleData.java b/packages/SystemUI/src/com/android/systemui/bubbles/BubbleData.java index 97224f1234dd..ccbbb2465742 100644 --- a/packages/SystemUI/src/com/android/systemui/bubbles/BubbleData.java +++ b/packages/SystemUI/src/com/android/systemui/bubbles/BubbleData.java @@ -111,7 +111,10 @@ public class BubbleData { } private final Context mContext; + /** Bubbles that are actively in the stack. */ private final List<Bubble> mBubbles; + /** Bubbles that are being loaded but haven't been added to the stack just yet. */ + private final List<Bubble> mPendingBubbles; private Bubble mSelectedBubble; private boolean mExpanded; private final int mMaxBubbles; @@ -143,6 +146,7 @@ public class BubbleData { public BubbleData(Context context) { mContext = context; mBubbles = new ArrayList<>(); + mPendingBubbles = new ArrayList<>(); mStateChange = new Update(mBubbles); mMaxBubbles = mContext.getResources().getInteger(R.integer.bubbles_max_rendered); } @@ -188,7 +192,15 @@ public class BubbleData { Bubble getOrCreateBubble(NotificationEntry entry) { Bubble bubble = getBubbleWithKey(entry.getKey()); if (bubble == null) { + // Check for it in pending + for (int i = 0; i < mPendingBubbles.size(); i++) { + Bubble b = mPendingBubbles.get(i); + if (b.getKey().equals(entry.getKey())) { + return b; + } + } bubble = new Bubble(entry); + mPendingBubbles.add(bubble); } else { bubble.setEntry(entry); } @@ -204,7 +216,7 @@ public class BubbleData { if (DEBUG_BUBBLE_DATA) { Log.d(TAG, "notificationEntryUpdated: " + bubble); } - + mPendingBubbles.remove(bubble); // No longer pending once we're here Bubble prevBubble = getBubbleWithKey(bubble.getKey()); suppressFlyout |= !shouldShowFlyout(bubble.getEntry()); @@ -377,6 +389,12 @@ public class BubbleData { } private void doRemove(String key, @DismissReason int reason) { + // If it was pending remove it + for (int i = 0; i < mPendingBubbles.size(); i++) { + if (mPendingBubbles.get(i).getKey().equals(key)) { + mPendingBubbles.remove(mPendingBubbles.get(i)); + } + } int indexToRemove = indexForKey(key); if (indexToRemove == -1) { return; diff --git a/packages/SystemUI/src/com/android/systemui/dagger/DependencyBinder.java b/packages/SystemUI/src/com/android/systemui/dagger/DependencyBinder.java index 6744d74004f0..20917bd9dcb0 100644 --- a/packages/SystemUI/src/com/android/systemui/dagger/DependencyBinder.java +++ b/packages/SystemUI/src/com/android/systemui/dagger/DependencyBinder.java @@ -21,9 +21,12 @@ import com.android.systemui.appops.AppOpsController; import com.android.systemui.appops.AppOpsControllerImpl; import com.android.systemui.classifier.FalsingManagerProxy; import com.android.systemui.doze.DozeHost; +import com.android.systemui.globalactions.GlobalActionsComponent; +import com.android.systemui.globalactions.GlobalActionsImpl; import com.android.systemui.plugins.ActivityStarter; import com.android.systemui.plugins.DarkIconDispatcher; import com.android.systemui.plugins.FalsingManager; +import com.android.systemui.plugins.GlobalActions; import com.android.systemui.plugins.VolumeDialogController; import com.android.systemui.plugins.statusbar.StatusBarStateController; import com.android.systemui.power.PowerNotificationWarnings; @@ -99,6 +102,17 @@ public abstract class DependencyBinder { /** */ @Binds + public abstract GlobalActions provideGlobalActions(GlobalActionsImpl controllerImpl); + + /** + */ + @Binds + public abstract GlobalActions.GlobalActionsManager provideGlobalActionsManager( + GlobalActionsComponent controllerImpl); + + /** + */ + @Binds public abstract LocationController provideLocationController( LocationControllerImpl controllerImpl); diff --git a/packages/SystemUI/src/com/android/systemui/dagger/SystemServicesModule.java b/packages/SystemUI/src/com/android/systemui/dagger/SystemServicesModule.java index 26337b1f24b1..3aa14a31a5d9 100644 --- a/packages/SystemUI/src/com/android/systemui/dagger/SystemServicesModule.java +++ b/packages/SystemUI/src/com/android/systemui/dagger/SystemServicesModule.java @@ -26,17 +26,24 @@ import android.app.KeyguardManager; import android.app.NotificationManager; import android.app.WallpaperManager; import android.app.admin.DevicePolicyManager; +import android.app.trust.TrustManager; +import android.content.ContentResolver; import android.content.Context; import android.content.pm.IPackageManager; import android.content.res.Resources; import android.hardware.SensorPrivacyManager; +import android.media.AudioManager; +import android.net.ConnectivityManager; import android.os.Handler; import android.os.PowerManager; import android.os.ServiceManager; import android.os.UserHandle; import android.os.UserManager; +import android.os.Vibrator; import android.service.dreams.DreamService; import android.service.dreams.IDreamManager; +import android.telecom.TelecomManager; +import android.telephony.TelephonyManager; import android.view.IWindowManager; import android.view.WindowManager; import android.view.WindowManagerGlobal; @@ -66,6 +73,12 @@ public class SystemServicesModule { return context.getSystemService(AccessibilityManager.class); } + @Provides + @Singleton + static ActivityManager provideActivityManager(Context context) { + return context.getSystemService(ActivityManager.class); + } + @Singleton @Provides static AlarmManager provideAlarmManager(Context context) { @@ -74,10 +87,21 @@ public class SystemServicesModule { @Provides @Singleton - static ActivityManager provideActivityManager(Context context) { - return context.getSystemService(ActivityManager.class); + static AudioManager provideAudioManager(Context context) { + return context.getSystemService(AudioManager.class); } + @Provides + @Singleton + static ConnectivityManager provideConnectivityManagager(Context context) { + return context.getSystemService(ConnectivityManager.class); + } + + @Provides + @Singleton + static ContentResolver provideContentResolver(Context context) { + return context.getContentResolver(); + } @Provides @DisplayId @@ -185,6 +209,31 @@ public class SystemServicesModule { @Provides @Singleton + static TelecomManager provideTelecomManager(Context context) { + return context.getSystemService(TelecomManager.class); + } + + @Provides + @Singleton + static TelephonyManager provideTelephonyManager(Context context) { + return context.getSystemService(TelephonyManager.class); + } + + @Provides + @Singleton + static TrustManager provideTrustManager(Context context) { + return context.getSystemService(TrustManager.class); + } + + @Provides + @Singleton + @Nullable + static Vibrator provideVibrator(Context context) { + return context.getSystemService(Vibrator.class); + } + + @Provides + @Singleton static UserManager provideUserManager(Context context) { return context.getSystemService(UserManager.class); } diff --git a/packages/SystemUI/src/com/android/systemui/dagger/SystemUIModule.java b/packages/SystemUI/src/com/android/systemui/dagger/SystemUIModule.java index 58ddda9ce720..b19523866814 100644 --- a/packages/SystemUI/src/com/android/systemui/dagger/SystemUIModule.java +++ b/packages/SystemUI/src/com/android/systemui/dagger/SystemUIModule.java @@ -31,6 +31,8 @@ import com.android.systemui.recents.Recents; import com.android.systemui.stackdivider.Divider; import com.android.systemui.statusbar.CommandQueue; import com.android.systemui.statusbar.notification.collection.NotifListBuilderImpl; +import com.android.systemui.statusbar.notification.collection.NotificationRowBinder; +import com.android.systemui.statusbar.notification.collection.NotificationRowBinderImpl; import com.android.systemui.statusbar.notification.collection.listbuilder.NotifListBuilder; import com.android.systemui.statusbar.notification.people.PeopleHubModule; import com.android.systemui.statusbar.phone.KeyguardLiftController; @@ -82,6 +84,11 @@ public abstract class SystemUIModule { keyguardUpdateMonitor, dumpController); } + /** */ + @Binds + public abstract NotificationRowBinder bindNotificationRowBinder( + NotificationRowBinderImpl notificationRowBinder); + @Singleton @Provides static SysUiState provideSysUiState() { diff --git a/packages/SystemUI/src/com/android/systemui/globalactions/GlobalActionsComponent.java b/packages/SystemUI/src/com/android/systemui/globalactions/GlobalActionsComponent.java index 19b6f8232a9a..e949007a158b 100644 --- a/packages/SystemUI/src/com/android/systemui/globalactions/GlobalActionsComponent.java +++ b/packages/SystemUI/src/com/android/systemui/globalactions/GlobalActionsComponent.java @@ -19,7 +19,6 @@ import android.os.RemoteException; import android.os.ServiceManager; import com.android.internal.statusbar.IStatusBarService; -import com.android.systemui.Dependency; import com.android.systemui.SystemUI; import com.android.systemui.plugins.GlobalActions; import com.android.systemui.plugins.GlobalActions.GlobalActionsManager; @@ -29,6 +28,7 @@ import com.android.systemui.statusbar.policy.ExtensionController; import com.android.systemui.statusbar.policy.ExtensionController.Extension; import javax.inject.Inject; +import javax.inject.Provider; import javax.inject.Singleton; /** @@ -38,23 +38,29 @@ import javax.inject.Singleton; public class GlobalActionsComponent extends SystemUI implements Callbacks, GlobalActionsManager { private final CommandQueue mCommandQueue; + private final ExtensionController mExtensionController; + private final Provider<GlobalActions> mGlobalActionsProvider; private GlobalActions mPlugin; private Extension<GlobalActions> mExtension; private IStatusBarService mBarService; @Inject - public GlobalActionsComponent(Context context, CommandQueue commandQueue) { + public GlobalActionsComponent(Context context, CommandQueue commandQueue, + ExtensionController extensionController, + Provider<GlobalActions> globalActionsProvider) { super(context); mCommandQueue = commandQueue; + mExtensionController = extensionController; + mGlobalActionsProvider = globalActionsProvider; } @Override public void start() { mBarService = IStatusBarService.Stub.asInterface( ServiceManager.getService(Context.STATUS_BAR_SERVICE)); - mExtension = Dependency.get(ExtensionController.class).newExtension(GlobalActions.class) + mExtension = mExtensionController.newExtension(GlobalActions.class) .withPlugin(GlobalActions.class) - .withDefault(() -> new GlobalActionsImpl(mContext, mCommandQueue)) + .withDefault(mGlobalActionsProvider::get) .withCallback(this::onExtensionCallback) .build(); mPlugin = mExtension.get(); diff --git a/packages/SystemUI/src/com/android/systemui/globalactions/GlobalActionsDialog.java b/packages/SystemUI/src/com/android/systemui/globalactions/GlobalActionsDialog.java index 3ccad64d76dc..fc19fa0c55fc 100644 --- a/packages/SystemUI/src/com/android/systemui/globalactions/GlobalActionsDialog.java +++ b/packages/SystemUI/src/com/android/systemui/globalactions/GlobalActionsDialog.java @@ -21,8 +21,10 @@ import static com.android.internal.widget.LockPatternUtils.StrongAuthTracker.SOM import static com.android.internal.widget.LockPatternUtils.StrongAuthTracker.STRONG_AUTH_NOT_REQUIRED; import static com.android.internal.widget.LockPatternUtils.StrongAuthTracker.STRONG_AUTH_REQUIRED_AFTER_USER_LOCKDOWN; +import android.annotation.Nullable; import android.app.ActivityManager; import android.app.Dialog; +import android.app.IActivityManager; import android.app.KeyguardManager; import android.app.PendingIntent; import android.app.StatusBarManager; @@ -30,11 +32,13 @@ import android.app.WallpaperManager; import android.app.admin.DevicePolicyManager; import android.app.trust.TrustManager; import android.content.BroadcastReceiver; +import android.content.ContentResolver; import android.content.Context; import android.content.DialogInterface; import android.content.Intent; import android.content.IntentFilter; import android.content.pm.UserInfo; +import android.content.res.Resources; import android.database.ContentObserver; import android.graphics.drawable.Drawable; import android.media.AudioManager; @@ -44,13 +48,11 @@ import android.os.Handler; import android.os.IBinder; import android.os.Message; import android.os.RemoteException; -import android.os.ServiceManager; import android.os.SystemProperties; import android.os.UserHandle; import android.os.UserManager; import android.os.Vibrator; import android.provider.Settings; -import android.service.dreams.DreamService; import android.service.dreams.IDreamManager; import android.sysprop.TelephonyProperties; import android.telecom.TelecomManager; @@ -92,6 +94,7 @@ import com.android.systemui.MultiListLayout; import com.android.systemui.MultiListLayout.MultiListAdapter; import com.android.systemui.broadcast.BroadcastDispatcher; import com.android.systemui.colorextraction.SysuiColorExtractor; +import com.android.systemui.dagger.qualifiers.Main; import com.android.systemui.plugins.ActivityStarter; import com.android.systemui.plugins.GlobalActions.GlobalActionsManager; import com.android.systemui.plugins.GlobalActionsPanelPlugin; @@ -106,6 +109,8 @@ import com.android.systemui.volume.SystemUIInterpolators.LogAccelerateInterpolat import java.util.ArrayList; import java.util.List; +import javax.inject.Inject; + /** * Helper to show the global actions dialog. Each item is an {@link Action} that * may show depending on whether the keyguard is showing, and whether the device @@ -146,6 +151,13 @@ public class GlobalActionsDialog implements DialogInterface.OnDismissListener, private final LockPatternUtils mLockPatternUtils; private final KeyguardManager mKeyguardManager; private final BroadcastDispatcher mBroadcastDispatcher; + private final ContentResolver mContentResolver; + private final Resources mResources; + private final UserManager mUserManager; + private final TrustManager mTrustManager; + private final IActivityManager mIActivityManager; + private final TelecomManager mTelecomManager; + private final MetricsLogger mMetricsLogger; private ArrayList<Action> mItems; private ActionsDialog mDialog; @@ -161,8 +173,6 @@ public class GlobalActionsDialog implements DialogInterface.OnDismissListener, private boolean mIsWaitingForEcmExit = false; private boolean mHasTelephony; private boolean mHasVibrator; - private boolean mHasLogoutButton; - private boolean mHasLockdownButton; private final boolean mShowSilentToggle; private final EmergencyAffordanceManager mEmergencyAffordanceManager; private final ScreenshotHelper mScreenshotHelper; @@ -173,17 +183,32 @@ public class GlobalActionsDialog implements DialogInterface.OnDismissListener, /** * @param context everything needs a context :( */ - public GlobalActionsDialog(Context context, GlobalActionsManager windowManagerFuncs) { + @Inject + public GlobalActionsDialog(Context context, GlobalActionsManager windowManagerFuncs, + AudioManager audioManager, IDreamManager iDreamManager, + DevicePolicyManager devicePolicyManager, LockPatternUtils lockPatternUtils, + KeyguardManager keyguardManager, BroadcastDispatcher broadcastDispatcher, + ConnectivityManager connectivityManager, TelephonyManager telephonyManager, + ContentResolver contentResolver, @Nullable Vibrator vibrator, @Main Resources resources, + ConfigurationController configurationController, ActivityStarter activityStarter, + KeyguardStateController keyguardStateController, UserManager userManager, + TrustManager trustManager, IActivityManager iActivityManager, + TelecomManager telecomManager, MetricsLogger metricsLogger) { mContext = new ContextThemeWrapper(context, com.android.systemui.R.style.qs_theme); mWindowManagerFuncs = windowManagerFuncs; - mAudioManager = (AudioManager) mContext.getSystemService(Context.AUDIO_SERVICE); - mDreamManager = IDreamManager.Stub.asInterface( - ServiceManager.getService(DreamService.DREAM_SERVICE)); - mDevicePolicyManager = (DevicePolicyManager) mContext.getSystemService( - Context.DEVICE_POLICY_SERVICE); - mLockPatternUtils = new LockPatternUtils(mContext); - mKeyguardManager = (KeyguardManager) mContext.getSystemService(Context.KEYGUARD_SERVICE); - mBroadcastDispatcher = Dependency.get(BroadcastDispatcher.class); + mAudioManager = audioManager; + mDreamManager = iDreamManager; + mDevicePolicyManager = devicePolicyManager; + mLockPatternUtils = lockPatternUtils; + mKeyguardManager = keyguardManager; + mBroadcastDispatcher = broadcastDispatcher; + mContentResolver = contentResolver; + mResources = resources; + mUserManager = userManager; + mTrustManager = trustManager; + mIActivityManager = iActivityManager; + mTelecomManager = telecomManager; + mMetricsLogger = metricsLogger; // receive broadcasts IntentFilter filter = new IntentFilter(); @@ -192,32 +217,25 @@ public class GlobalActionsDialog implements DialogInterface.OnDismissListener, filter.addAction(TelephonyManager.ACTION_EMERGENCY_CALLBACK_MODE_CHANGED); mBroadcastDispatcher.registerReceiver(mBroadcastReceiver, filter); - ConnectivityManager cm = (ConnectivityManager) - context.getSystemService(Context.CONNECTIVITY_SERVICE); - mHasTelephony = cm.isNetworkSupported(ConnectivityManager.TYPE_MOBILE); + mHasTelephony = connectivityManager.isNetworkSupported(ConnectivityManager.TYPE_MOBILE); // get notified of phone state changes - TelephonyManager telephonyManager = - (TelephonyManager) context.getSystemService(Context.TELEPHONY_SERVICE); telephonyManager.listen(mPhoneStateListener, PhoneStateListener.LISTEN_SERVICE_STATE); - mContext.getContentResolver().registerContentObserver( + contentResolver.registerContentObserver( Settings.Global.getUriFor(Settings.Global.AIRPLANE_MODE_ON), true, mAirplaneModeObserver); - Vibrator vibrator = (Vibrator) mContext.getSystemService(Context.VIBRATOR_SERVICE); mHasVibrator = vibrator != null && vibrator.hasVibrator(); - mShowSilentToggle = SHOW_SILENT_TOGGLE && !mContext.getResources().getBoolean( + mShowSilentToggle = SHOW_SILENT_TOGGLE && !resources.getBoolean( R.bool.config_useFixedVolume); mEmergencyAffordanceManager = new EmergencyAffordanceManager(context); mScreenshotHelper = new ScreenshotHelper(context); mScreenRecordHelper = new ScreenRecordHelper(context); - Dependency.get(ConfigurationController.class).addCallback(this); + configurationController.addCallback(this); - mActivityStarter = Dependency.get(ActivityStarter.class); - KeyguardStateController keyguardStateController = - Dependency.get(KeyguardStateController.class); + mActivityStarter = activityStarter; keyguardStateController.addCallback(new KeyguardStateController.Callback() { @Override public void onUnlockedChanged() { @@ -343,12 +361,9 @@ public class GlobalActionsDialog implements DialogInterface.OnDismissListener, onAirplaneModeChanged(); mItems = new ArrayList<Action>(); - String[] defaultActions = mContext.getResources().getStringArray( - R.array.config_globalActionsList); + String[] defaultActions = mResources.getStringArray(R.array.config_globalActionsList); ArraySet<String> addedKeys = new ArraySet<String>(); - mHasLogoutButton = false; - mHasLockdownButton = false; for (int i = 0; i < defaultActions.length; i++) { String actionKey = defaultActions[i]; if (addedKeys.contains(actionKey)) { @@ -360,7 +375,7 @@ public class GlobalActionsDialog implements DialogInterface.OnDismissListener, } else if (GLOBAL_ACTION_KEY_AIRPLANE.equals(actionKey)) { mItems.add(mAirplaneModeOn); } else if (GLOBAL_ACTION_KEY_BUGREPORT.equals(actionKey)) { - if (Settings.Global.getInt(mContext.getContentResolver(), + if (Settings.Global.getInt(mContentResolver, Settings.Global.BUGREPORT_IN_POWER_MENU, 0) != 0 && isCurrentUserOwner()) { mItems.add(new BugReportAction()); } @@ -375,11 +390,10 @@ public class GlobalActionsDialog implements DialogInterface.OnDismissListener, } else if (GLOBAL_ACTION_KEY_SETTINGS.equals(actionKey)) { mItems.add(getSettingsAction()); } else if (GLOBAL_ACTION_KEY_LOCKDOWN.equals(actionKey)) { - if (Settings.Secure.getIntForUser(mContext.getContentResolver(), + if (Settings.Secure.getIntForUser(mContentResolver, Settings.Secure.LOCKDOWN_IN_POWER_MENU, 0, getCurrentUser().id) != 0 && shouldDisplayLockdown()) { mItems.add(getLockdownAction()); - mHasLockdownButton = true; } } else if (GLOBAL_ACTION_KEY_VOICEASSIST.equals(actionKey)) { mItems.add(getVoiceAssistAction()); @@ -393,7 +407,6 @@ public class GlobalActionsDialog implements DialogInterface.OnDismissListener, if (mDevicePolicyManager.isLogoutEnabled() && getCurrentUser().id != UserHandle.USER_SYSTEM) { mItems.add(new LogoutAction()); - mHasLogoutButton = true; } } else if (GLOBAL_ACTION_KEY_EMERGENCY.equals(actionKey)) { if (!mEmergencyAffordanceManager.needsEmergencyAffordance()) { @@ -474,8 +487,7 @@ public class GlobalActionsDialog implements DialogInterface.OnDismissListener, @Override public boolean onLongPress() { - UserManager um = (UserManager) mContext.getSystemService(Context.USER_SERVICE); - if (!um.hasUserRestriction(UserManager.DISALLOW_SAFE_BOOT)) { + if (!mUserManager.hasUserRestriction(UserManager.DISALLOW_SAFE_BOOT)) { mWindowManagerFuncs.reboot(true); return true; } @@ -560,9 +572,8 @@ public class GlobalActionsDialog implements DialogInterface.OnDismissListener, @Override public void onPress() { - MetricsLogger.action(mContext, MetricsEvent.ACTION_EMERGENCY_DIALER_FROM_POWER_MENU); - Intent intent = mContext.getSystemService(TelecomManager.class) - .createLaunchEmergencyDialerIntent(null /* number */); + mMetricsLogger.action(MetricsEvent.ACTION_EMERGENCY_DIALER_FROM_POWER_MENU); + Intent intent = mTelecomManager.createLaunchEmergencyDialerIntent(null /* number */); intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_EXCLUDE_FROM_RECENTS | Intent.FLAG_ACTIVITY_CLEAR_TOP); @@ -579,8 +590,7 @@ public class GlobalActionsDialog implements DialogInterface.OnDismissListener, @Override public boolean onLongPress() { - UserManager um = (UserManager) mContext.getSystemService(Context.USER_SERVICE); - if (!um.hasUserRestriction(UserManager.DISALLOW_SAFE_BOOT)) { + if (!mUserManager.hasUserRestriction(UserManager.DISALLOW_SAFE_BOOT)) { mWindowManagerFuncs.reboot(true); return true; } @@ -618,8 +628,7 @@ public class GlobalActionsDialog implements DialogInterface.OnDismissListener, @Override public void run() { mScreenshotHelper.takeScreenshot(1, true, true, mHandler, null); - MetricsLogger.action(mContext, - MetricsEvent.ACTION_SCREENSHOT_POWER_MENU); + mMetricsLogger.action(MetricsEvent.ACTION_SCREENSHOT_POWER_MENU); } }, 500); } @@ -666,11 +675,11 @@ public class GlobalActionsDialog implements DialogInterface.OnDismissListener, public void run() { try { // Take an "interactive" bugreport. - MetricsLogger.action(mContext, + mMetricsLogger.action( MetricsEvent.ACTION_BUGREPORT_FROM_POWER_MENU_INTERACTIVE); - if (!ActivityManager.getService().launchBugReportHandlerApp()) { + if (!mIActivityManager.launchBugReportHandlerApp()) { Log.w(TAG, "Bugreport handler could not be launched"); - ActivityManager.getService().requestInteractiveBugReport(); + mIActivityManager.requestInteractiveBugReport(); } } catch (RemoteException e) { } @@ -687,8 +696,8 @@ public class GlobalActionsDialog implements DialogInterface.OnDismissListener, } try { // Take a "full" bugreport. - MetricsLogger.action(mContext, MetricsEvent.ACTION_BUGREPORT_FROM_POWER_MENU_FULL); - ActivityManager.getService().requestFullBugReport(); + mMetricsLogger.action(MetricsEvent.ACTION_BUGREPORT_FROM_POWER_MENU_FULL); + mIActivityManager.requestFullBugReport(); } catch (RemoteException e) { } return false; @@ -726,8 +735,8 @@ public class GlobalActionsDialog implements DialogInterface.OnDismissListener, mHandler.postDelayed(() -> { try { int currentUserId = getCurrentUser().id; - ActivityManager.getService().switchUser(UserHandle.USER_SYSTEM); - ActivityManager.getService().stopUser(currentUserId, true /*force*/, null); + mIActivityManager.switchUser(UserHandle.USER_SYSTEM); + mIActivityManager.stopUser(currentUserId, true /*force*/, null); } catch (RemoteException re) { Log.e(TAG, "Couldn't logout user " + re); } @@ -834,20 +843,18 @@ public class GlobalActionsDialog implements DialogInterface.OnDismissListener, } private void lockProfiles() { - final UserManager um = (UserManager) mContext.getSystemService(Context.USER_SERVICE); - final TrustManager tm = (TrustManager) mContext.getSystemService(Context.TRUST_SERVICE); final int currentUserId = getCurrentUser().id; - final int[] profileIds = um.getEnabledProfileIds(currentUserId); + final int[] profileIds = mUserManager.getEnabledProfileIds(currentUserId); for (final int id : profileIds) { if (id != currentUserId) { - tm.setDeviceLockedForUser(id, true); + mTrustManager.setDeviceLockedForUser(id, true); } } } private UserInfo getCurrentUser() { try { - return ActivityManager.getService().getCurrentUser(); + return mIActivityManager.getCurrentUser(); } catch (RemoteException re) { return null; } @@ -859,9 +866,8 @@ public class GlobalActionsDialog implements DialogInterface.OnDismissListener, } private void addUsersToMenu(ArrayList<Action> items) { - UserManager um = (UserManager) mContext.getSystemService(Context.USER_SERVICE); - if (um.isUserSwitcherEnabled()) { - List<UserInfo> users = um.getUsers(); + if (mUserManager.isUserSwitcherEnabled()) { + List<UserInfo> users = mUserManager.getUsers(); UserInfo currentUser = getCurrentUser(); for (final UserInfo user : users) { if (user.supportsSwitchToByUser()) { @@ -875,7 +881,7 @@ public class GlobalActionsDialog implements DialogInterface.OnDismissListener, + (isCurrentUser ? " \u2714" : "")) { public void onPress() { try { - ActivityManager.getService().switchUser(user.id); + mIActivityManager.switchUser(user.id); } catch (RemoteException re) { Log.e(TAG, "Couldn't switch user " + re); } @@ -932,7 +938,7 @@ public class GlobalActionsDialog implements DialogInterface.OnDismissListener, /** {@inheritDoc} */ public void onShow(DialogInterface dialog) { - MetricsLogger.visible(mContext, MetricsEvent.POWER_MENU); + mMetricsLogger.visible(MetricsEvent.POWER_MENU); } /** @@ -1492,7 +1498,7 @@ public class GlobalActionsDialog implements DialogInterface.OnDismissListener, if (mHasTelephony) return; boolean airplaneModeOn = Settings.Global.getInt( - mContext.getContentResolver(), + mContentResolver, Settings.Global.AIRPLANE_MODE_ON, 0) == 1; mAirplaneState = airplaneModeOn ? ToggleAction.State.On : ToggleAction.State.Off; @@ -1504,7 +1510,7 @@ public class GlobalActionsDialog implements DialogInterface.OnDismissListener, */ private void changeAirplaneModeSystemSetting(boolean on) { Settings.Global.putInt( - mContext.getContentResolver(), + mContentResolver, Settings.Global.AIRPLANE_MODE_ON, on ? 1 : 0); Intent intent = new Intent(Intent.ACTION_AIRPLANE_MODE_CHANGED); diff --git a/packages/SystemUI/src/com/android/systemui/globalactions/GlobalActionsImpl.java b/packages/SystemUI/src/com/android/systemui/globalactions/GlobalActionsImpl.java index d385123117d3..c911bf28effd 100644 --- a/packages/SystemUI/src/com/android/systemui/globalactions/GlobalActionsImpl.java +++ b/packages/SystemUI/src/com/android/systemui/globalactions/GlobalActionsImpl.java @@ -45,24 +45,32 @@ import com.android.systemui.statusbar.policy.DeviceProvisionedController; import com.android.systemui.statusbar.policy.ExtensionController; import com.android.systemui.statusbar.policy.KeyguardStateController; +import javax.inject.Inject; + +import dagger.Lazy; + public class GlobalActionsImpl implements GlobalActions, CommandQueue.Callbacks, PluginListener<GlobalActionsPanelPlugin> { private static final float SHUTDOWN_SCRIM_ALPHA = 0.95f; private final Context mContext; + private final Lazy<GlobalActionsDialog> mGlobalActionsDialogLazy; private final KeyguardStateController mKeyguardStateController; private final DeviceProvisionedController mDeviceProvisionedController; private final ExtensionController.Extension<GlobalActionsPanelPlugin> mPanelExtension; private GlobalActionsPanelPlugin mPlugin; private final CommandQueue mCommandQueue; - private GlobalActionsDialog mGlobalActions; + private GlobalActionsDialog mGlobalActionsDialog; private boolean mDisabled; private final PluginManager mPluginManager; private final String mPluginPackageName; - public GlobalActionsImpl(Context context, CommandQueue commandQueue) { + @Inject + public GlobalActionsImpl(Context context, CommandQueue commandQueue, + Lazy<GlobalActionsDialog> globalActionsDialogLazy) { mContext = context; + mGlobalActionsDialogLazy = globalActionsDialogLazy; mKeyguardStateController = Dependency.get(KeyguardStateController.class); mDeviceProvisionedController = Dependency.get(DeviceProvisionedController.class); mPluginManager = Dependency.get(PluginManager.class); @@ -83,19 +91,17 @@ public class GlobalActionsImpl implements GlobalActions, CommandQueue.Callbacks, mCommandQueue.removeCallback(this); mPluginManager.removePluginListener(this); if (mPlugin != null) mPlugin.onDestroy(); - if (mGlobalActions != null) { - mGlobalActions.destroy(); - mGlobalActions = null; + if (mGlobalActionsDialog != null) { + mGlobalActionsDialog.destroy(); + mGlobalActionsDialog = null; } } @Override public void showGlobalActions(GlobalActionsManager manager) { if (mDisabled) return; - if (mGlobalActions == null) { - mGlobalActions = new GlobalActionsDialog(mContext, manager); - } - mGlobalActions.showDialog(mKeyguardStateController.isShowing(), + mGlobalActionsDialog = mGlobalActionsDialogLazy.get(); + mGlobalActionsDialog.showDialog(mKeyguardStateController.isShowing(), mDeviceProvisionedController.isDeviceProvisioned(), mPlugin != null ? mPlugin : mPanelExtension.get()); Dependency.get(KeyguardUpdateMonitor.class).requestFaceAuth(); @@ -189,8 +195,8 @@ public class GlobalActionsImpl implements GlobalActions, CommandQueue.Callbacks, final boolean disabled = (state2 & DISABLE2_GLOBAL_ACTIONS) != 0; if (displayId != mContext.getDisplayId() || disabled == mDisabled) return; mDisabled = disabled; - if (disabled && mGlobalActions != null) { - mGlobalActions.dismissDialog(); + if (disabled && mGlobalActionsDialog != null) { + mGlobalActionsDialog.dismissDialog(); } } diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/StatusBarIconView.java b/packages/SystemUI/src/com/android/systemui/statusbar/StatusBarIconView.java index dc84b5785ad9..9c626f7b877d 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/StatusBarIconView.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/StatusBarIconView.java @@ -307,6 +307,7 @@ public class StatusBarIconView extends AnimatedImageView implements StatusIconDi case Icon.TYPE_RESOURCE: return a.getResPackage().equals(b.getResPackage()) && a.getResId() == b.getResId(); case Icon.TYPE_URI: + case Icon.TYPE_URI_ADAPTIVE_BITMAP: return a.getUriString().equals(b.getUriString()); default: return false; diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationEntryManager.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationEntryManager.java index a2578ab3cfa6..36dcaac15a5f 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationEntryManager.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationEntryManager.java @@ -32,7 +32,6 @@ import android.util.Log; import com.android.internal.annotations.VisibleForTesting; import com.android.internal.statusbar.NotificationVisibility; -import com.android.systemui.Dependency; import com.android.systemui.Dumpable; import com.android.systemui.statusbar.FeatureFlags; import com.android.systemui.statusbar.NotificationLifetimeExtender; @@ -68,6 +67,8 @@ import java.util.Set; import javax.inject.Inject; import javax.inject.Singleton; +import dagger.Lazy; + /** * NotificationEntryManager is responsible for the adding, removing, and updating of * {@link NotificationEntry}s. It also handles tasks such as their inflation and their interaction @@ -126,8 +127,9 @@ public class NotificationEntryManager implements new ArrayMap<>(); // Lazily retrieved dependencies - private NotificationRemoteInputManager mRemoteInputManager; - private NotificationRowBinder mNotificationRowBinder; + private final Lazy<NotificationRowBinder> mNotificationRowBinderLazy; + private final Lazy<NotificationRemoteInputManager> mRemoteInputManagerLazy; + private final LeakDetector mLeakDetector; private final KeyguardEnvironment mKeyguardEnvironment; private final NotificationGroupManager mGroupManager; @@ -173,12 +175,18 @@ public class NotificationEntryManager implements NotificationGroupManager groupManager, NotificationRankingManager rankingManager, KeyguardEnvironment keyguardEnvironment, - FeatureFlags featureFlags) { + FeatureFlags featureFlags, + Lazy<NotificationRowBinder> notificationRowBinderLazy, + Lazy<NotificationRemoteInputManager> notificationRemoteInputManagerLazy, + LeakDetector leakDetector) { mNotifLog = notifLog; mGroupManager = groupManager; mRankingManager = rankingManager; mKeyguardEnvironment = keyguardEnvironment; mFeatureFlags = featureFlags; + mNotificationRowBinderLazy = notificationRowBinderLazy; + mRemoteInputManagerLazy = notificationRemoteInputManagerLazy; + mLeakDetector = leakDetector; } /** Once called, the NEM will start processing notification events from system server. */ @@ -204,20 +212,6 @@ public class NotificationEntryManager implements mRemoveInterceptor = interceptor; } - /** - * Our dependencies can have cyclic references, so some need to be lazy - */ - private NotificationRemoteInputManager getRemoteInputManager() { - if (mRemoteInputManager == null) { - mRemoteInputManager = Dependency.get(NotificationRemoteInputManager.class); - } - return mRemoteInputManager; - } - - public void setRowBinder(NotificationRowBinder notificationRowBinder) { - mNotificationRowBinder = notificationRowBinder; - } - public void setUpWithPresenter(NotificationPresenter presenter, NotificationListContainer listContainer, HeadsUpManager headsUpManager) { @@ -468,7 +462,7 @@ public class NotificationEntryManager implements handleGroupSummaryRemoved(key); removeVisibleNotification(key); updateNotifications("removeNotificationInternal"); - Dependency.get(LeakDetector.class).trackGarbage(entry); + mLeakDetector.trackGarbage(entry); removedByUser |= entryDismissed; mNotifLog.log(NotifEvent.NOTIF_REMOVED, entry.getSbn(), @@ -507,8 +501,8 @@ public class NotificationEntryManager implements boolean isForeground = (entry.getSbn().getNotification().flags & Notification.FLAG_FOREGROUND_SERVICE) != 0; boolean keepForReply = - getRemoteInputManager().shouldKeepForRemoteInputHistory(childEntry) - || getRemoteInputManager().shouldKeepForSmartReplyHistory(childEntry); + mRemoteInputManagerLazy.get().shouldKeepForRemoteInputHistory(childEntry) + || mRemoteInputManagerLazy.get().shouldKeepForSmartReplyHistory(childEntry); if (isForeground || keepForReply) { // the child is a foreground service notification which we can't remove or it's // a child we're keeping around for reply! @@ -536,12 +530,13 @@ public class NotificationEntryManager implements NotificationEntry entry = new NotificationEntry(notification, ranking); - Dependency.get(LeakDetector.class).trackInstance(entry); + mLeakDetector.trackInstance(entry); // Construct the expanded view. if (!mFeatureFlags.isNewNotifPipelineRenderingEnabled()) { - requireBinder().inflateViews(entry, () -> performRemoveNotification(notification, - REASON_CANCEL)); + mNotificationRowBinderLazy.get() + .inflateViews(entry, () -> performRemoveNotification(notification, + REASON_CANCEL)); } abortExistingInflation(key, "addNotification"); @@ -586,15 +581,16 @@ public class NotificationEntryManager implements } if (!mFeatureFlags.isNewNotifPipelineRenderingEnabled()) { - requireBinder().inflateViews(entry, () -> performRemoveNotification(notification, - REASON_CANCEL)); + mNotificationRowBinderLazy.get() + .inflateViews(entry, () -> performRemoveNotification(notification, + REASON_CANCEL)); } updateNotifications("updateNotificationInternal"); if (DEBUG) { // Is this for you? - boolean isForCurrentUser = Dependency.get(KeyguardEnvironment.class) + boolean isForCurrentUser = mKeyguardEnvironment .isNotificationForCurrentProfiles(notification); Log.d(TAG, "notification is " + (isForCurrentUser ? "" : "not ") + "for you"); } @@ -644,11 +640,12 @@ public class NotificationEntryManager implements // By comparing the old and new UI adjustments, reinflate the view accordingly. for (NotificationEntry entry : entries) { - requireBinder().onNotificationRankingUpdated( - entry, - oldImportances.get(entry.getKey()), - oldAdjustments.get(entry.getKey()), - NotificationUiAdjustment.extractFromNotificationEntry(entry)); + mNotificationRowBinderLazy.get() + .onNotificationRankingUpdated( + entry, + oldImportances.get(entry.getKey()), + oldAdjustments.get(entry.getKey()), + NotificationUiAdjustment.extractFromNotificationEntry(entry)); } updateNotifications("updateNotificationRanking"); @@ -728,14 +725,6 @@ public class NotificationEntryManager implements } } - private NotificationRowBinder requireBinder() { - if (mNotificationRowBinder == null) { - throw new RuntimeException("You must initialize NotificationEntryManager by calling" - + "setRowBinder() before using."); - } - return mNotificationRowBinder; - } - /* * ----- * Annexed from NotificationData below: diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/NotificationRowBinderImpl.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/NotificationRowBinderImpl.java index 6dc647d33046..6a2774b1842d 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/NotificationRowBinderImpl.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/NotificationRowBinderImpl.java @@ -16,6 +16,7 @@ package com.android.systemui.statusbar.notification.collection; +import static com.android.systemui.Dependency.ALLOW_NOTIFICATION_LONG_PRESS_NAME; import static com.android.systemui.statusbar.NotificationRemoteInputManager.ENABLE_REMOTE_INPUT; import static com.android.systemui.statusbar.notification.row.NotificationRowContentBinder.FLAG_CONTENT_VIEW_HEADS_UP; @@ -29,7 +30,6 @@ import android.util.Log; import android.view.ViewGroup; import com.android.internal.util.NotificationMessagingUtil; -import com.android.systemui.Dependency; import com.android.systemui.R; import com.android.systemui.plugins.statusbar.StatusBarStateController; import com.android.systemui.statusbar.NotificationLockscreenUserManager; @@ -52,27 +52,30 @@ import com.android.systemui.statusbar.policy.HeadsUpManager; import java.util.Objects; +import javax.inject.Inject; +import javax.inject.Named; +import javax.inject.Singleton; + /** Handles inflating and updating views for notifications. */ +@Singleton public class NotificationRowBinderImpl implements NotificationRowBinder { private static final String TAG = "NotificationViewManager"; - private final NotificationGroupManager mGroupManager = - Dependency.get(NotificationGroupManager.class); - private final NotificationGutsManager mGutsManager = - Dependency.get(NotificationGutsManager.class); - private final NotificationInterruptionStateProvider mNotificationInterruptionStateProvider = - Dependency.get(NotificationInterruptionStateProvider.class); + private final NotificationGroupManager mGroupManager; + private final NotificationGutsManager mGutsManager; + private final NotificationInterruptionStateProvider mNotificationInterruptionStateProvider; private final Context mContext; private final NotificationMessagingUtil mMessagingUtil; private final ExpandableNotificationRow.ExpansionLogger mExpansionLogger = this::logNotificationExpansion; + private final NotificationRemoteInputManager mNotificationRemoteInputManager; + private final NotificationLockscreenUserManager mNotificationLockscreenUserManager; private final boolean mAllowLongPress; private final KeyguardBypassController mKeyguardBypassController; private final StatusBarStateController mStatusBarStateController; - private NotificationRemoteInputManager mRemoteInputManager; private NotificationPresenter mPresenter; private NotificationListContainer mListContainer; private HeadsUpManager mHeadsUpManager; @@ -82,27 +85,31 @@ public class NotificationRowBinderImpl implements NotificationRowBinder { private NotificationClicker mNotificationClicker; private final NotificationLogger mNotificationLogger; + @Inject public NotificationRowBinderImpl( Context context, - boolean allowLongPress, + NotificationRemoteInputManager notificationRemoteInputManager, + NotificationLockscreenUserManager notificationLockscreenUserManager, + @Named(ALLOW_NOTIFICATION_LONG_PRESS_NAME) boolean allowLongPress, KeyguardBypassController keyguardBypassController, StatusBarStateController statusBarStateController, + NotificationGroupManager notificationGroupManager, + NotificationGutsManager notificationGutsManager, + NotificationInterruptionStateProvider notificationInterruptionStateProvider, NotificationLogger logger) { mContext = context; mMessagingUtil = new NotificationMessagingUtil(context); + mNotificationRemoteInputManager = notificationRemoteInputManager; + mNotificationLockscreenUserManager = notificationLockscreenUserManager; mAllowLongPress = allowLongPress; mKeyguardBypassController = keyguardBypassController; mStatusBarStateController = statusBarStateController; + mGroupManager = notificationGroupManager; + mGutsManager = notificationGutsManager; + mNotificationInterruptionStateProvider = notificationInterruptionStateProvider; mNotificationLogger = logger; } - private NotificationRemoteInputManager getRemoteInputManager() { - if (mRemoteInputManager == null) { - mRemoteInputManager = Dependency.get(NotificationRemoteInputManager.class); - } - return mRemoteInputManager; - } - /** * Sets up late-bound dependencies for this component. */ @@ -167,7 +174,7 @@ public class NotificationRowBinderImpl implements NotificationRowBinder { row.setLongPressListener(mGutsManager::openGuts); } mListContainer.bindRow(row); - getRemoteInputManager().bindRow(row); + mNotificationRemoteInputManager.bindRow(row); // Get the app name. // Note that Notification.Builder#bindHeaderAppName has similar logic @@ -263,8 +270,7 @@ public class NotificationRowBinderImpl implements NotificationRowBinder { if (mNotificationInterruptionStateProvider.shouldHeadsUp(entry)) { row.setInflationFlags(FLAG_CONTENT_VIEW_HEADS_UP); } - row.setNeedsRedaction( - Dependency.get(NotificationLockscreenUserManager.class).needsRedaction(entry)); + row.setNeedsRedaction(mNotificationLockscreenUserManager.needsRedaction(entry)); row.inflateViews(); // bind the click event to the content area diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/NotifCoordinators.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/NotifCoordinators.java index eeb54abf92b4..2436bb9f82f0 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/NotifCoordinators.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/NotifCoordinators.java @@ -76,6 +76,7 @@ public class NotifCoordinators implements Dumpable { @Override public void dump(FileDescriptor fd, PrintWriter pw, String[] args) { + pw.println(); pw.println(TAG + ":"); for (Coordinator c : mCoordinators) { pw.println("\t" + c.getClass()); diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/notifcollection/GroupCoalescer.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/notifcollection/GroupCoalescer.java index 069c15f9d7a2..c3e3c5373b7b 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/notifcollection/GroupCoalescer.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/notifcollection/GroupCoalescer.java @@ -259,7 +259,7 @@ public class GroupCoalescer implements Dumpable { pw.println("Coalesced notifications:"); for (EventBatch batch : mBatches.values()) { pw.println(" Batch " + batch.mGroupKey + ":"); - pw.println(" Created" + (now - batch.mCreatedTimestamp) + "ms ago"); + pw.println(" Created " + (now - batch.mCreatedTimestamp) + "ms ago"); for (CoalescedEvent event : batch.mMembers) { pw.println(" " + event.getKey()); eventCount++; @@ -299,5 +299,5 @@ public class GroupCoalescer implements Dumpable { void onNotificationBatchPosted(List<CoalescedEvent> events); } - private static final int GROUP_LINGER_DURATION = 40; + private static final int GROUP_LINGER_DURATION = 500; } diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBar.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBar.java index ccc86b1f8c5f..189d3b6f530b 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBar.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBar.java @@ -29,7 +29,6 @@ import static android.view.InsetsState.containsType; import static android.view.WindowInsetsController.APPEARANCE_LOW_PROFILE_BARS; import static android.view.WindowInsetsController.APPEARANCE_OPAQUE_STATUS_BARS; -import static com.android.systemui.Dependency.ALLOW_NOTIFICATION_LONG_PRESS_NAME; import static com.android.systemui.Dependency.TIME_TICK_HANDLER_NAME; import static com.android.systemui.keyguard.WakefulnessLifecycle.WAKEFULNESS_ASLEEP; import static com.android.systemui.keyguard.WakefulnessLifecycle.WAKEFULNESS_AWAKE; @@ -365,7 +364,6 @@ public class StatusBar extends SystemUI implements DemoMode, private final HeadsUpManagerPhone mHeadsUpManager; private final DynamicPrivacyController mDynamicPrivacyController; private final BypassHeadsUpNotifier mBypassHeadsUpNotifier; - private final boolean mAllowNotificationLongPress; private final Lazy<NewNotifPipeline> mNewNotifPipeline; private final FalsingManager mFalsingManager; private final BroadcastDispatcher mBroadcastDispatcher; @@ -388,6 +386,7 @@ public class StatusBar extends SystemUI implements DemoMode, private final KeyguardDismissUtil mKeyguardDismissUtil; private final ExtensionController mExtensionController; private final UserInfoControllerImpl mUserInfoControllerImpl; + private final NotificationRowBinderImpl mNotificationRowBinder; private final DismissCallbackRegistry mDismissCallbackRegistry; // expanded notifications @@ -626,7 +625,6 @@ public class StatusBar extends SystemUI implements DemoMode, HeadsUpManagerPhone headsUpManagerPhone, DynamicPrivacyController dynamicPrivacyController, BypassHeadsUpNotifier bypassHeadsUpNotifier, - @Named(ALLOW_NOTIFICATION_LONG_PRESS_NAME) boolean allowNotificationLongPress, Lazy<NewNotifPipeline> newNotifPipeline, FalsingManager falsingManager, BroadcastDispatcher broadcastDispatcher, @@ -693,6 +691,7 @@ public class StatusBar extends SystemUI implements DemoMode, KeyguardDismissUtil keyguardDismissUtil, ExtensionController extensionController, UserInfoControllerImpl userInfoControllerImpl, + NotificationRowBinderImpl notificationRowBinder, DismissCallbackRegistry dismissCallbackRegistry) { super(context); mFeatureFlags = featureFlags; @@ -707,7 +706,6 @@ public class StatusBar extends SystemUI implements DemoMode, mHeadsUpManager = headsUpManagerPhone; mDynamicPrivacyController = dynamicPrivacyController; mBypassHeadsUpNotifier = bypassHeadsUpNotifier; - mAllowNotificationLongPress = allowNotificationLongPress; mNewNotifPipeline = newNotifPipeline; mFalsingManager = falsingManager; mBroadcastDispatcher = broadcastDispatcher; @@ -772,6 +770,7 @@ public class StatusBar extends SystemUI implements DemoMode, mKeyguardDismissUtil = keyguardDismissUtil; mExtensionController = extensionController; mUserInfoControllerImpl = userInfoControllerImpl; + mNotificationRowBinder = notificationRowBinder; mDismissCallbackRegistry = dismissCallbackRegistry; mBubbleExpandListener = @@ -1237,19 +1236,11 @@ public class StatusBar extends SystemUI implements DemoMode, mStatusBarWindowViewController, this, mNotificationPanelViewController, (NotificationListContainer) mStackScroller); - final NotificationRowBinderImpl rowBinder = - new NotificationRowBinderImpl( - mContext, - mAllowNotificationLongPress, - mKeyguardBypassController, - mStatusBarStateController, - mNotificationLogger); - // TODO: inject this. mPresenter = new StatusBarNotificationPresenter(mContext, mNotificationPanelViewController, mHeadsUpManager, mStatusBarWindow, mStackScroller, mDozeScrimController, mScrimController, mActivityLaunchAnimator, mDynamicPrivacyController, - mNotificationAlertingManager, rowBinder, mKeyguardStateController, + mNotificationAlertingManager, mNotificationRowBinder, mKeyguardStateController, mKeyguardIndicationController, this /* statusBar */, mShadeController, mCommandQueue, mInitController); @@ -1273,20 +1264,19 @@ public class StatusBar extends SystemUI implements DemoMode, mGutsManager.setNotificationActivityStarter(mNotificationActivityStarter); if (!mFeatureFlags.isNewNotifPipelineRenderingEnabled()) { - mEntryManager.setRowBinder(rowBinder); - rowBinder.setInflationCallback(mEntryManager); + mNotificationRowBinder.setInflationCallback(mEntryManager); } mRemoteInputUriController.attach(mEntryManager); - rowBinder.setNotificationClicker(new NotificationClicker( + mNotificationRowBinder.setNotificationClicker(new NotificationClicker( Optional.of(this), mBubbleController, mNotificationActivityStarter)); mGroupAlertTransferHelper.bind(mEntryManager, mGroupManager); mNotificationListController.bind(); if (mFeatureFlags.isNewNotifPipelineEnabled()) { - mNewNotifPipeline.get().initialize(mNotificationListener, rowBinder); + mNewNotifPipeline.get().initialize(mNotificationListener, mNotificationRowBinder); } mEntryManager.attach(mNotificationListener); } diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarModule.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarModule.java index 153ca22933a9..be7f0a0c36f2 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarModule.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarModule.java @@ -16,7 +16,6 @@ package com.android.systemui.statusbar.phone; -import static com.android.systemui.Dependency.ALLOW_NOTIFICATION_LONG_PRESS_NAME; import static com.android.systemui.Dependency.TIME_TICK_HANDLER_NAME; import android.content.Context; @@ -66,6 +65,7 @@ import com.android.systemui.statusbar.notification.NotificationEntryManager; import com.android.systemui.statusbar.notification.NotificationInterruptionStateProvider; import com.android.systemui.statusbar.notification.NotificationWakeUpCoordinator; import com.android.systemui.statusbar.notification.VisualStabilityManager; +import com.android.systemui.statusbar.notification.collection.NotificationRowBinderImpl; import com.android.systemui.statusbar.notification.collection.init.NewNotifPipeline; import com.android.systemui.statusbar.notification.logging.NotificationLogger; import com.android.systemui.statusbar.notification.row.NotificationGutsManager; @@ -117,7 +117,6 @@ public class StatusBarModule { HeadsUpManagerPhone headsUpManagerPhone, DynamicPrivacyController dynamicPrivacyController, BypassHeadsUpNotifier bypassHeadsUpNotifier, - @Named(ALLOW_NOTIFICATION_LONG_PRESS_NAME) boolean allowNotificationLongPress, Lazy<NewNotifPipeline> newNotifPipeline, FalsingManager falsingManager, BroadcastDispatcher broadcastDispatcher, @@ -184,6 +183,7 @@ public class StatusBarModule { KeyguardDismissUtil keyguardDismissUtil, ExtensionController extensionController, UserInfoControllerImpl userInfoControllerImpl, + NotificationRowBinderImpl notificationRowBinder, DismissCallbackRegistry dismissCallbackRegistry) { return new StatusBar( context, @@ -199,7 +199,6 @@ public class StatusBarModule { headsUpManagerPhone, dynamicPrivacyController, bypassHeadsUpNotifier, - allowNotificationLongPress, newNotifPipeline, falsingManager, broadcastDispatcher, @@ -265,6 +264,7 @@ public class StatusBarModule { keyguardDismissUtil, extensionController, userInfoControllerImpl, + notificationRowBinder, dismissCallbackRegistry); } } diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/MobileSignalController.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/MobileSignalController.java index 6b842d5fa0b3..5916180d75ce 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/MobileSignalController.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/MobileSignalController.java @@ -654,7 +654,7 @@ public class MobileSignalController extends SignalController< } boolean isDataDisabled() { - return !mPhone.isDataCapable(); + return !mPhone.isDataConnectionEnabled(); } @VisibleForTesting diff --git a/packages/SystemUI/src/com/android/systemui/util/concurrency/ConcurrencyModule.java b/packages/SystemUI/src/com/android/systemui/util/concurrency/ConcurrencyModule.java index 7cdba8607d86..cc6d607a60cf 100644 --- a/packages/SystemUI/src/com/android/systemui/util/concurrency/ConcurrencyModule.java +++ b/packages/SystemUI/src/com/android/systemui/util/concurrency/ConcurrencyModule.java @@ -83,17 +83,19 @@ public abstract class ConcurrencyModule { * Provide a Background-Thread Executor by default. */ @Provides + @Singleton public static Executor provideExecutor(@Background Looper looper) { - return new ExecutorImpl(new Handler(looper)); + return new ExecutorImpl(looper); } /** * Provide a Background-Thread Executor. */ @Provides + @Singleton @Background public static Executor provideBackgroundExecutor(@Background Looper looper) { - return new ExecutorImpl(new Handler(looper)); + return new ExecutorImpl(looper); } /** @@ -109,26 +111,29 @@ public abstract class ConcurrencyModule { * Provide a Background-Thread Executor by default. */ @Provides + @Singleton public static DelayableExecutor provideDelayableExecutor(@Background Looper looper) { - return new ExecutorImpl(new Handler(looper)); + return new ExecutorImpl(looper); } /** * Provide a Background-Thread Executor. */ @Provides + @Singleton @Background public static DelayableExecutor provideBackgroundDelayableExecutor(@Background Looper looper) { - return new ExecutorImpl(new Handler(looper)); + return new ExecutorImpl(looper); } /** * Provide a Main-Thread Executor. */ @Provides + @Singleton @Main public static DelayableExecutor provideMainDelayableExecutor(@Main Looper looper) { - return new ExecutorImpl(new Handler(looper)); + return new ExecutorImpl(looper); } /** diff --git a/packages/SystemUI/src/com/android/systemui/util/concurrency/ExecutorImpl.java b/packages/SystemUI/src/com/android/systemui/util/concurrency/ExecutorImpl.java index 7e7732135e3a..2bbf9507122a 100644 --- a/packages/SystemUI/src/com/android/systemui/util/concurrency/ExecutorImpl.java +++ b/packages/SystemUI/src/com/android/systemui/util/concurrency/ExecutorImpl.java @@ -17,37 +17,69 @@ package com.android.systemui.util.concurrency; import android.os.Handler; -import android.os.HandlerExecutor; +import android.os.Looper; import android.os.Message; +import java.util.concurrent.RejectedExecutionException; import java.util.concurrent.TimeUnit; /** * Implementations of {@link DelayableExecutor} for SystemUI. */ -public class ExecutorImpl extends HandlerExecutor implements DelayableExecutor { +public class ExecutorImpl implements DelayableExecutor { private final Handler mHandler; - public ExecutorImpl(Handler handler) { - super(handler); - mHandler = handler; + ExecutorImpl(Looper looper) { + mHandler = new Handler(looper, this::onHandleMessage); + } + + @Override + public void execute(Runnable command) { + if (!mHandler.post(command)) { + throw new RejectedExecutionException(mHandler + " is shutting down"); + } } @Override public Runnable executeDelayed(Runnable r, long delay, TimeUnit unit) { - Object token = new Object(); - Message m = mHandler.obtainMessage(0, token); + ExecutionToken token = new ExecutionToken(r); + Message m = mHandler.obtainMessage(MSG_EXECUTE_RUNNABLE, token); mHandler.sendMessageDelayed(m, unit.toMillis(delay)); - return () -> mHandler.removeCallbacksAndMessages(token); + return token; } @Override public Runnable executeAtTime(Runnable r, long uptimeMillis, TimeUnit unit) { - Object token = new Object(); - Message m = mHandler.obtainMessage(0, token); + ExecutionToken token = new ExecutionToken(r); + Message m = mHandler.obtainMessage(MSG_EXECUTE_RUNNABLE, token); mHandler.sendMessageAtTime(m, unit.toMillis(uptimeMillis)); - return () -> mHandler.removeCallbacksAndMessages(token); + return token; + } + + private boolean onHandleMessage(Message msg) { + if (msg.what == MSG_EXECUTE_RUNNABLE) { + ExecutionToken token = (ExecutionToken) msg.obj; + token.runnable.run(); + } else { + throw new IllegalStateException("Unrecognized message: " + msg.what); + } + return true; } + + private class ExecutionToken implements Runnable { + public final Runnable runnable; + + private ExecutionToken(Runnable runnable) { + this.runnable = runnable; + } + + @Override + public void run() { + mHandler.removeCallbacksAndMessages(this); + } + } + + private static final int MSG_EXECUTE_RUNNABLE = 0; } diff --git a/packages/SystemUI/tests/src/com/android/systemui/TestableDependency.java b/packages/SystemUI/tests/src/com/android/systemui/TestableDependency.java index 0c53b03de4d2..2ecc8ea8400c 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/TestableDependency.java +++ b/packages/SystemUI/tests/src/com/android/systemui/TestableDependency.java @@ -52,6 +52,7 @@ public class TestableDependency extends Dependency { @Override protected <T> T createDependency(Object key) { if (mObjs.containsKey(key)) return (T) mObjs.get(key); + mInstantiatedObjects.add(key); return super.createDependency(key); } diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/NotificationEntryManagerTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/NotificationEntryManagerTest.java index cd33cf922482..a60fd52b7c7f 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/NotificationEntryManagerTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/NotificationEntryManagerTest.java @@ -58,16 +58,13 @@ import android.widget.FrameLayout; import androidx.annotation.NonNull; import androidx.test.filters.SmallTest; -import com.android.internal.logging.MetricsLogger; import com.android.internal.statusbar.NotificationVisibility; import com.android.systemui.Dependency; -import com.android.systemui.ForegroundServiceController; import com.android.systemui.R; import com.android.systemui.SysuiTestCase; import com.android.systemui.plugins.statusbar.StatusBarStateController; import com.android.systemui.statusbar.FeatureFlags; import com.android.systemui.statusbar.NotificationLifetimeExtender; -import com.android.systemui.statusbar.NotificationListener; import com.android.systemui.statusbar.NotificationLockscreenUserManager; import com.android.systemui.statusbar.NotificationMediaManager; import com.android.systemui.statusbar.NotificationPresenter; @@ -81,7 +78,6 @@ import com.android.systemui.statusbar.notification.NotificationEntryManager.Keyg import com.android.systemui.statusbar.notification.collection.NotificationEntry; import com.android.systemui.statusbar.notification.collection.NotificationEntryBuilder; import com.android.systemui.statusbar.notification.collection.NotificationRankingManager; -import com.android.systemui.statusbar.notification.collection.NotificationRowBinder; import com.android.systemui.statusbar.notification.collection.NotificationRowBinderImpl; import com.android.systemui.statusbar.notification.collection.provider.HighPriorityProvider; import com.android.systemui.statusbar.notification.logging.NotifLog; @@ -93,10 +89,10 @@ import com.android.systemui.statusbar.notification.row.RowInflaterTask; import com.android.systemui.statusbar.notification.stack.NotificationListContainer; import com.android.systemui.statusbar.phone.KeyguardBypassController; import com.android.systemui.statusbar.phone.NotificationGroupManager; -import com.android.systemui.statusbar.phone.ShadeController; import com.android.systemui.statusbar.policy.DeviceProvisionedController; import com.android.systemui.statusbar.policy.HeadsUpManager; import com.android.systemui.util.Assert; +import com.android.systemui.util.leak.LeakDetector; import org.junit.Before; import org.junit.Test; @@ -130,22 +126,16 @@ public class NotificationEntryManagerTest extends SysuiTestCase { @Mock private HeadsUpManager mHeadsUpManager; @Mock private RankingMap mRankingMap; @Mock private RemoteInputController mRemoteInputController; - - // Dependency mocks: - @Mock private ForegroundServiceController mForegroundServiceController; + @Mock private NotificationInterruptionStateProvider mNotificationInterruptionStateProvider; @Mock private NotificationLockscreenUserManager mLockscreenUserManager; @Mock private NotificationGroupManager mGroupManager; @Mock private NotificationGutsManager mGutsManager; @Mock private NotificationRemoteInputManager mRemoteInputManager; - @Mock private NotificationListener mNotificationListener; @Mock private DeviceProvisionedController mDeviceProvisionedController; - @Mock private VisualStabilityManager mVisualStabilityManager; - @Mock private MetricsLogger mMetricsLogger; - @Mock private SmartReplyController mSmartReplyController; @Mock private RowInflaterTask mAsyncInflationTask; - @Mock private NotificationRowBinder mMockedRowBinder; @Mock private NotifLog mNotifLog; @Mock private FeatureFlags mFeatureFlags; + @Mock private LeakDetector mLeakDetector; private int mId; private NotificationEntry mEntry; @@ -192,21 +182,9 @@ public class NotificationEntryManagerTest extends SysuiTestCase { @Before public void setUp() { MockitoAnnotations.initMocks(this); - mDependency.injectMockDependency(ShadeController.class); - mDependency.injectTestDependency(ForegroundServiceController.class, - mForegroundServiceController); - mDependency.injectTestDependency(NotificationLockscreenUserManager.class, - mLockscreenUserManager); - mDependency.injectTestDependency(NotificationGroupManager.class, mGroupManager); - mDependency.injectTestDependency(NotificationGutsManager.class, mGutsManager); - mDependency.injectTestDependency(NotificationRemoteInputManager.class, mRemoteInputManager); - mDependency.injectTestDependency(NotificationListener.class, mNotificationListener); - mDependency.injectTestDependency(DeviceProvisionedController.class, - mDeviceProvisionedController); - mDependency.injectTestDependency(VisualStabilityManager.class, mVisualStabilityManager); - mDependency.injectTestDependency(MetricsLogger.class, mMetricsLogger); - mDependency.injectTestDependency(SmartReplyController.class, mSmartReplyController); - mDependency.injectTestDependency(KeyguardEnvironment.class, mEnvironment); + if (!mDependency.hasInstantiatedDependency(SmartReplyController.class)) { + mDependency.injectMockDependency(SmartReplyController.class); + } mDependency.injectMockDependency(NotificationMediaManager.class); mCountDownLatch = new CountDownLatch(1); @@ -222,6 +200,17 @@ public class NotificationEntryManagerTest extends SysuiTestCase { mEntry.expandedIcon = mock(StatusBarIconView.class); + NotificationRowBinderImpl notificationRowBinder = + new NotificationRowBinderImpl(mContext, + mRemoteInputManager, mLockscreenUserManager, + true, /* allowLongPress */ + mock(KeyguardBypassController.class), + mock(StatusBarStateController.class), + mGroupManager, + mGutsManager, + mNotificationInterruptionStateProvider, + mock(NotificationLogger.class)); + when(mFeatureFlags.isNewNotifPipelineEnabled()).thenReturn(false); when(mFeatureFlags.isNewNotifPipelineRenderingEnabled()).thenReturn(false); mEntryManager = new TestableNotificationEntryManager( @@ -237,22 +226,19 @@ public class NotificationEntryManagerTest extends SysuiTestCase { mock(PeopleNotificationIdentifier.class), mock(HighPriorityProvider.class)), mEnvironment, - mFeatureFlags + mFeatureFlags, + () -> notificationRowBinder, + () -> mRemoteInputManager, + mLeakDetector ); mEntryManager.setUpWithPresenter(mPresenter, mListContainer, mHeadsUpManager); mEntryManager.addNotificationEntryListener(mEntryListener); mEntryManager.setNotificationRemoveInterceptor(mRemoveInterceptor); - NotificationRowBinderImpl notificationRowBinder = - new NotificationRowBinderImpl(mContext, true, /* allowLongPress */ - mock(KeyguardBypassController.class), - mock(StatusBarStateController.class), - mock(NotificationLogger.class)); notificationRowBinder.setUpWithPresenter( mPresenter, mListContainer, mHeadsUpManager, mBindCallback); notificationRowBinder.setInflationCallback(mEntryManager); notificationRowBinder.setNotificationClicker(mock(NotificationClicker.class)); - mEntryManager.setRowBinder(notificationRowBinder); setUserSentiment( mEntry.getKey(), Ranking.USER_SENTIMENT_NEUTRAL); @@ -372,9 +358,6 @@ public class NotificationEntryManagerTest extends SysuiTestCase { @Test public void testRemoveNotification_whilePending() { - - mEntryManager.setRowBinder(mMockedRowBinder); - mEntryManager.addNotification(mSbn, mRankingMap); mEntryManager.removeNotification(mSbn.getKey(), mRankingMap, UNDEFINED_DISMISS_REASON); @@ -447,7 +430,6 @@ public class NotificationEntryManagerTest extends SysuiTestCase { @Test public void testLifetimeExtenders_ifNotificationIsRetainedItIsntRemoved() { // GIVEN an entry manager with a notification - mEntryManager.setRowBinder(mMockedRowBinder); mEntryManager.addActiveNotificationForTest(mEntry); // GIVEN a lifetime extender that always tries to extend lifetime @@ -471,7 +453,6 @@ public class NotificationEntryManagerTest extends SysuiTestCase { Assert.sMainLooper = TestableLooper.get(this).getLooper(); // GIVEN an entry manager with a notification whose life has been extended - mEntryManager.setRowBinder(mMockedRowBinder); mEntryManager.addActiveNotificationForTest(mEntry); final FakeNotificationLifetimeExtender extender = new FakeNotificationLifetimeExtender(); mEntryManager.addNotificationLifetimeExtender(extender); @@ -490,7 +471,6 @@ public class NotificationEntryManagerTest extends SysuiTestCase { @Test public void testLifetimeExtenders_whenNotificationUpdatedRetainersAreCanceled() { // GIVEN an entry manager with a notification whose life has been extended - mEntryManager.setRowBinder(mMockedRowBinder); mEntryManager.addActiveNotificationForTest(mEntry); NotificationLifetimeExtender extender = mock(NotificationLifetimeExtender.class); when(extender.shouldExtendLifetime(mEntry)).thenReturn(true); @@ -507,7 +487,6 @@ public class NotificationEntryManagerTest extends SysuiTestCase { @Test public void testLifetimeExtenders_whenNewExtenderTakesPrecedenceOldExtenderIsCanceled() { // GIVEN an entry manager with a notification - mEntryManager.setRowBinder(mMockedRowBinder); mEntryManager.addActiveNotificationForTest(mEntry); // GIVEN two lifetime extenders, the first which never extends and the second which @@ -546,7 +525,6 @@ public class NotificationEntryManagerTest extends SysuiTestCase { @Test public void testRemoveInterceptor_interceptsDontGetRemoved() throws InterruptedException { // GIVEN an entry manager with a notification - mEntryManager.setRowBinder(mMockedRowBinder); mEntryManager.addActiveNotificationForTest(mEntry); // GIVEN interceptor that intercepts that entry @@ -568,7 +546,6 @@ public class NotificationEntryManagerTest extends SysuiTestCase { Assert.sMainLooper = TestableLooper.get(this).getLooper(); // GIVEN an entry manager with a notification - mEntryManager.setRowBinder(mMockedRowBinder); mEntryManager.addActiveNotificationForTest(mEntry); // GIVEN interceptor that doesn't intercept diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/TestableNotificationEntryManager.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/TestableNotificationEntryManager.kt index 1afee120e495..bf84f2bc599c 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/TestableNotificationEntryManager.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/TestableNotificationEntryManager.kt @@ -18,12 +18,15 @@ package com.android.systemui.statusbar.notification import com.android.systemui.statusbar.FeatureFlags import com.android.systemui.statusbar.NotificationPresenter +import com.android.systemui.statusbar.NotificationRemoteInputManager import com.android.systemui.statusbar.notification.collection.NotificationEntry import com.android.systemui.statusbar.notification.collection.NotificationRankingManager +import com.android.systemui.statusbar.notification.collection.NotificationRowBinder import com.android.systemui.statusbar.notification.logging.NotifLog import com.android.systemui.statusbar.notification.stack.NotificationListContainer import com.android.systemui.statusbar.phone.HeadsUpManagerPhone import com.android.systemui.statusbar.phone.NotificationGroupManager +import com.android.systemui.util.leak.LeakDetector import java.util.concurrent.CountDownLatch @@ -35,8 +38,12 @@ class TestableNotificationEntryManager( gm: NotificationGroupManager, rm: NotificationRankingManager, ke: KeyguardEnvironment, - ff: FeatureFlags -) : NotificationEntryManager(log, gm, rm, ke, ff) { + ff: FeatureFlags, + rb: dagger.Lazy<NotificationRowBinder>, + notificationRemoteInputManagerLazy: dagger.Lazy<NotificationRemoteInputManager>, + leakDetector: LeakDetector +) : NotificationEntryManager(log, gm, rm, ke, ff, rb, + notificationRemoteInputManagerLazy, leakDetector) { public var countDownLatch: CountDownLatch = CountDownLatch(1) diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationContentViewTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationContentViewTest.java index 675b3efc5a26..84c651368dc9 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationContentViewTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationContentViewTest.java @@ -16,20 +16,17 @@ package com.android.systemui.statusbar.notification.row; -import static org.junit.Assert.assertFalse; import static org.mockito.ArgumentMatchers.any; import static org.mockito.ArgumentMatchers.anyFloat; import static org.mockito.Mockito.doNothing; import static org.mockito.Mockito.doReturn; import static org.mockito.Mockito.mock; -import static org.mockito.Mockito.never; import static org.mockito.Mockito.spy; import static org.mockito.Mockito.times; import static org.mockito.Mockito.verify; import static org.mockito.Mockito.when; import android.app.AppOpsManager; -import android.graphics.drawable.Icon; import android.util.ArraySet; import android.view.NotificationHeaderView; import android.view.View; diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayoutTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayoutTest.java index ea8d4ee20aab..9bd3914da3da 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayoutTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayoutTest.java @@ -71,6 +71,7 @@ import com.android.systemui.statusbar.notification.VisualStabilityManager; import com.android.systemui.statusbar.notification.collection.NotificationEntry; import com.android.systemui.statusbar.notification.collection.NotificationEntryBuilder; import com.android.systemui.statusbar.notification.collection.NotificationRankingManager; +import com.android.systemui.statusbar.notification.collection.NotificationRowBinder; import com.android.systemui.statusbar.notification.collection.provider.HighPriorityProvider; import com.android.systemui.statusbar.notification.logging.NotifLog; import com.android.systemui.statusbar.notification.people.PeopleHubSectionFooterViewAdapter; @@ -89,6 +90,7 @@ import com.android.systemui.statusbar.phone.StatusBar; import com.android.systemui.statusbar.policy.ConfigurationController; import com.android.systemui.statusbar.policy.ZenModeController; import com.android.systemui.util.DeviceConfigProxyFake; +import com.android.systemui.util.leak.LeakDetector; import org.junit.After; import org.junit.Before; @@ -170,7 +172,10 @@ public class NotificationStackScrollLayoutTest extends SysuiTestCase { mock(HighPriorityProvider.class) ), mock(NotificationEntryManager.KeyguardEnvironment.class), - mock(FeatureFlags.class)); + mock(FeatureFlags.class), + () -> mock(NotificationRowBinder.class), + () -> mRemoteInputManager, + mock(LeakDetector.class)); mDependency.injectTestDependency(NotificationEntryManager.class, mEntryManager); mEntryManager.setUpForTest(mock(NotificationPresenter.class), null, mHeadsUpManager); diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarTest.java index 7e485f45e5e6..560aadb93a14 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarTest.java @@ -120,6 +120,7 @@ import com.android.systemui.statusbar.notification.NotificationWakeUpCoordinator import com.android.systemui.statusbar.notification.VisualStabilityManager; import com.android.systemui.statusbar.notification.collection.NotificationEntry; import com.android.systemui.statusbar.notification.collection.NotificationEntryBuilder; +import com.android.systemui.statusbar.notification.collection.NotificationRowBinderImpl; import com.android.systemui.statusbar.notification.collection.init.NewNotifPipeline; import com.android.systemui.statusbar.notification.logging.NotificationLogger; import com.android.systemui.statusbar.notification.row.NotificationGutsManager; @@ -255,6 +256,7 @@ public class StatusBarTest extends SysuiTestCase { @Mock private KeyguardDismissUtil mKeyguardDismissUtil; @Mock private ExtensionController mExtensionController; @Mock private UserInfoControllerImpl mUserInfoControllerImpl; + @Mock private NotificationRowBinderImpl mNotificationRowBinder; private ShadeController mShadeController; private FakeExecutor mUiBgExecutor = new FakeExecutor(new FakeSystemClock()); private InitController mInitController = new InitController(); @@ -344,7 +346,6 @@ public class StatusBarTest extends SysuiTestCase { mHeadsUpManager, mDynamicPrivacyController, mBypassHeadsUpNotifier, - true, () -> mNewNotifPipeline, new FalsingManagerFake(), mBroadcastDispatcher, @@ -413,6 +414,7 @@ public class StatusBarTest extends SysuiTestCase { mKeyguardDismissUtil, mExtensionController, mUserInfoControllerImpl, + mNotificationRowBinder, mDismissCallbackRegistry); when(mStatusBarWindowView.findViewById(R.id.lock_icon_container)).thenReturn( diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/NetworkControllerBaseTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/NetworkControllerBaseTest.java index c4caeb3ac531..9cb06a5d85c1 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/NetworkControllerBaseTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/NetworkControllerBaseTest.java @@ -180,7 +180,7 @@ public class NetworkControllerBaseTest extends SysuiTestCase { protected void setupNetworkController() { // For now just pretend to be the data sim, so we can test that too. mSubId = SubscriptionManager.DEFAULT_SUBSCRIPTION_ID; - when(mMockTm.isDataCapable()).thenReturn(true); + when(mMockTm.isDataConnectionEnabled()).thenReturn(true); setDefaultSubId(mSubId); setSubscriptions(mSubId); mMobileSignalController = mNetworkController.mMobileSignalControllers.get(mSubId); diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/NetworkControllerDataTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/NetworkControllerDataTest.java index 95b055c7d0b5..5a5ef8bd21af 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/NetworkControllerDataTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/NetworkControllerDataTest.java @@ -121,7 +121,7 @@ public class NetworkControllerDataTest extends NetworkControllerBaseTest { @Test public void testNoInternetIcon_withDefaultSub() { setupNetworkController(); - when(mMockTm.isDataCapable()).thenReturn(false); + when(mMockTm.isDataConnectionEnabled()).thenReturn(false); setupDefaultSignal(); updateDataConnectionState(TelephonyManager.DATA_CONNECTED, 0); setConnectivityViaBroadcast(NetworkCapabilities.TRANSPORT_CELLULAR, false, false); @@ -135,7 +135,7 @@ public class NetworkControllerDataTest extends NetworkControllerBaseTest { @Test public void testDataDisabledIcon_withDefaultSub() { setupNetworkController(); - when(mMockTm.isDataCapable()).thenReturn(false); + when(mMockTm.isDataConnectionEnabled()).thenReturn(false); setupDefaultSignal(); updateDataConnectionState(TelephonyManager.DATA_DISCONNECTED, 0); setConnectivityViaBroadcast(NetworkCapabilities.TRANSPORT_CELLULAR, false, false); @@ -149,7 +149,7 @@ public class NetworkControllerDataTest extends NetworkControllerBaseTest { @Test public void testNonDefaultSIM_showsFullSignal_connected() { setupNetworkController(); - when(mMockTm.isDataCapable()).thenReturn(false); + when(mMockTm.isDataConnectionEnabled()).thenReturn(false); setupDefaultSignal(); setDefaultSubId(mSubId + 1); updateDataConnectionState(TelephonyManager.DATA_CONNECTED, 0); @@ -164,7 +164,7 @@ public class NetworkControllerDataTest extends NetworkControllerBaseTest { @Test public void testNonDefaultSIM_showsFullSignal_disconnected() { setupNetworkController(); - when(mMockTm.isDataCapable()).thenReturn(false); + when(mMockTm.isDataConnectionEnabled()).thenReturn(false); setupDefaultSignal(); setDefaultSubId(mSubId + 1); updateDataConnectionState(TelephonyManager.DATA_DISCONNECTED, 0); @@ -429,7 +429,7 @@ public class NetworkControllerDataTest extends NetworkControllerBaseTest { @Test public void testDataDisabledIcon_UserNotSetup() { setupNetworkController(); - when(mMockTm.isDataCapable()).thenReturn(false); + when(mMockTm.isDataConnectionEnabled()).thenReturn(false); setupDefaultSignal(); updateDataConnectionState(TelephonyManager.DATA_DISCONNECTED, 0); setConnectivityViaBroadcast(NetworkCapabilities.TRANSPORT_CELLULAR, false, false); @@ -444,7 +444,7 @@ public class NetworkControllerDataTest extends NetworkControllerBaseTest { @Test public void testAlwaysShowDataRatIcon() { setupDefaultSignal(); - when(mMockTm.isDataCapable()).thenReturn(false); + when(mMockTm.isDataConnectionEnabled()).thenReturn(false); updateDataConnectionState(TelephonyManager.DATA_DISCONNECTED, TelephonyManager.NETWORK_TYPE_GSM); diff --git a/packages/services/PacProcessor/jni/Android.bp b/packages/services/PacProcessor/jni/Android.bp index 2a94237f2aed..61f8143e68b5 100644 --- a/packages/services/PacProcessor/jni/Android.bp +++ b/packages/services/PacProcessor/jni/Android.bp @@ -37,4 +37,10 @@ cc_library_shared { "-Wunused", "-Wunreachable-code", ], + sanitize: { + cfi: true, + diag: { + cfi: true, + }, + }, } diff --git a/services/core/java/android/content/pm/PackageManagerInternal.java b/services/core/java/android/content/pm/PackageManagerInternal.java index 312dd46fbc73..76572d35d516 100644 --- a/services/core/java/android/content/pm/PackageManagerInternal.java +++ b/services/core/java/android/content/pm/PackageManagerInternal.java @@ -811,6 +811,12 @@ public abstract class PackageManagerInternal { public abstract boolean isApexPackage(String packageName); /** + * Returns list of {@code packageName} of apks inside the given apex. + * @param apexPackageName Package name of the apk container of apex + */ + public abstract List<String> getApksInApex(String apexPackageName); + + /** * Uninstalls given {@code packageName}. * * @param packageName apex package to uninstall. diff --git a/services/core/java/com/android/server/LocationManagerService.java b/services/core/java/com/android/server/LocationManagerService.java index 32128d5f26f8..dc393d1609de 100644 --- a/services/core/java/com/android/server/LocationManagerService.java +++ b/services/core/java/com/android/server/LocationManagerService.java @@ -69,7 +69,6 @@ import android.os.Process; import android.os.RemoteException; import android.os.SystemClock; import android.os.UserHandle; -import android.os.UserManager; import android.os.WorkSource; import android.os.WorkSource.WorkChain; import android.stats.location.LocationStatsEnums; @@ -84,7 +83,6 @@ import com.android.internal.annotations.GuardedBy; import com.android.internal.content.PackageMonitor; import com.android.internal.location.ProviderProperties; import com.android.internal.location.ProviderRequest; -import com.android.internal.util.ArrayUtils; import com.android.internal.util.DumpUtils; import com.android.internal.util.IndentingPrintWriter; import com.android.internal.util.Preconditions; @@ -105,6 +103,7 @@ import com.android.server.location.LocationUsageLogger; import com.android.server.location.MockProvider; import com.android.server.location.MockableLocationProvider; import com.android.server.location.PassiveProvider; +import com.android.server.location.UserInfoStore; import com.android.server.pm.permission.PermissionManagerServiceInternal; import java.io.ByteArrayOutputStream; @@ -194,6 +193,7 @@ public class LocationManagerService extends ILocationManager.Stub { private final Object mLock = new Object(); private final Context mContext; private final Handler mHandler; + private final UserInfoStore mUserInfoStore; private final LocationSettingsStore mSettingsStore; private final LocationUsageLogger mLocationUsageLogger; @@ -203,7 +203,6 @@ public class LocationManagerService extends ILocationManager.Stub { private PackageManager mPackageManager; private PowerManager mPowerManager; private ActivityManager mActivityManager; - private UserManager mUserManager; private GeofenceManager mGeofenceManager; private LocationFudger mLocationFudger; @@ -237,10 +236,6 @@ public class LocationManagerService extends ILocationManager.Stub { private final HashMap<String, Location> mLastLocationCoarseInterval = new HashMap<>(); - // current active user on the device - private int mCurrentUserId; - private int[] mCurrentUserProfiles; - @GuardedBy("mLock") @PowerManager.LocationPowerSaveMode private int mBatterySaverMode; @@ -248,12 +243,10 @@ public class LocationManagerService extends ILocationManager.Stub { private LocationManagerService(Context context) { mContext = context; mHandler = FgThread.getHandler(); + mUserInfoStore = new UserInfoStore(mContext); mSettingsStore = new LocationSettingsStore(mContext, mHandler); mLocationUsageLogger = new LocationUsageLogger(); - mCurrentUserId = UserHandle.USER_NULL; - mCurrentUserProfiles = new int[]{UserHandle.USER_NULL}; - // set up passive provider - we do this early because it has no dependencies on system // services or external code that isn't ready yet, and because this allows the variable to // be final. other more complex providers are initialized later, when system services are @@ -277,6 +270,7 @@ public class LocationManagerService extends ILocationManager.Stub { } private void onSystemReady() { + mUserInfoStore.onSystemReady(); mSettingsStore.onSystemReady(); synchronized (mLock) { @@ -284,7 +278,6 @@ public class LocationManagerService extends ILocationManager.Stub { mAppOps = mContext.getSystemService(AppOpsManager.class); mPowerManager = mContext.getSystemService(PowerManager.class); mActivityManager = mContext.getSystemService(ActivityManager.class); - mUserManager = mContext.getSystemService(UserManager.class); mLocationFudger = new LocationFudger(mContext, mHandler); mGeofenceManager = new GeofenceManager(mContext, mSettingsStore); @@ -372,10 +365,13 @@ public class LocationManagerService extends ILocationManager.Stub { } }.register(mContext, mHandler.getLooper(), true); + mUserInfoStore.addListener((oldUserId, newUserId) -> { + synchronized (mLock) { + onUserChangedLocked(oldUserId, newUserId); + } + }); + IntentFilter intentFilter = new IntentFilter(); - intentFilter.addAction(Intent.ACTION_USER_SWITCHED); - intentFilter.addAction(Intent.ACTION_MANAGED_PROFILE_ADDED); - intentFilter.addAction(Intent.ACTION_MANAGED_PROFILE_REMOVED); intentFilter.addAction(Intent.ACTION_SCREEN_OFF); intentFilter.addAction(Intent.ACTION_SCREEN_ON); @@ -388,14 +384,6 @@ public class LocationManagerService extends ILocationManager.Stub { } synchronized (mLock) { switch (action) { - case Intent.ACTION_USER_SWITCHED: - onUserChangedLocked( - intent.getIntExtra(Intent.EXTRA_USER_HANDLE, 0)); - break; - case Intent.ACTION_MANAGED_PROFILE_ADDED: - case Intent.ACTION_MANAGED_PROFILE_REMOVED: - onUserProfilesChangedLocked(); - break; case Intent.ACTION_SCREEN_ON: case Intent.ACTION_SCREEN_OFF: onScreenStateChangedLocked(); @@ -405,11 +393,10 @@ public class LocationManagerService extends ILocationManager.Stub { } }, UserHandle.ALL, intentFilter, null, mHandler); - // switching the user from null to system here performs the bulk of the initialization + // switching the user from null to current here performs the bulk of the initialization // work. the user being changed will cause a reload of all user specific settings, which // causes initialization, and propagates changes until a steady state is reached - mCurrentUserId = UserHandle.USER_NULL; - onUserChangedLocked(ActivityManager.getCurrentUser()); + onUserChangedLocked(UserHandle.USER_NULL, mUserInfoStore.getCurrentUserId()); } } @@ -551,16 +538,6 @@ public class LocationManagerService extends ILocationManager.Stub { } @GuardedBy("mLock") - private void onUserProfilesChangedLocked() { - mCurrentUserProfiles = mUserManager.getProfileIdsWithDisabled(mCurrentUserId); - } - - @GuardedBy("mLock") - private boolean isCurrentProfileLocked(int userId) { - return ArrayUtils.contains(mCurrentUserProfiles, userId); - } - - @GuardedBy("mLock") private void ensureFallbackFusedProviderPresentLocked(String[] pkgs) { PackageManager pm = mContext.getPackageManager(); String systemPackageName = mContext.getPackageName(); @@ -568,7 +545,7 @@ public class LocationManagerService extends ILocationManager.Stub { List<ResolveInfo> rInfos = pm.queryIntentServicesAsUser( new Intent(FUSED_LOCATION_SERVICE_ACTION), - PackageManager.GET_META_DATA, mCurrentUserId); + PackageManager.GET_META_DATA, mUserInfoStore.getCurrentUserId()); for (ResolveInfo rInfo : rInfos) { String packageName = rInfo.serviceInfo.packageName; @@ -752,27 +729,18 @@ public class LocationManagerService extends ILocationManager.Stub { } @GuardedBy("mLock") - private void onUserChangedLocked(int userId) { - if (mCurrentUserId == userId) { - return; - } - + private void onUserChangedLocked(int oldUserId, int newUserId) { if (D) { - Log.d(TAG, "foreground user is changing to " + userId); + Log.d(TAG, "foreground user is changing to " + newUserId); } - int oldUserId = mCurrentUserId; - mCurrentUserId = userId; - onUserProfilesChangedLocked(); - - // let providers know the current user has changed for (LocationProviderManager manager : mProviderManagers) { // update LOCATION_PROVIDERS_ALLOWED for best effort backwards compatibility mSettingsStore.setLocationProviderAllowed(manager.getName(), - manager.isUseable(mCurrentUserId), mCurrentUserId); + manager.isUseable(newUserId), newUserId); manager.onUseableChangedLocked(oldUserId); - manager.onUseableChangedLocked(mCurrentUserId); + manager.onUseableChangedLocked(newUserId); } } @@ -786,8 +754,12 @@ public class LocationManagerService extends ILocationManager.Stub { // acquiring mLock makes operations on mProvider atomic, but is otherwise unnecessary protected final MockableLocationProvider mProvider; + // useable state for parent user ids, no entry implies false. location state is only kept + // for parent user ids, the location state for a profile user id is assumed to be the same + // as for the parent. if querying this structure, ensure that the user id being used is a + // parent id or the results may be incorrect. @GuardedBy("mLock") - private final SparseArray<Boolean> mUseable; // combined state for each user id + private final SparseArray<Boolean> mUseable; private LocationProviderManager(String name) { mName = name; @@ -795,6 +767,9 @@ public class LocationManagerService extends ILocationManager.Stub { // initialize last since this lets our reference escape mProvider = new MockableLocationProvider(mContext, mLock, this); + + // we can assume all users start with unuseable location state since the initial state + // of all providers is disabled. no need to initialize mUseable further. } public String getName() { @@ -868,29 +843,6 @@ public class LocationManagerService extends ILocationManager.Stub { mProvider.sendExtraCommand(uid, pid, command, extras); } - public void dump(FileDescriptor fd, IndentingPrintWriter pw, String[] args) { - synchronized (mLock) { - pw.print(mName + " provider"); - if (mProvider.isMock()) { - pw.print(" [mock]"); - } - pw.println(":"); - - pw.increaseIndent(); - - pw.println("useable=" + isUseable(mCurrentUserId)); - if (!isUseable(mCurrentUserId)) { - pw.println("enabled=" + mProvider.getState().enabled); - } - - pw.println("properties=" + mProvider.getState().properties); - } - - mProvider.dump(fd, pw, args); - - pw.decreaseIndent(); - } - @GuardedBy("mLock") @Override public void onReportLocation(Location location) { @@ -927,18 +879,20 @@ public class LocationManagerService extends ILocationManager.Stub { // it would be more correct to call this for all users, but we know this can // only affect the current user since providers are disabled for non-current // users - onUseableChangedLocked(mCurrentUserId); + onUseableChangedLocked(mUserInfoStore.getCurrentUserId()); } } - @GuardedBy("mLock") public boolean isUseable() { - return isUseable(mCurrentUserId); + return isUseable(mUserInfoStore.getCurrentUserId()); } - @GuardedBy("mLock") public boolean isUseable(int userId) { synchronized (mLock) { + // normalize user id to always refer to parent since profile state is always the + // same as parent state + userId = mUserInfoStore.getParentUserId(userId); + return mUseable.get(userId, Boolean.FALSE); } } @@ -950,15 +904,20 @@ public class LocationManagerService extends ILocationManager.Stub { return; } + // normalize user id to always refer to parent since profile state is always the same + // as parent state + userId = mUserInfoStore.getParentUserId(userId); + // if any property that contributes to "useability" here changes state, it MUST result // in a direct or indrect call to onUseableChangedLocked. this allows the provider to // guarantee that it will always eventually reach the correct state. - boolean useable = isCurrentProfileLocked(userId) + boolean useable = (userId == mUserInfoStore.getCurrentUserId()) && mSettingsStore.isLocationEnabled(userId) && mProvider.getState().enabled; if (useable == isUseable(userId)) { return; } + mUseable.put(userId, useable); if (D) { @@ -986,6 +945,30 @@ public class LocationManagerService extends ILocationManager.Stub { updateProviderUseableLocked(this); } + + public void dump(FileDescriptor fd, IndentingPrintWriter pw, String[] args) { + synchronized (mLock) { + pw.print(mName + " provider"); + if (mProvider.isMock()) { + pw.print(" [mock]"); + } + pw.println(":"); + + pw.increaseIndent(); + + boolean useable = isUseable(); + pw.println("useable=" + useable); + if (!useable) { + pw.println("enabled=" + mProvider.getState().enabled); + } + + pw.println("properties=" + mProvider.getState().properties); + } + + mProvider.dump(fd, pw, args); + + pw.decreaseIndent(); + } } class PassiveLocationProviderManager extends LocationProviderManager { @@ -1626,7 +1609,7 @@ public class LocationManagerService extends ILocationManager.Stub { ArrayList<UpdateRecord> records = mRecordsByProvider.get(manager.getName()); if (records != null) { for (UpdateRecord record : records) { - if (!isCurrentProfileLocked( + if (!mUserInfoStore.isCurrentUserOrProfile( UserHandle.getUserId(record.mReceiver.mCallerIdentity.mUid))) { continue; } @@ -1691,7 +1674,7 @@ public class LocationManagerService extends ILocationManager.Stub { // initialize the low power mode to true and set to false if any of the records requires providerRequest.setLowPowerMode(true); for (UpdateRecord record : records) { - if (!isCurrentProfileLocked( + if (!mUserInfoStore.isCurrentUserOrProfile( UserHandle.getUserId(record.mReceiver.mCallerIdentity.mUid))) { continue; } @@ -1750,7 +1733,7 @@ public class LocationManagerService extends ILocationManager.Stub { // TODO: overflow long thresholdInterval = (providerRequest.getInterval() + 1000) * 3 / 2; for (UpdateRecord record : records) { - if (isCurrentProfileLocked( + if (mUserInfoStore.isCurrentUserOrProfile( UserHandle.getUserId(record.mReceiver.mCallerIdentity.mUid))) { LocationRequest locationRequest = record.mRequest; @@ -2243,8 +2226,8 @@ public class LocationManagerService extends ILocationManager.Stub { if (manager == null) return null; // only the current user or location providers may get location this way - if (!isCurrentProfileLocked(UserHandle.getUserId(uid)) && !isProviderPackage( - packageName)) { + if (!mUserInfoStore.isCurrentUserOrProfile(UserHandle.getUserId(uid)) + && !isProviderPackage(packageName)) { return null; } @@ -2773,12 +2756,11 @@ public class LocationManagerService extends ILocationManager.Stub { } int receiverUserId = UserHandle.getUserId(receiver.mCallerIdentity.mUid); - if (!isCurrentProfileLocked(receiverUserId) + if (!mUserInfoStore.isCurrentUserOrProfile(receiverUserId) && !isProviderPackage(receiver.mCallerIdentity.mPackageName)) { if (D) { Log.d(TAG, "skipping loc update for background user " + receiverUserId + - " (current user: " + mCurrentUserId + ", app: " + - receiver.mCallerIdentity.mPackageName + ")"); + " (app: " + receiver.mCallerIdentity.mPackageName + ")"); } continue; } @@ -3028,9 +3010,17 @@ public class LocationManagerService extends ILocationManager.Stub { + TimeUtils.logTimeOfDay(System.currentTimeMillis())); ipw.println(", Current Elapsed Time: " + TimeUtils.formatDuration(SystemClock.elapsedRealtime())); - ipw.println("Current user: " + mCurrentUserId + " " + Arrays.toString( - mCurrentUserProfiles)); - ipw.println("Location Mode: " + isLocationEnabledForUser(mCurrentUserId)); + + ipw.println("User Info:"); + ipw.increaseIndent(); + mUserInfoStore.dump(fd, ipw, args); + ipw.decreaseIndent(); + + ipw.println("Location Settings:"); + ipw.increaseIndent(); + mSettingsStore.dump(fd, ipw, args); + ipw.decreaseIndent(); + ipw.println("Battery Saver Location Mode: " + locationPowerSaveModeToString(mBatterySaverMode)); @@ -3096,21 +3086,14 @@ public class LocationManagerService extends ILocationManager.Stub { mLocationFudger.dump(fd, ipw, args); ipw.decreaseIndent(); } - } - - ipw.println("Location Settings:"); - ipw.increaseIndent(); - mSettingsStore.dump(fd, ipw, args); - ipw.decreaseIndent(); - ipw.println("Location Providers:"); - ipw.increaseIndent(); - for (LocationProviderManager manager : mProviderManagers) { - manager.dump(fd, ipw, args); - } - ipw.decreaseIndent(); + ipw.println("Location Providers:"); + ipw.increaseIndent(); + for (LocationProviderManager manager : mProviderManagers) { + manager.dump(fd, ipw, args); + } + ipw.decreaseIndent(); - synchronized (mLock) { if (mGnssManagerService != null) { ipw.println("GNSS:"); ipw.increaseIndent(); diff --git a/services/core/java/com/android/server/TelephonyRegistry.java b/services/core/java/com/android/server/TelephonyRegistry.java index 3e6ccb572860..0be21c5f15b6 100644 --- a/services/core/java/com/android/server/TelephonyRegistry.java +++ b/services/core/java/com/android/server/TelephonyRegistry.java @@ -2194,6 +2194,17 @@ public class TelephonyRegistry extends ITelephonyRegistry.Stub { private static final String PHONE_CONSTANTS_STATE_KEY = "state"; private static final String PHONE_CONSTANTS_SUBSCRIPTION_KEY = "subscription"; + /** + * Broadcast Action: The phone's signal strength has changed. The intent will have the + * following extra values: + * phoneName - A string version of the phone name. + * asu - A numeric value for the signal strength. + * An ASU is 0-31 or -1 if unknown (for GSM, dBm = -113 - 2 * asu). + * The following special values are defined: + * 0 means "-113 dBm or less".31 means "-51 dBm or greater". + */ + public static final String ACTION_SIGNAL_STRENGTH_CHANGED = "android.intent.action.SIG_STR"; + private void broadcastServiceStateChanged(ServiceState state, int phoneId, int subId) { long ident = Binder.clearCallingIdentity(); try { @@ -2228,7 +2239,7 @@ public class TelephonyRegistry extends ITelephonyRegistry.Stub { Binder.restoreCallingIdentity(ident); } - Intent intent = new Intent(TelephonyIntents.ACTION_SIGNAL_STRENGTH_CHANGED); + Intent intent = new Intent(ACTION_SIGNAL_STRENGTH_CHANGED); Bundle data = new Bundle(); fillInSignalStrengthNotifierBundle(signalStrength, data); intent.putExtras(data); diff --git a/services/core/java/com/android/server/am/ActiveServices.java b/services/core/java/com/android/server/am/ActiveServices.java index a58bd9bf32ff..e2a036aec172 100644 --- a/services/core/java/com/android/server/am/ActiveServices.java +++ b/services/core/java/com/android/server/am/ActiveServices.java @@ -2068,7 +2068,7 @@ public final class ActiveServices { + " type=" + resolvedType + " callingUid=" + callingUid); userId = mAm.mUserController.handleIncomingUser(callingPid, callingUid, userId, false, - ActivityManagerInternal.ALLOW_NON_FULL_IN_PROFILE, "service", + ActivityManagerInternal.ALLOW_ALL_PROFILE_PERMISSIONS_IN_PROFILE, "service", callingPackage); ServiceMap smap = getServiceMapLocked(userId); diff --git a/services/core/java/com/android/server/am/UserController.java b/services/core/java/com/android/server/am/UserController.java index eb1ab3863624..2b207827a5ef 100644 --- a/services/core/java/com/android/server/am/UserController.java +++ b/services/core/java/com/android/server/am/UserController.java @@ -16,12 +16,14 @@ package com.android.server.am; +import static android.Manifest.permission.INTERACT_ACROSS_PROFILES; import static android.Manifest.permission.INTERACT_ACROSS_USERS; import static android.Manifest.permission.INTERACT_ACROSS_USERS_FULL; import static android.app.ActivityManager.USER_OP_ERROR_IS_SYSTEM; import static android.app.ActivityManager.USER_OP_ERROR_RELATED_USERS_CANNOT_STOP; import static android.app.ActivityManager.USER_OP_IS_CURRENT; import static android.app.ActivityManager.USER_OP_SUCCESS; +import static android.app.ActivityManagerInternal.ALLOW_ALL_PROFILE_PERMISSIONS_IN_PROFILE; import static android.app.ActivityManagerInternal.ALLOW_FULL_ONLY; import static android.app.ActivityManagerInternal.ALLOW_NON_FULL; import static android.app.ActivityManagerInternal.ALLOW_NON_FULL_IN_PROFILE; @@ -1641,9 +1643,10 @@ class UserController implements Handler.Callback { if (callingUid != 0 && callingUid != SYSTEM_UID) { final boolean allow; + final boolean isSameProfileGroup = isSameProfileGroup(callingUserId, targetUserId); if (mInjector.isCallerRecents(callingUid) && callingUserId == getCurrentUserId() - && isSameProfileGroup(callingUserId, targetUserId)) { + && isSameProfileGroup) { // If the caller is Recents and it is running in the current user, we then allow it // to access its profiles. allow = true; @@ -1654,6 +1657,9 @@ class UserController implements Handler.Callback { } else if (allowMode == ALLOW_FULL_ONLY) { // We require full access, sucks to be you. allow = false; + } else if (canInteractWithAcrossProfilesPermission( + allowMode, isSameProfileGroup, callingPid, callingUid)) { + allow = true; } else if (mInjector.checkComponentPermission(INTERACT_ACROSS_USERS, callingPid, callingUid, -1, true) != PackageManager.PERMISSION_GRANTED) { // If the caller does not have either permission, they are always doomed. @@ -1661,10 +1667,11 @@ class UserController implements Handler.Callback { } else if (allowMode == ALLOW_NON_FULL) { // We are blanket allowing non-full access, you lucky caller! allow = true; - } else if (allowMode == ALLOW_NON_FULL_IN_PROFILE) { + } else if (allowMode == ALLOW_NON_FULL_IN_PROFILE + || allowMode == ALLOW_ALL_PROFILE_PERMISSIONS_IN_PROFILE) { // We may or may not allow this depending on whether the two users are // in the same profile. - allow = isSameProfileGroup(callingUserId, targetUserId); + allow = isSameProfileGroup; } else { throw new IllegalArgumentException("Unknown mode: " + allowMode); } @@ -1690,6 +1697,11 @@ class UserController implements Handler.Callback { if (allowMode != ALLOW_FULL_ONLY) { builder.append(" or "); builder.append(INTERACT_ACROSS_USERS); + if (isSameProfileGroup + && allowMode == ALLOW_ALL_PROFILE_PERMISSIONS_IN_PROFILE) { + builder.append(" or "); + builder.append(INTERACT_ACROSS_PROFILES); + } } String msg = builder.toString(); Slog.w(TAG, msg); @@ -1710,6 +1722,19 @@ class UserController implements Handler.Callback { return targetUserId; } + private boolean canInteractWithAcrossProfilesPermission( + int allowMode, boolean isSameProfileGroup, int callingPid, int callingUid) { + if (allowMode != ALLOW_ALL_PROFILE_PERMISSIONS_IN_PROFILE) { + return false; + } + if (!isSameProfileGroup) { + return false; + } + return mInjector.checkComponentPermission( + INTERACT_ACROSS_PROFILES, callingPid, callingUid, /*owningUid= */-1, + /*exported= */true) == PackageManager.PERMISSION_GRANTED; + } + int unsafeConvertIncomingUser(@UserIdInt int userId) { return (userId == UserHandle.USER_CURRENT || userId == UserHandle.USER_CURRENT_OR_SELF) ? getCurrentUserId(): userId; diff --git a/services/core/java/com/android/server/location/UserInfoStore.java b/services/core/java/com/android/server/location/UserInfoStore.java new file mode 100644 index 000000000000..550f51c7de58 --- /dev/null +++ b/services/core/java/com/android/server/location/UserInfoStore.java @@ -0,0 +1,226 @@ +/* + * Copyright (C) 2020 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.server.location; + +import android.annotation.Nullable; +import android.annotation.UserIdInt; +import android.app.ActivityManager; +import android.content.BroadcastReceiver; +import android.content.Context; +import android.content.Intent; +import android.content.IntentFilter; +import android.content.pm.UserInfo; +import android.os.Build; +import android.os.UserHandle; +import android.os.UserManager; + +import com.android.internal.annotations.GuardedBy; +import com.android.internal.util.ArrayUtils; +import com.android.internal.util.Preconditions; +import com.android.server.FgThread; + +import java.io.FileDescriptor; +import java.io.PrintWriter; +import java.util.Arrays; +import java.util.concurrent.CopyOnWriteArrayList; + +/** + * Provides accessors and listeners for all user info. + */ +public class UserInfoStore { + + /** + * Listener for current user changes. + */ + public interface UserChangedListener { + /** + * Called when the current user changes. + */ + void onUserChanged(@UserIdInt int oldUserId, @UserIdInt int newUserId); + } + + private final Context mContext; + private final CopyOnWriteArrayList<UserChangedListener> mListeners; + + @GuardedBy("this") + @Nullable + private UserManager mUserManager; + + @GuardedBy("this") + @UserIdInt + private int mCurrentUserId; + + @GuardedBy("this") + @UserIdInt + private int mCachedParentUserId; + @GuardedBy("this") + private int[] mCachedProfileUserIds; + + public UserInfoStore(Context context) { + mContext = context; + mListeners = new CopyOnWriteArrayList<>(); + + mCurrentUserId = UserHandle.USER_NULL; + mCachedParentUserId = UserHandle.USER_NULL; + mCachedProfileUserIds = new int[]{UserHandle.USER_NULL}; + } + + /** Called when system is ready. */ + public synchronized void onSystemReady() { + if (mUserManager != null) { + return; + } + + mUserManager = mContext.getSystemService(UserManager.class); + + IntentFilter intentFilter = new IntentFilter(); + intentFilter.addAction(Intent.ACTION_USER_SWITCHED); + intentFilter.addAction(Intent.ACTION_MANAGED_PROFILE_ADDED); + intentFilter.addAction(Intent.ACTION_MANAGED_PROFILE_REMOVED); + + mContext.registerReceiverAsUser(new BroadcastReceiver() { + @Override + public void onReceive(Context context, Intent intent) { + String action = intent.getAction(); + if (action == null) { + return; + } + switch (action) { + case Intent.ACTION_USER_SWITCHED: + int userId = intent.getIntExtra(Intent.EXTRA_USER_HANDLE, + UserHandle.USER_NULL); + if (userId != UserHandle.USER_NULL) { + onUserChanged(userId); + } + break; + case Intent.ACTION_MANAGED_PROFILE_ADDED: + case Intent.ACTION_MANAGED_PROFILE_REMOVED: + onUserProfilesChanged(); + break; + } + } + }, UserHandle.ALL, intentFilter, null, FgThread.getHandler()); + + mCurrentUserId = ActivityManager.getCurrentUser(); + } + + /** + * Adds a listener for user changed events. + */ + public void addListener(UserChangedListener listener) { + mListeners.add(listener); + } + + /** + * Removes a listener for user changed events. + */ + public void removeListener(UserChangedListener listener) { + mListeners.remove(listener); + } + + private void onUserChanged(@UserIdInt int newUserId) { + int oldUserId; + synchronized (this) { + if (newUserId == mCurrentUserId) { + return; + } + + oldUserId = mCurrentUserId; + mCurrentUserId = newUserId; + } + + for (UserChangedListener listener : mListeners) { + listener.onUserChanged(oldUserId, newUserId); + } + } + + private synchronized void onUserProfilesChanged() { + // this intent is only sent to the current user + if (mCachedParentUserId == mCurrentUserId) { + mCachedParentUserId = UserHandle.USER_NULL; + mCachedProfileUserIds = null; + } + } + + /** + * Returns the user id of the current user. + */ + @UserIdInt + public synchronized int getCurrentUserId() { + return mCurrentUserId; + } + + /** + * Returns true if the given user id is either the current user or a profile of the current + * user. + */ + public synchronized boolean isCurrentUserOrProfile(@UserIdInt int userId) { + return userId == mCurrentUserId || ArrayUtils.contains( + getProfileUserIdsForParentUser(mCurrentUserId), userId); + } + + /** + * Returns the parent user id of the given user id, or the user id itself if the user id either + * is a parent or has no profiles. + */ + @UserIdInt + public synchronized int getParentUserId(@UserIdInt int userId) { + int parentUserId; + if (userId == mCachedParentUserId || ArrayUtils.contains(mCachedProfileUserIds, userId)) { + parentUserId = mCachedParentUserId; + } else { + Preconditions.checkState(mUserManager != null); + + UserInfo userInfo = mUserManager.getProfileParent(userId); + if (userInfo != null) { + parentUserId = userInfo.id; + } else { + // getProfileParent() returns null if the userId is already the parent... + parentUserId = userId; + } + + // force profiles into cache + getProfileUserIdsForParentUser(parentUserId); + } + + return parentUserId; + } + + @GuardedBy("this") + private int[] getProfileUserIdsForParentUser(@UserIdInt int parentUserId) { + Preconditions.checkState(mUserManager != null); + + if (Build.IS_DEBUGGABLE) { + Preconditions.checkArgument(mUserManager.getProfileParent(parentUserId) == null); + } + + if (parentUserId != mCachedParentUserId) { + mCachedParentUserId = parentUserId; + mCachedProfileUserIds = mUserManager.getProfileIdsWithDisabled(parentUserId); + } + + return mCachedProfileUserIds; + } + + /** + * Dump info for debugging. + */ + public synchronized void dump(FileDescriptor fd, PrintWriter pw, String[] args) { + pw.println("Current User: " + mCurrentUserId + " " + Arrays.toString( + getProfileUserIdsForParentUser(mCurrentUserId))); + } +} diff --git a/services/core/java/com/android/server/locksettings/RebootEscrowManager.java b/services/core/java/com/android/server/locksettings/RebootEscrowManager.java index d2e54f9cd64c..46ea9d11d1dc 100644 --- a/services/core/java/com/android/server/locksettings/RebootEscrowManager.java +++ b/services/core/java/com/android/server/locksettings/RebootEscrowManager.java @@ -25,11 +25,13 @@ import android.os.RemoteException; import android.os.ServiceManager; import android.os.UserManager; import android.util.Slog; +import android.util.StatsLog; import com.android.internal.annotations.VisibleForTesting; import com.android.internal.widget.RebootEscrowListener; import java.io.IOException; +import java.util.ArrayList; import java.util.List; import java.util.NoSuchElementException; import java.util.concurrent.atomic.AtomicBoolean; @@ -109,20 +111,50 @@ class RebootEscrowManager { } void loadRebootEscrowDataIfAvailable() { + List<UserInfo> users = mUserManager.getUsers(); + List<UserInfo> rebootEscrowUsers = new ArrayList<>(); + for (UserInfo user : users) { + if (mCallbacks.isUserSecure(user.id) && mStorage.hasRebootEscrow(user.id)) { + rebootEscrowUsers.add(user); + } + } + + if (rebootEscrowUsers.isEmpty()) { + return; + } + + SecretKeySpec escrowKey = getAndClearRebootEscrowKey(); + if (escrowKey == null) { + Slog.w(TAG, "Had reboot escrow data for users, but no key; removing escrow storage."); + for (UserInfo user : users) { + mStorage.removeRebootEscrow(user.id); + } + StatsLog.write(StatsLog.REBOOT_ESCROW_RECOVERY_REPORTED, false); + return; + } + + boolean allUsersUnlocked = true; + for (UserInfo user : rebootEscrowUsers) { + allUsersUnlocked &= restoreRebootEscrowForUser(user.id, escrowKey); + } + StatsLog.write(StatsLog.REBOOT_ESCROW_RECOVERY_REPORTED, allUsersUnlocked); + } + + private SecretKeySpec getAndClearRebootEscrowKey() { IRebootEscrow rebootEscrow = mInjector.getRebootEscrow(); if (rebootEscrow == null) { - return; + return null; } - final SecretKeySpec escrowKey; try { byte[] escrowKeyBytes = rebootEscrow.retrieveKey(); if (escrowKeyBytes == null) { - return; + Slog.w(TAG, "Had reboot escrow data for users, but could not retrieve key"); + return null; } else if (escrowKeyBytes.length != 32) { Slog.e(TAG, "IRebootEscrow returned key of incorrect size " + escrowKeyBytes.length); - return; + return null; } // Make sure we didn't get the null key. @@ -132,29 +164,22 @@ class RebootEscrowManager { } if (zero == 0) { Slog.w(TAG, "IRebootEscrow returned an all-zeroes key"); - return; + return null; } // Overwrite the existing key with the null key rebootEscrow.storeKey(new byte[32]); - escrowKey = RebootEscrowData.fromKeyBytes(escrowKeyBytes); + return RebootEscrowData.fromKeyBytes(escrowKeyBytes); } catch (RemoteException e) { Slog.w(TAG, "Could not retrieve escrow data"); - return; - } - - List<UserInfo> users = mUserManager.getUsers(); - for (UserInfo user : users) { - if (mCallbacks.isUserSecure(user.id)) { - restoreRebootEscrowForUser(user.id, escrowKey); - } + return null; } } - private void restoreRebootEscrowForUser(@UserIdInt int userId, SecretKeySpec escrowKey) { + private boolean restoreRebootEscrowForUser(@UserIdInt int userId, SecretKeySpec escrowKey) { if (!mStorage.hasRebootEscrow(userId)) { - return; + return false; } try { @@ -165,9 +190,11 @@ class RebootEscrowManager { mCallbacks.onRebootEscrowRestored(escrowData.getSpVersion(), escrowData.getSyntheticPassword(), userId); + return true; } catch (IOException e) { Slog.w(TAG, "Could not load reboot escrow data for user " + userId, e); } + return false; } void callToRebootEscrowIfNeeded(@UserIdInt int userId, byte spVersion, diff --git a/services/core/java/com/android/server/locksettings/recoverablekeystore/KeySyncTask.java b/services/core/java/com/android/server/locksettings/recoverablekeystore/KeySyncTask.java index 29338ba06dc2..52750f392d5b 100644 --- a/services/core/java/com/android/server/locksettings/recoverablekeystore/KeySyncTask.java +++ b/services/core/java/com/android/server/locksettings/recoverablekeystore/KeySyncTask.java @@ -20,6 +20,7 @@ import static android.security.keystore.recovery.KeyChainProtectionParams.TYPE_L import android.content.Context; import android.os.RemoteException; +import android.os.UserHandle; import android.security.Scrypt; import android.security.keystore.recovery.KeyChainProtectionParams; import android.security.keystore.recovery.KeyChainSnapshot; @@ -163,16 +164,28 @@ public class KeySyncTask implements Runnable { } private void syncKeys() throws RemoteException { + int generation = mPlatformKeyManager.getGenerationId(mUserId); if (mCredentialType == LockPatternUtils.CREDENTIAL_TYPE_NONE) { // Application keys for the user will not be available for sync. Log.w(TAG, "Credentials are not set for user " + mUserId); - int generation = mPlatformKeyManager.getGenerationId(mUserId); - mPlatformKeyManager.invalidatePlatformKey(mUserId, generation); + if (generation < PlatformKeyManager.MIN_GENERATION_ID_FOR_UNLOCKED_DEVICE_REQUIRED + || mUserId != UserHandle.USER_SYSTEM) { + // Only invalidate keys with legacy protection param. + mPlatformKeyManager.invalidatePlatformKey(mUserId, generation); + } return; } if (isCustomLockScreen()) { - Log.w(TAG, "Unsupported credential type " + mCredentialType + "for user " + mUserId); - mRecoverableKeyStoreDb.invalidateKeysForUserIdOnCustomScreenLock(mUserId); + Log.w(TAG, "Unsupported credential type " + mCredentialType + " for user " + mUserId); + // Keys will be synced when user starts using non custom screen lock. + if (generation < PlatformKeyManager.MIN_GENERATION_ID_FOR_UNLOCKED_DEVICE_REQUIRED + || mUserId != UserHandle.USER_SYSTEM) { + mRecoverableKeyStoreDb.invalidateKeysForUserIdOnCustomScreenLock(mUserId); + } + return; + } + if (mPlatformKeyManager.isDeviceLocked(mUserId) && mUserId == UserHandle.USER_SYSTEM) { + Log.w(TAG, "Can't sync keys for locked user " + mUserId); return; } diff --git a/services/core/java/com/android/server/locksettings/recoverablekeystore/PlatformKeyManager.java b/services/core/java/com/android/server/locksettings/recoverablekeystore/PlatformKeyManager.java index 0ad6c2a69556..0761cde825b6 100644 --- a/services/core/java/com/android/server/locksettings/recoverablekeystore/PlatformKeyManager.java +++ b/services/core/java/com/android/server/locksettings/recoverablekeystore/PlatformKeyManager.java @@ -67,8 +67,9 @@ import javax.crypto.spec.GCMParameterSpec; * @hide */ public class PlatformKeyManager { - private static final String TAG = "PlatformKeyManager"; + static final int MIN_GENERATION_ID_FOR_UNLOCKED_DEVICE_REQUIRED = 1000000; + private static final String TAG = "PlatformKeyManager"; private static final String KEY_ALGORITHM = "AES"; private static final int KEY_SIZE_BITS = 256; private static final String KEY_ALIAS_PREFIX = @@ -131,14 +132,14 @@ public class PlatformKeyManager { /** * Returns {@code true} if the platform key is available. A platform key won't be available if - * the user has not set up a lock screen. + * device is locked. * * @param userId The ID of the user to whose lock screen the platform key must be bound. * * @hide */ - public boolean isAvailable(int userId) { - return mContext.getSystemService(KeyguardManager.class).isDeviceSecure(userId); + public boolean isDeviceLocked(int userId) { + return mContext.getSystemService(KeyguardManager.class).isDeviceLocked(userId); } /** @@ -169,7 +170,6 @@ public class PlatformKeyManager { * @param userId The ID of the user to whose lock screen the platform key must be bound. * @throws NoSuchAlgorithmException if AES is unavailable - should never happen. * @throws KeyStoreException if there is an error in AndroidKeyStore. - * @throws InsecureUserException if the user does not have a lock screen set. * @throws IOException if there was an issue with local database update. * @throws RemoteException if there was an issue communicating with {@link IGateKeeperService}. * @@ -177,13 +177,8 @@ public class PlatformKeyManager { */ @VisibleForTesting void regenerate(int userId) - throws NoSuchAlgorithmException, KeyStoreException, InsecureUserException, IOException, + throws NoSuchAlgorithmException, KeyStoreException, IOException, RemoteException { - if (!isAvailable(userId)) { - throw new InsecureUserException(String.format( - Locale.US, "%d does not have a lock screen set.", userId)); - } - int generationId = getGenerationId(userId); int nextId; if (generationId == -1) { @@ -192,6 +187,7 @@ public class PlatformKeyManager { invalidatePlatformKey(userId, generationId); nextId = generationId + 1; } + generationId = Math.max(generationId, MIN_GENERATION_ID_FOR_UNLOCKED_DEVICE_REQUIRED); generateAndLoadKey(userId, nextId); } @@ -203,7 +199,6 @@ public class PlatformKeyManager { * @throws KeyStoreException if there was an AndroidKeyStore error. * @throws UnrecoverableKeyException if the key could not be recovered. * @throws NoSuchAlgorithmException if AES is unavailable - should never occur. - * @throws InsecureUserException if the user does not have a lock screen set. * @throws IOException if there was an issue with local database update. * @throws RemoteException if there was an issue communicating with {@link IGateKeeperService}. * @@ -211,7 +206,7 @@ public class PlatformKeyManager { */ public PlatformEncryptionKey getEncryptKey(int userId) throws KeyStoreException, UnrecoverableKeyException, NoSuchAlgorithmException, - InsecureUserException, IOException, RemoteException { + IOException, RemoteException { init(userId); try { // Try to see if the decryption key is still accessible before using the encryption key. @@ -234,12 +229,11 @@ public class PlatformKeyManager { * @throws KeyStoreException if there was an AndroidKeyStore error. * @throws UnrecoverableKeyException if the key could not be recovered. * @throws NoSuchAlgorithmException if AES is unavailable - should never occur. - * @throws InsecureUserException if the user does not have a lock screen set. * * @hide */ private PlatformEncryptionKey getEncryptKeyInternal(int userId) throws KeyStoreException, - UnrecoverableKeyException, NoSuchAlgorithmException, InsecureUserException { + UnrecoverableKeyException, NoSuchAlgorithmException { int generationId = getGenerationId(userId); String alias = getEncryptAlias(userId, generationId); if (!isKeyLoaded(userId, generationId)) { @@ -258,7 +252,6 @@ public class PlatformKeyManager { * @throws KeyStoreException if there was an AndroidKeyStore error. * @throws UnrecoverableKeyException if the key could not be recovered. * @throws NoSuchAlgorithmException if AES is unavailable - should never occur. - * @throws InsecureUserException if the user does not have a lock screen set. * @throws IOException if there was an issue with local database update. * @throws RemoteException if there was an issue communicating with {@link IGateKeeperService}. * @@ -266,7 +259,7 @@ public class PlatformKeyManager { */ public PlatformDecryptionKey getDecryptKey(int userId) throws KeyStoreException, UnrecoverableKeyException, NoSuchAlgorithmException, - InsecureUserException, IOException, RemoteException { + IOException, RemoteException { init(userId); try { PlatformDecryptionKey decryptionKey = getDecryptKeyInternal(userId); @@ -288,12 +281,11 @@ public class PlatformKeyManager { * @throws KeyStoreException if there was an AndroidKeyStore error. * @throws UnrecoverableKeyException if the key could not be recovered. * @throws NoSuchAlgorithmException if AES is unavailable - should never occur. - * @throws InsecureUserException if the user does not have a lock screen set. * * @hide */ private PlatformDecryptionKey getDecryptKeyInternal(int userId) throws KeyStoreException, - UnrecoverableKeyException, NoSuchAlgorithmException, InsecureUserException { + UnrecoverableKeyException, NoSuchAlgorithmException { int generationId = getGenerationId(userId); String alias = getDecryptAlias(userId, generationId); if (!isKeyLoaded(userId, generationId)) { @@ -340,13 +332,8 @@ public class PlatformKeyManager { * @hide */ void init(int userId) - throws KeyStoreException, NoSuchAlgorithmException, InsecureUserException, IOException, + throws KeyStoreException, NoSuchAlgorithmException, IOException, RemoteException { - if (!isAvailable(userId)) { - throw new InsecureUserException(String.format( - Locale.US, "%d does not have a lock screen set.", userId)); - } - int generationId = getGenerationId(userId); if (isKeyLoaded(userId, generationId)) { Log.i(TAG, String.format( @@ -363,6 +350,7 @@ public class PlatformKeyManager { generationId++; } + generationId = Math.max(generationId, MIN_GENERATION_ID_FOR_UNLOCKED_DEVICE_REQUIRED); generateAndLoadKey(userId, generationId); } @@ -440,12 +428,16 @@ public class PlatformKeyManager { KeyProtection.Builder decryptionKeyProtection = new KeyProtection.Builder(KeyProperties.PURPOSE_DECRYPT) - .setUserAuthenticationRequired(true) - .setUserAuthenticationValidityDurationSeconds( - USER_AUTHENTICATION_VALIDITY_DURATION_SECONDS) .setBlockModes(KeyProperties.BLOCK_MODE_GCM) .setEncryptionPaddings(KeyProperties.ENCRYPTION_PADDING_NONE); - if (userId != UserHandle.USER_SYSTEM) { + // Skip UserAuthenticationRequired for main user + if (userId == UserHandle.USER_SYSTEM) { + decryptionKeyProtection.setUnlockedDeviceRequired(true); + } else { + // With setUnlockedDeviceRequired, KeyStore thinks that device is locked . + decryptionKeyProtection.setUserAuthenticationRequired(true); + decryptionKeyProtection.setUserAuthenticationValidityDurationSeconds( + USER_AUTHENTICATION_VALIDITY_DURATION_SECONDS); // Bind decryption key to secondary profile lock screen secret. long secureUserId = getGateKeeperService().getSecureUserId(userId); // TODO(b/124095438): Propagate this failure instead of silently failing. diff --git a/services/core/java/com/android/server/locksettings/recoverablekeystore/RecoverableKeyStoreManager.java b/services/core/java/com/android/server/locksettings/recoverablekeystore/RecoverableKeyStoreManager.java index 383d5cf326c0..6d97ed7a69a7 100644 --- a/services/core/java/com/android/server/locksettings/recoverablekeystore/RecoverableKeyStoreManager.java +++ b/services/core/java/com/android/server/locksettings/recoverablekeystore/RecoverableKeyStoreManager.java @@ -19,7 +19,6 @@ package com.android.server.locksettings.recoverablekeystore; import static android.security.keystore.recovery.RecoveryController.ERROR_BAD_CERTIFICATE_FORMAT; import static android.security.keystore.recovery.RecoveryController.ERROR_DECRYPTION_FAILED; import static android.security.keystore.recovery.RecoveryController.ERROR_DOWNGRADE_CERTIFICATE; -import static android.security.keystore.recovery.RecoveryController.ERROR_INSECURE_USER; import static android.security.keystore.recovery.RecoveryController.ERROR_INVALID_CERTIFICATE; import static android.security.keystore.recovery.RecoveryController.ERROR_INVALID_KEY_FORMAT; import static android.security.keystore.recovery.RecoveryController.ERROR_NO_SNAPSHOT_PENDING; @@ -46,7 +45,6 @@ import android.util.Log; import com.android.internal.annotations.VisibleForTesting; import com.android.internal.util.HexDump; -import com.android.internal.util.Preconditions; import com.android.server.locksettings.recoverablekeystore.certificate.CertParsingException; import com.android.server.locksettings.recoverablekeystore.certificate.CertUtils; import com.android.server.locksettings.recoverablekeystore.certificate.CertValidationException; @@ -76,8 +74,9 @@ import java.util.List; import java.util.Locale; import java.util.Map; import java.util.Objects; -import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; +import java.util.concurrent.ScheduledExecutorService; +import java.util.concurrent.TimeUnit; import javax.crypto.AEADBadTagException; @@ -89,13 +88,14 @@ import javax.crypto.AEADBadTagException; */ public class RecoverableKeyStoreManager { private static final String TAG = "RecoverableKeyStoreMgr"; + private static final long SYNC_DELAY_MILLIS = 2000; private static RecoverableKeyStoreManager mInstance; private final Context mContext; private final RecoverableKeyStoreDb mDatabase; private final RecoverySessionStorage mRecoverySessionStorage; - private final ExecutorService mExecutorService; + private final ScheduledExecutorService mExecutorService; private final RecoverySnapshotListenersStorage mListenersStorage; private final RecoverableKeyGenerator mRecoverableKeyGenerator; private final RecoverySnapshotStorage mSnapshotStorage; @@ -136,7 +136,7 @@ public class RecoverableKeyStoreManager { context.getApplicationContext(), db, new RecoverySessionStorage(), - Executors.newSingleThreadExecutor(), + Executors.newScheduledThreadPool(1), snapshotStorage, new RecoverySnapshotListenersStorage(), platformKeyManager, @@ -152,7 +152,7 @@ public class RecoverableKeyStoreManager { Context context, RecoverableKeyStoreDb recoverableKeyStoreDb, RecoverySessionStorage recoverySessionStorage, - ExecutorService executorService, + ScheduledExecutorService executorService, RecoverySnapshotStorage snapshotStorage, RecoverySnapshotListenersStorage listenersStorage, PlatformKeyManager platformKeyManager, @@ -724,8 +724,6 @@ public class RecoverableKeyStoreManager { throw new RuntimeException(e); } catch (KeyStoreException | UnrecoverableKeyException | IOException e) { throw new ServiceSpecificException(ERROR_SERVICE_INTERNAL_ERROR, e.getMessage()); - } catch (InsecureUserException e) { - throw new ServiceSpecificException(ERROR_INSECURE_USER, e.getMessage()); } try { @@ -793,8 +791,6 @@ public class RecoverableKeyStoreManager { throw new RuntimeException(e); } catch (KeyStoreException | UnrecoverableKeyException | IOException e) { throw new ServiceSpecificException(ERROR_SERVICE_INTERNAL_ERROR, e.getMessage()); - } catch (InsecureUserException e) { - throw new ServiceSpecificException(ERROR_INSECURE_USER, e.getMessage()); } try { @@ -915,7 +911,7 @@ public class RecoverableKeyStoreManager { int storedHashType, @NonNull byte[] credential, int userId) { // So as not to block the critical path unlocking the phone, defer to another thread. try { - mExecutorService.execute(KeySyncTask.newInstance( + mExecutorService.schedule(KeySyncTask.newInstance( mContext, mDatabase, mSnapshotStorage, @@ -923,7 +919,10 @@ public class RecoverableKeyStoreManager { userId, storedHashType, credential, - /*credentialUpdated=*/ false)); + /*credentialUpdated=*/ false), + SYNC_DELAY_MILLIS, + TimeUnit.MILLISECONDS + ); } catch (NoSuchAlgorithmException e) { Log.wtf(TAG, "Should never happen - algorithm unavailable for KeySync", e); } catch (KeyStoreException e) { @@ -947,7 +946,7 @@ public class RecoverableKeyStoreManager { int userId) { // So as not to block the critical path unlocking the phone, defer to another thread. try { - mExecutorService.execute(KeySyncTask.newInstance( + mExecutorService.schedule(KeySyncTask.newInstance( mContext, mDatabase, mSnapshotStorage, @@ -955,7 +954,10 @@ public class RecoverableKeyStoreManager { userId, storedHashType, credential, - /*credentialUpdated=*/ true)); + /*credentialUpdated=*/ true), + SYNC_DELAY_MILLIS, + TimeUnit.MILLISECONDS + ); } catch (NoSuchAlgorithmException e) { Log.wtf(TAG, "Should never happen - algorithm unavailable for KeySync", e); } catch (KeyStoreException e) { diff --git a/services/core/java/com/android/server/notification/NotificationManagerService.java b/services/core/java/com/android/server/notification/NotificationManagerService.java index 5eafb4102208..0e08033e0f68 100755 --- a/services/core/java/com/android/server/notification/NotificationManagerService.java +++ b/services/core/java/com/android/server/notification/NotificationManagerService.java @@ -1010,12 +1010,14 @@ public class NotificationManagerService extends SystemService { if (clearEffects) { clearEffects(); } + mAssistants.onPanelRevealed(items); } @Override public void onPanelHidden() { MetricsLogger.hidden(getContext(), MetricsEvent.NOTIFICATION_PANEL); EventLogTags.writeNotificationPanelHidden(); + mAssistants.onPanelHidden(); } @Override @@ -1062,6 +1064,7 @@ public class NotificationManagerService extends SystemService { reportSeen(r); } r.setVisibility(true, nv.rank, nv.count); + mAssistants.notifyAssistantVisibilityChangedLocked(r.sbn, true); boolean isHun = (nv.location == NotificationVisibility.NotificationLocation.LOCATION_FIRST_HEADS_UP); // hasBeenVisiblyExpanded must be called after updating the expansion state of @@ -1080,6 +1083,7 @@ public class NotificationManagerService extends SystemService { NotificationRecord r = mNotificationsByKey.get(nv.key); if (r == null) continue; r.setVisibility(false, nv.rank, nv.count); + mAssistants.notifyAssistantVisibilityChangedLocked(r.sbn, false); nv.recycle(); } } @@ -8346,6 +8350,32 @@ public class NotificationManagerService extends SystemService { } } + protected void onPanelRevealed(int items) { + for (final ManagedServiceInfo info : NotificationAssistants.this.getServices()) { + mHandler.post(() -> { + final INotificationListener assistant = (INotificationListener) info.service; + try { + assistant.onPanelRevealed(items); + } catch (RemoteException ex) { + Slog.e(TAG, "unable to notify assistant (panel revealed): " + info, ex); + } + }); + } + } + + protected void onPanelHidden() { + for (final ManagedServiceInfo info : NotificationAssistants.this.getServices()) { + mHandler.post(() -> { + final INotificationListener assistant = (INotificationListener) info.service; + try { + assistant.onPanelHidden(); + } catch (RemoteException ex) { + Slog.e(TAG, "unable to notify assistant (panel hidden): " + info, ex); + } + }); + } + } + boolean hasUserSet(int userId) { synchronized (mLock) { return mUserSetMap.getOrDefault(userId, false); @@ -8413,6 +8443,24 @@ public class NotificationManagerService extends SystemService { } @GuardedBy("mNotificationLock") + void notifyAssistantVisibilityChangedLocked( + final StatusBarNotification sbn, + final boolean isVisible) { + final String key = sbn.getKey(); + Slog.d(TAG, "notifyAssistantVisibilityChangedLocked: " + key); + notifyAssistantLocked( + sbn, + false /* sameUserOnly */, + (assistant, sbnHolder) -> { + try { + assistant.onNotificationVisibilityChanged(key, isVisible); + } catch (RemoteException ex) { + Slog.e(TAG, "unable to notify assistant (visible): " + assistant, ex); + } + }); + } + + @GuardedBy("mNotificationLock") void notifyAssistantExpansionChangedLocked( final StatusBarNotification sbn, final boolean isUserAction, diff --git a/services/core/java/com/android/server/pm/ApexManager.java b/services/core/java/com/android/server/pm/ApexManager.java index 307a07bb09a2..a009183f7ecb 100644 --- a/services/core/java/com/android/server/pm/ApexManager.java +++ b/services/core/java/com/android/server/pm/ApexManager.java @@ -32,10 +32,13 @@ import android.content.pm.PackageInfo; import android.content.pm.PackageInstaller; import android.content.pm.PackageManager; import android.content.pm.PackageParser; +import android.content.pm.parsing.AndroidPackage; import android.os.Environment; import android.os.RemoteException; import android.os.ServiceManager; import android.sysprop.ApexProperties; +import android.util.ArrayMap; +import android.util.ArraySet; import android.util.Singleton; import android.util.Slog; @@ -44,15 +47,20 @@ import com.android.internal.annotations.VisibleForTesting; import com.android.internal.os.BackgroundThread; import com.android.internal.util.IndentingPrintWriter; +import com.google.android.collect.Lists; + import java.io.File; import java.io.PrintWriter; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; import java.util.ArrayList; -import java.util.Arrays; import java.util.Collections; +import java.util.HashMap; import java.util.HashSet; +import java.util.Iterator; import java.util.List; +import java.util.Map; +import java.util.Set; import java.util.stream.Collectors; /** @@ -97,12 +105,27 @@ abstract class ApexManager { * Minimal information about APEX mount points and the original APEX package they refer to. */ static class ActiveApexInfo { + @Nullable public final String apexModuleName; public final File apexDirectory; - public final File preinstalledApexPath; + public final File preInstalledApexPath; + + private ActiveApexInfo(File apexDirectory, File preInstalledApexPath) { + this(null, apexDirectory, preInstalledApexPath); + } - private ActiveApexInfo(File apexDirectory, File preinstalledApexPath) { + private ActiveApexInfo(@Nullable String apexModuleName, File apexDirectory, + File preInstalledApexPath) { + this.apexModuleName = apexModuleName; this.apexDirectory = apexDirectory; - this.preinstalledApexPath = preinstalledApexPath; + this.preInstalledApexPath = preInstalledApexPath; + } + + private ActiveApexInfo(ApexInfo apexInfo) { + this( + apexInfo.moduleName, + new File(Environment.getApexDirectory() + File.separator + + apexInfo.moduleName), + new File(apexInfo.preinstalledModulePath)); } } @@ -232,6 +255,17 @@ abstract class ApexManager { abstract boolean uninstallApex(String apexPackagePath); /** + * Registers an APK package as an embedded apk of apex. + */ + abstract void registerApkInApex(AndroidPackage pkg); + + /** + * Returns list of {@code packageName} of apks inside the given apex. + * @param apexPackageName Package name of the apk container of apex + */ + abstract List<String> getApksInApex(String apexPackageName); + + /** * Dumps various state information to the provided {@link PrintWriter} object. * * @param pw the {@link PrintWriter} object to send information to. @@ -255,16 +289,33 @@ abstract class ApexManager { static class ApexManagerImpl extends ApexManager { private final IApexService mApexService; private final Object mLock = new Object(); + + @GuardedBy("mLock") + private Set<ActiveApexInfo> mActiveApexInfosCache; + /** - * A map from {@code APEX packageName} to the {@Link PackageInfo} generated from the {@code - * AndroidManifest.xml} - * - * <p>Note that key of this map is {@code packageName} field of the corresponding {@code - * AndroidManifest.xml}. - */ + * Contains the list of {@code packageName}s of apks-in-apex for given + * {@code apexModuleName}. See {@link #mPackageNameToApexModuleName} to understand the + * difference between {@code packageName} and {@code apexModuleName}. + */ + @GuardedBy("mLock") + private Map<String, List<String>> mApksInApex = new ArrayMap<>(); + @GuardedBy("mLock") private List<PackageInfo> mAllPackagesCache; + /** + * An APEX is a file format that delivers the apex-payload wrapped in an apk container. The + * apk container has a reference name, called {@code packageName}, which is found inside the + * {@code AndroidManifest.xml}. The apex payload inside the container also has a reference + * name, called {@code apexModuleName}, which is found in {@code apex_manifest.json} file. + * + * {@link #mPackageNameToApexModuleName} contains the mapping from {@code packageName} of + * the apk container to {@code apexModuleName} of the apex-payload inside. + */ + @GuardedBy("mLock") + private Map<String, String> mPackageNameToApexModuleName; + ApexManagerImpl(IApexService apexService) { mApexService = apexService; } @@ -291,18 +342,25 @@ abstract class ApexManager { @Override List<ActiveApexInfo> getActiveApexInfos() { - try { - return Arrays.stream(mApexService.getActivePackages()) - .map(apexInfo -> new ActiveApexInfo( - new File( - Environment.getApexDirectory() + File.separator - + apexInfo.moduleName), - new File(apexInfo.preinstalledModulePath))).collect( - Collectors.toList()); - } catch (RemoteException e) { - Slog.e(TAG, "Unable to retrieve packages from apexservice", e); + synchronized (mLock) { + if (mActiveApexInfosCache == null) { + try { + mActiveApexInfosCache = new ArraySet<>(); + final ApexInfo[] activePackages = mApexService.getActivePackages(); + for (int i = 0; i < activePackages.length; i++) { + ApexInfo apexInfo = activePackages[i]; + mActiveApexInfosCache.add(new ActiveApexInfo(apexInfo)); + } + } catch (RemoteException e) { + Slog.e(TAG, "Unable to retrieve packages from apexservice", e); + } + } + if (mActiveApexInfosCache != null) { + return new ArrayList<>(mActiveApexInfosCache); + } else { + return Collections.emptyList(); + } } - return Collections.emptyList(); } @Override @@ -325,6 +383,7 @@ abstract class ApexManager { } try { mAllPackagesCache = new ArrayList<>(); + mPackageNameToApexModuleName = new HashMap<>(); HashSet<String> activePackagesSet = new HashSet<>(); HashSet<String> factoryPackagesSet = new HashSet<>(); final ApexInfo[] allPkgs = mApexService.getAllPackages(); @@ -350,6 +409,7 @@ abstract class ApexManager { final PackageInfo packageInfo = PackageParser.generatePackageInfo(pkg, ai, flags); mAllPackagesCache.add(packageInfo); + mPackageNameToApexModuleName.put(packageInfo.packageName, ai.moduleName); if (ai.isActive) { if (activePackagesSet.contains(packageInfo.packageName)) { throw new IllegalStateException( @@ -366,7 +426,6 @@ abstract class ApexManager { } factoryPackagesSet.add(packageInfo.packageName); } - } } catch (RemoteException re) { Slog.e(TAG, "Unable to retrieve packages from apexservice: " + re.toString()); @@ -533,6 +592,37 @@ abstract class ApexManager { } } + @Override + void registerApkInApex(AndroidPackage pkg) { + synchronized (mLock) { + final Iterator<ActiveApexInfo> it = mActiveApexInfosCache.iterator(); + while (it.hasNext()) { + final ActiveApexInfo aai = it.next(); + if (pkg.getBaseCodePath().startsWith(aai.apexDirectory.getAbsolutePath())) { + List<String> apks = mApksInApex.get(aai.apexModuleName); + if (apks == null) { + apks = Lists.newArrayList(); + mApksInApex.put(aai.apexModuleName, apks); + } + apks.add(pkg.getPackageName()); + } + } + } + } + + @Override + List<String> getApksInApex(String apexPackageName) { + // TODO(b/142712057): Avoid calling populateAllPackagesCacheIfNeeded during boot. + populateAllPackagesCacheIfNeeded(); + synchronized (mLock) { + String moduleName = mPackageNameToApexModuleName.get(apexPackageName); + if (moduleName == null) { + return Collections.emptyList(); + } + return mApksInApex.getOrDefault(moduleName, Collections.emptyList()); + } + } + /** * Dump information about the packages contained in a particular cache * @param packagesCache the cache to print information about. @@ -614,7 +704,6 @@ abstract class ApexManager { * updating APEX packages. */ private static final class ApexManagerFlattenedApex extends ApexManager { - @Override List<ActiveApexInfo> getActiveApexInfos() { // There is no apexd running in case of flattened apex @@ -721,6 +810,16 @@ abstract class ApexManager { } @Override + void registerApkInApex(AndroidPackage pkg) { + // No-op + } + + @Override + List<String> getApksInApex(String apexPackageName) { + return Collections.emptyList(); + } + + @Override void dump(PrintWriter pw, String packageName) { // No-op } diff --git a/services/core/java/com/android/server/pm/PackageInstallerService.java b/services/core/java/com/android/server/pm/PackageInstallerService.java index c43f23454ca6..6331dd46c035 100644 --- a/services/core/java/com/android/server/pm/PackageInstallerService.java +++ b/services/core/java/com/android/server/pm/PackageInstallerService.java @@ -188,7 +188,7 @@ public class PackageInstallerService extends IPackageInstaller.Stub implements } }; - public PackageInstallerService(Context context, PackageManagerService pm, ApexManager am) { + public PackageInstallerService(Context context, PackageManagerService pm) { mContext = context; mPm = pm; mPermissionManager = LocalServices.getService(PermissionManagerServiceInternal.class); @@ -206,9 +206,8 @@ public class PackageInstallerService extends IPackageInstaller.Stub implements mSessionsDir = new File(Environment.getDataSystemDirectory(), "install_sessions"); mSessionsDir.mkdirs(); - mApexManager = am; - - mStagingManager = new StagingManager(this, am, context); + mApexManager = ApexManager.getInstance(); + mStagingManager = new StagingManager(this, context); } boolean okToSendBroadcasts() { diff --git a/services/core/java/com/android/server/pm/PackageInstallerSession.java b/services/core/java/com/android/server/pm/PackageInstallerSession.java index 97a2d43e6183..165bdebe070f 100644 --- a/services/core/java/com/android/server/pm/PackageInstallerSession.java +++ b/services/core/java/com/android/server/pm/PackageInstallerSession.java @@ -2413,16 +2413,6 @@ public class PackageInstallerSession extends IPackageInstallerSession.Stub { @Override public void addFile(String name, long lengthBytes, byte[] metadata) { - if (mIncrementalFileStorages != null) { - try { - mIncrementalFileStorages.addFile(new InstallationFile(name, lengthBytes, metadata)); - //TODO(b/136132412): merge incremental and callback installation schemes - return; - } catch (IOException ex) { - throw new IllegalStateException( - "Failed to add and configure Incremental File: " + name, ex); - } - } if (!isDataLoaderInstallation()) { throw new IllegalStateException( "Cannot add files to non-data loader installation session."); @@ -2435,7 +2425,6 @@ public class PackageInstallerSession extends IPackageInstallerSession.Stub { synchronized (mLock) { assertCallerIsOwnerOrRootLocked(); assertPreparedAndNotSealedLocked("addFile"); - mFiles.add(FileInfo.added(name, lengthBytes, metadata)); } } @@ -2486,7 +2475,7 @@ public class PackageInstallerSession extends IPackageInstallerSession.Stub { */ private void prepareDataLoader() throws PackageManagerException, StreamingException { - if (!isStreamingInstallation()) { + if (!isDataLoaderInstallation()) { return; } @@ -2500,6 +2489,18 @@ public class PackageInstallerSession extends IPackageInstallerSession.Stub { file -> file.name.substring( 0, file.name.length() - REMOVE_MARKER_EXTENSION.length())).collect( Collectors.toList()); + if (mIncrementalFileStorages != null) { + for (InstallationFile file : addedFiles) { + try { + mIncrementalFileStorages.addFile(file); + } catch (IOException ex) { + // TODO(b/146080380): add incremental-specific error code + throw new PackageManagerException(INSTALL_FAILED_INTERNAL_ERROR, + "Failed to add and configure Incremental File: " + file.getName(), ex); + } + } + return; + } final FileSystemConnector connector = new FileSystemConnector(addedFiles); diff --git a/services/core/java/com/android/server/pm/PackageManagerService.java b/services/core/java/com/android/server/pm/PackageManagerService.java index b26e6c7021c1..17870ebe9957 100644 --- a/services/core/java/com/android/server/pm/PackageManagerService.java +++ b/services/core/java/com/android/server/pm/PackageManagerService.java @@ -492,6 +492,7 @@ public class PackageManagerService extends IPackageManager.Stub static final int SCAN_AS_PRODUCT = 1 << 20; static final int SCAN_AS_SYSTEM_EXT = 1 << 21; static final int SCAN_AS_ODM = 1 << 22; + static final int SCAN_AS_APK_IN_APEX = 1 << 23; @IntDef(flag = true, prefix = { "SCAN_" }, value = { SCAN_NO_DEX, @@ -2589,6 +2590,9 @@ public class PackageManagerService extends IPackageManager.Stub & (SCAN_AS_VENDOR | SCAN_AS_ODM | SCAN_AS_PRODUCT | SCAN_AS_SYSTEM_EXT)) != 0) { return true; } + if ((scanFlags & SCAN_AS_APK_IN_APEX) != 0) { + return true; + } return false; } @@ -3332,7 +3336,7 @@ public class PackageManagerService extends IPackageManager.Stub } } - mInstallerService = new PackageInstallerService(mContext, this, mApexManager); + mInstallerService = new PackageInstallerService(mContext, this); final Pair<ComponentName, String> instantAppResolverComponent = getInstantAppResolverLPr(); if (instantAppResolverComponent != null) { @@ -5344,8 +5348,9 @@ public class PackageManagerService extends IPackageManager.Stub if (!mUserManager.exists(userId)) return null; final int callingUid = Binder.getCallingUid(); flags = updateFlagsForComponent(flags, userId); - mPermissionManager.enforceCrossUserPermission(callingUid, userId, - false /* requireFullPermission */, false /* checkShell */, "get service info"); + mPermissionManager.enforceCrossUserOrProfilePermission( + callingUid, userId, false /* requireFullPermission */, false /* checkShell */, + "get service info"); synchronized (mLock) { ParsedService s = mComponentResolver.getService(component); if (DEBUG_PACKAGE_INFO) Log.v( @@ -7795,8 +7800,10 @@ public class PackageManagerService extends IPackageManager.Stub String resolvedType, int flags, int userId, int callingUid, boolean includeInstantApps) { if (!mUserManager.exists(userId)) return Collections.emptyList(); - mPermissionManager.enforceCrossUserPermission(callingUid, userId, - false /*requireFullPermission*/, false /*checkShell*/, + mPermissionManager.enforceCrossUserOrProfilePermission(callingUid, + userId, + false /*requireFullPermission*/, + false /*checkShell*/, "query intent receivers"); final String instantAppPkgName = getInstantAppPackageName(callingUid); flags = updateFlagsForResolve(flags, userId, callingUid, includeInstantApps); @@ -11710,6 +11717,9 @@ public class PackageManagerService extends IPackageManager.Stub mSettings.insertPackageSettingLPw(pkgSetting, pkg); // Add the new setting to mPackages mPackages.put(pkg.getAppInfoPackageName(), pkg); + if ((scanFlags & SCAN_AS_APK_IN_APEX) != 0) { + mApexManager.registerApkInApex(pkg); + } // Add the package's KeySets to the global KeySetManagerService KeySetManagerService ksms = mSettings.mKeySetManagerService; @@ -17757,10 +17767,10 @@ public class PackageManagerService extends IPackageManager.Stub ApexManager.ActiveApexInfo apexInfo) { for (int i = 0, size = SYSTEM_PARTITIONS.size(); i < size; i++) { SystemPartition sp = SYSTEM_PARTITIONS.get(i); - if (apexInfo.preinstalledApexPath.getAbsolutePath().startsWith( + if (apexInfo.preInstalledApexPath.getAbsolutePath().startsWith( sp.folder.getAbsolutePath())) { - return new SystemPartition(apexInfo.apexDirectory, sp.scanFlag, - false /* hasOverlays */); + return new SystemPartition(apexInfo.apexDirectory, + sp.scanFlag | SCAN_AS_APK_IN_APEX, false /* hasOverlays */); } } return null; @@ -20122,8 +20132,7 @@ public class PackageManagerService extends IPackageManager.Stub // Disable any carrier apps. We do this very early in boot to prevent the apps from being // disabled after already being started. CarrierAppUtils.disableCarrierAppsUntilPrivileged(mContext.getOpPackageName(), this, - mPermissionManagerService, mContext.getContentResolver(), - UserHandle.USER_SYSTEM); + mPermissionManagerService, UserHandle.USER_SYSTEM, mContext); disableSkuSpecificApps(); @@ -22791,7 +22800,7 @@ public class PackageManagerService extends IPackageManager.Stub ArrayList<String> systemPackageNames = new ArrayList<>(pkgNames.length); for (String pkgName: pkgNames) { - synchronized (mPackages) { + synchronized (mLock) { if (pkgName == null) { continue; } @@ -23452,6 +23461,11 @@ public class PackageManagerService extends IPackageManager.Stub } @Override + public List<String> getApksInApex(String apexPackageName) { + return PackageManagerService.this.mApexManager.getApksInApex(apexPackageName); + } + + @Override public void uninstallApex(String packageName, long versionCode, int userId, IntentSender intentSender, int flags) { final int callerUid = Binder.getCallingUid(); @@ -23632,7 +23646,7 @@ public class PackageManagerService extends IPackageManager.Stub @Nullable public PackageSetting getPackageSetting(String packageName) { - synchronized (mPackages) { + synchronized (mLock) { packageName = resolveInternalPackageNameLPr( packageName, PackageManager.VERSION_CODE_HIGHEST); return mSettings.mPackages.get(packageName); diff --git a/services/core/java/com/android/server/pm/StagingManager.java b/services/core/java/com/android/server/pm/StagingManager.java index 9e462cd529bb..2265d010216e 100644 --- a/services/core/java/com/android/server/pm/StagingManager.java +++ b/services/core/java/com/android/server/pm/StagingManager.java @@ -33,11 +33,13 @@ import android.content.pm.PackageInfo; import android.content.pm.PackageInstaller; import android.content.pm.PackageInstaller.SessionInfo; import android.content.pm.PackageManager; +import android.content.pm.PackageManagerInternal; import android.content.pm.PackageParser; import android.content.pm.PackageParser.PackageParserException; import android.content.pm.PackageParser.SigningDetails; import android.content.pm.PackageParser.SigningDetails.SignatureSchemeVersion; import android.content.pm.ParceledListSlice; +import android.content.pm.parsing.AndroidPackage; import android.content.rollback.IRollbackManager; import android.content.rollback.RollbackInfo; import android.content.rollback.RollbackManager; @@ -50,6 +52,8 @@ import android.os.ParcelFileDescriptor; import android.os.PowerManager; import android.os.RemoteException; import android.os.ServiceManager; +import android.os.UserHandle; +import android.os.UserManagerInternal; import android.os.storage.IStorageManager; import android.os.storage.StorageManager; import android.util.IntArray; @@ -61,6 +65,7 @@ import android.util.apk.ApkSignatureVerifier; import com.android.internal.annotations.GuardedBy; import com.android.internal.content.PackageHelper; import com.android.internal.os.BackgroundThread; +import com.android.server.LocalServices; import java.io.File; import java.io.IOException; @@ -93,10 +98,11 @@ public class StagingManager { @GuardedBy("mStagedSessions") private final SparseIntArray mSessionRollbackIds = new SparseIntArray(); - StagingManager(PackageInstallerService pi, ApexManager am, Context context) { + StagingManager(PackageInstallerService pi, Context context) { mPi = pi; - mApexManager = am; mContext = context; + + mApexManager = ApexManager.getInstance(); mPowerManager = (PowerManager) context.getSystemService(Context.POWER_SERVICE); mPreRebootVerificationHandler = new PreRebootVerificationHandler( BackgroundThread.get().getLooper()); @@ -334,6 +340,88 @@ public class StagingManager { return PackageHelper.getStorageManager().needsCheckpoint(); } + /** + * Apks inside apex are not installed using apk-install flow. They are scanned from the system + * directory directly by PackageManager, as such, RollbackManager need to handle their data + * separately here. + */ + private void snapshotAndRestoreApkInApexUserData(PackageInstallerSession session) { + // We want to process apks inside apex. So current session needs to contain apex. + if (!sessionContainsApex(session)) { + return; + } + + boolean doSnapshotOrRestore = + (session.params.installFlags & PackageManager.INSTALL_ENABLE_ROLLBACK) != 0 + || session.params.installReason == PackageManager.INSTALL_REASON_ROLLBACK; + if (!doSnapshotOrRestore) { + return; + } + + // Find all the apex sessions that needs processing + List<PackageInstallerSession> apexSessions = new ArrayList<>(); + if (session.isMultiPackage()) { + List<PackageInstallerSession> childrenSessions = new ArrayList<>(); + synchronized (mStagedSessions) { + for (int childSessionId : session.getChildSessionIds()) { + PackageInstallerSession childSession = mStagedSessions.get(childSessionId); + if (childSession != null) { + childrenSessions.add(childSession); + } + } + } + for (PackageInstallerSession childSession : childrenSessions) { + if (sessionContainsApex(childSession)) { + apexSessions.add(childSession); + } + } + } else { + apexSessions.add(session); + } + + // For each apex, process the apks inside it + for (PackageInstallerSession apexSession : apexSessions) { + List<String> apksInApex = mApexManager.getApksInApex(apexSession.getPackageName()); + for (String apk: apksInApex) { + snapshotAndRestoreApkInApexUserData(apk); + } + } + } + + private void snapshotAndRestoreApkInApexUserData(String packageName) { + IRollbackManager rm = IRollbackManager.Stub.asInterface( + ServiceManager.getService(Context.ROLLBACK_SERVICE)); + + PackageManagerInternal mPmi = LocalServices.getService(PackageManagerInternal.class); + AndroidPackage pkg = mPmi.getPackage(packageName); + if (pkg == null) { + Slog.e(TAG, "Could not find package: " + packageName + + "for snapshotting/restoring user data."); + return; + } + final String seInfo = pkg.getSeInfo(); + final UserManagerInternal um = LocalServices.getService(UserManagerInternal.class); + final int[] allUsers = um.getUserIds(); + + int appId = -1; + long ceDataInode = -1; + final PackageSetting ps = (PackageSetting) mPmi.getPackageSetting(packageName); + if (ps != null && rm != null) { + appId = ps.appId; + ceDataInode = ps.getCeDataInode(UserHandle.USER_SYSTEM); + // NOTE: We ignore the user specified in the InstallParam because we know this is + // an update, and hence need to restore data for all installed users. + final int[] installedUsers = ps.queryInstalledUsers(allUsers, true); + + try { + rm.snapshotAndRestoreUserData(packageName, installedUsers, appId, ceDataInode, + seInfo, 0 /*token*/); + } catch (RemoteException re) { + Slog.e(TAG, "Error snapshotting/restoring user data: " + re); + } + } + } + private void resumeSession(@NonNull PackageInstallerSession session) { Slog.d(TAG, "Resuming session " + session.sessionId); @@ -407,6 +495,7 @@ public class StagingManager { abortCheckpoint(); return; } + snapshotAndRestoreApkInApexUserData(session); Slog.i(TAG, "APEX packages in session " + session.sessionId + " were successfully activated. Proceeding with APK packages, if any"); } @@ -529,7 +618,7 @@ public class StagingManager { Arrays.stream(session.getChildSessionIds()) // Retrieve cached sessions matching ids. .mapToObj(i -> mStagedSessions.get(i)) - // Filter only the ones containing APKs.s + // Filter only the ones containing APKs. .filter(childSession -> !isApexSession(childSession)) .collect(Collectors.toList()); } diff --git a/services/core/java/com/android/server/pm/permission/PermissionManagerService.java b/services/core/java/com/android/server/pm/permission/PermissionManagerService.java index d921f313eb48..d8c196674c59 100644 --- a/services/core/java/com/android/server/pm/permission/PermissionManagerService.java +++ b/services/core/java/com/android/server/pm/permission/PermissionManagerService.java @@ -4005,23 +4005,130 @@ public class PermissionManagerService extends IPermissionManager.Stub { PackageManagerServiceUtils.enforceShellRestriction(mUserManagerInt, UserManager.DISALLOW_DEBUGGING_FEATURES, callingUid, userId); } - if (!requirePermissionWhenSameUser && userId == UserHandle.getUserId(callingUid)) return; - if (callingUid != Process.SYSTEM_UID && callingUid != Process.ROOT_UID) { - if (requireFullPermission) { - mContext.enforceCallingOrSelfPermission( - android.Manifest.permission.INTERACT_ACROSS_USERS_FULL, message); - } else { - try { - mContext.enforceCallingOrSelfPermission( - android.Manifest.permission.INTERACT_ACROSS_USERS_FULL, message); - } catch (SecurityException se) { - mContext.enforceCallingOrSelfPermission( - android.Manifest.permission.INTERACT_ACROSS_USERS, message); - } - } + final int callingUserId = UserHandle.getUserId(callingUid); + if (hasCrossUserPermission( + callingUid, callingUserId, userId, requireFullPermission, + requirePermissionWhenSameUser)) { + return; + } + String errorMessage = buildInvalidCrossUserPermissionMessage( + message, requireFullPermission); + Slog.w(TAG, errorMessage); + throw new SecurityException(errorMessage); + } + + /** + * Checks if the request is from the system or an app that has the appropriate cross-user + * permissions defined as follows: + * <ul> + * <li>INTERACT_ACROSS_USERS_FULL if {@code requireFullPermission} is true.</li> + * <li>INTERACT_ACROSS_USERS if the given {@userId} is in a different profile group + * to the caller.</li> + * <li>Otherwise, INTERACT_ACROSS_PROFILES if the given {@userId} is in the same profile group + * as the caller.</li> + * </ul> + * + * @param checkShell whether to prevent shell from access if there's a debugging restriction + * @param message the message to log on security exception + */ + private void enforceCrossUserOrProfilePermission(int callingUid, int userId, + boolean requireFullPermission, boolean checkShell, + String message) { + if (userId < 0) { + throw new IllegalArgumentException("Invalid userId " + userId); + } + if (checkShell) { + PackageManagerServiceUtils.enforceShellRestriction(mUserManagerInt, + UserManager.DISALLOW_DEBUGGING_FEATURES, callingUid, userId); + } + final int callingUserId = UserHandle.getUserId(callingUid); + if (hasCrossUserPermission(callingUid, callingUserId, userId, requireFullPermission, + /*requirePermissionWhenSameUser= */ false)) { + return; + } + final boolean isSameProfileGroup = isSameProfileGroup(callingUserId, userId); + if (isSameProfileGroup + && hasPermission(android.Manifest.permission.INTERACT_ACROSS_PROFILES)) { + return; + } + String errorMessage = buildInvalidCrossUserOrProfilePermissionMessage( + message, requireFullPermission, isSameProfileGroup); + Slog.w(TAG, errorMessage); + throw new SecurityException(errorMessage); + } + + private boolean hasCrossUserPermission( + int callingUid, int callingUserId, int userId, boolean requireFullPermission, + boolean requirePermissionWhenSameUser) { + if (!requirePermissionWhenSameUser && userId == callingUserId) { + return true; + } + if (callingUid == Process.SYSTEM_UID || callingUid == Process.ROOT_UID) { + return true; + } + if (requireFullPermission) { + return hasPermission(Manifest.permission.INTERACT_ACROSS_USERS_FULL); + } + return hasPermission(android.Manifest.permission.INTERACT_ACROSS_USERS_FULL) + || hasPermission(Manifest.permission.INTERACT_ACROSS_USERS); + } + + private boolean hasPermission(String permission) { + return mContext.checkCallingOrSelfPermission(permission) + == PackageManager.PERMISSION_GRANTED; + } + + private boolean isSameProfileGroup(@UserIdInt int callerUserId, @UserIdInt int userId) { + final long identity = Binder.clearCallingIdentity(); + try { + return UserManagerService.getInstance().isSameProfileGroup(callerUserId, userId); + } finally { + Binder.restoreCallingIdentity(identity); } } + private static String buildInvalidCrossUserPermissionMessage( + String message, boolean requireFullPermission) { + StringBuilder builder = new StringBuilder(); + if (message != null) { + builder.append(message); + builder.append(": "); + } + builder.append("Requires "); + builder.append(android.Manifest.permission.INTERACT_ACROSS_USERS_FULL); + if (requireFullPermission) { + builder.append("."); + return builder.toString(); + } + builder.append(" or "); + builder.append(android.Manifest.permission.INTERACT_ACROSS_USERS); + builder.append("."); + return builder.toString(); + } + + private static String buildInvalidCrossUserOrProfilePermissionMessage( + String message, boolean requireFullPermission, boolean isSameProfileGroup) { + StringBuilder builder = new StringBuilder(); + if (message != null) { + builder.append(message); + builder.append(": "); + } + builder.append("Requires "); + builder.append(android.Manifest.permission.INTERACT_ACROSS_USERS_FULL); + if (requireFullPermission) { + builder.append("."); + return builder.toString(); + } + builder.append(" or "); + builder.append(android.Manifest.permission.INTERACT_ACROSS_USERS); + if (isSameProfileGroup) { + builder.append(" or "); + builder.append(android.Manifest.permission.INTERACT_ACROSS_PROFILES); + } + builder.append("."); + return builder.toString(); + } + @GuardedBy({"mSettings.mLock", "mLock"}) private int calculateCurrentPermissionFootprintLocked(BasePermission tree) { int size = 0; @@ -4215,6 +4322,17 @@ public class PermissionManagerService extends IPermissionManager.Stub { PermissionManagerService.this.enforceCrossUserPermission(callingUid, userId, requireFullPermission, checkShell, requirePermissionWhenSameUser, message); } + + @Override + public void enforceCrossUserOrProfilePermission(int callingUid, int userId, + boolean requireFullPermission, boolean checkShell, String message) { + PermissionManagerService.this.enforceCrossUserOrProfilePermission(callingUid, + userId, + requireFullPermission, + checkShell, + message); + } + @Override public void enforceGrantRevokeRuntimePermissionPermissions(String message) { PermissionManagerService.this.enforceGrantRevokeRuntimePermissionPermissions(message); diff --git a/services/core/java/com/android/server/pm/permission/PermissionManagerServiceInternal.java b/services/core/java/com/android/server/pm/permission/PermissionManagerServiceInternal.java index 0f22619fafa6..58a9f42f372d 100644 --- a/services/core/java/com/android/server/pm/permission/PermissionManagerServiceInternal.java +++ b/services/core/java/com/android/server/pm/permission/PermissionManagerServiceInternal.java @@ -271,6 +271,15 @@ public abstract class PermissionManagerServiceInternal extends PermissionManager */ public abstract void enforceCrossUserPermission(int callingUid, int userId, boolean requireFullPermission, boolean checkShell, @NonNull String message); + + /** + * Similar to {@link #enforceCrossUserPermission(int, int, boolean, boolean, String)} + * but also allows INTERACT_ACROSS_PROFILES permission if calling user and {@code userId} are + * in the same profile group. + */ + public abstract void enforceCrossUserOrProfilePermission(int callingUid, int userId, + boolean requireFullPermission, boolean checkShell, @NonNull String message); + /** * @see #enforceCrossUserPermission(int, int, boolean, boolean, String) * @param requirePermissionWhenSameUser When {@code true}, still require the cross user diff --git a/services/core/java/com/android/server/rollback/Rollback.java b/services/core/java/com/android/server/rollback/Rollback.java index 88c1564fdb60..9f592b85a5e6 100644 --- a/services/core/java/com/android/server/rollback/Rollback.java +++ b/services/core/java/com/android/server/rollback/Rollback.java @@ -62,7 +62,7 @@ class Rollback { private static final String TAG = "RollbackManager"; - @IntDef(flag = true, prefix = { "ROLLBACK_STATE_" }, value = { + @IntDef(prefix = { "ROLLBACK_STATE_" }, value = { ROLLBACK_STATE_ENABLING, ROLLBACK_STATE_AVAILABLE, ROLLBACK_STATE_COMMITTED, @@ -92,6 +92,19 @@ class Rollback { */ static final int ROLLBACK_STATE_DELETED = 4; + @IntDef(flag = true, prefix = { "MATCH_" }, value = { + MATCH_APK_IN_APEX, + }) + @Retention(RetentionPolicy.SOURCE) + @interface RollbackInfoFlags {} + + /** + * {@link RollbackInfo} flag: include {@code RollbackInfo} packages that are apk-in-apex. + * These packages do not have their own sessions. They are embedded in an apex which has a + * session id. + */ + static final int MATCH_APK_IN_APEX = 1; + /** * The session ID for the staged session if this rollback data represents a staged session, * {@code -1} otherwise. @@ -323,8 +336,8 @@ class Rollback { new VersionedPackage(packageName, newVersion), new VersionedPackage(packageName, installedVersion), new IntArray() /* pendingBackups */, new ArrayList<>() /* pendingRestores */, - isApex, new IntArray(), new SparseLongArray() /* ceSnapshotInodes */, - rollbackDataPolicy); + isApex, false /* isApkInApex */, new IntArray(), + new SparseLongArray() /* ceSnapshotInodes */, rollbackDataPolicy); synchronized (mLock) { info.getPackages().add(packageRollbackInfo); @@ -334,6 +347,30 @@ class Rollback { } /** + * Enables this rollback for the provided apk-in-apex. + * + * @return boolean True if the rollback was enabled successfully for the specified package. + */ + boolean enableForPackageInApex(String packageName, long installedVersion, + int rollbackDataPolicy) { + // TODO(b/142712057): Extract the new version number of apk-in-apex + // The new version for the apk-in-apex is set to 0 for now. If the package is then further + // updated via non-staged install flow, then RollbackManagerServiceImpl#onPackageReplaced() + // will be called and this rollback will be deleted. Other ways of package update have not + // been handled yet. + PackageRollbackInfo packageRollbackInfo = new PackageRollbackInfo( + new VersionedPackage(packageName, 0 /* newVersion */), + new VersionedPackage(packageName, installedVersion), + new IntArray() /* pendingBackups */, new ArrayList<>() /* pendingRestores */, + false /* isApex */, true /* isApkInApex */, new IntArray(), + new SparseLongArray() /* ceSnapshotInodes */, rollbackDataPolicy); + synchronized (mLock) { + info.getPackages().add(packageRollbackInfo); + } + return true; + } + + /** * Snapshots user data for the provided package and user ids. Does nothing if this rollback is * not in the ENABLING state. */ @@ -428,6 +465,11 @@ class Rollback { parentSessionId); for (PackageRollbackInfo pkgRollbackInfo : info.getPackages()) { + if (pkgRollbackInfo.isApkInApex()) { + // No need to issue a downgrade install request for apk-in-apex. It will + // be rolled back when its parent apex is downgraded. + continue; + } PackageInstaller.SessionParams params = new PackageInstaller.SessionParams( PackageInstaller.SessionParams.MODE_FULL_INSTALL); String installerPackageName = mInstallerPackageName; @@ -453,7 +495,8 @@ class Rollback { this, pkgRollbackInfo.getPackageName()); if (packageCodePaths == null) { sendFailure(context, statusReceiver, RollbackManager.STATUS_FAILURE, - "Backup copy of package inaccessible"); + "Backup copy of package: " + + pkgRollbackInfo.getPackageName() + " is inaccessible"); return; } @@ -696,9 +739,30 @@ class Rollback { } } - int getPackageCount() { + /** + * Returns the number of {@link PackageRollbackInfo} we are storing in this {@link Rollback} + * instance. By default, this method does not include apk-in-apex package in the count. + * + * @param flags Apk-in-apex packages can be included in the count by passing + * {@link Rollback#MATCH_APK_IN_APEX} + * + * @return Counts number of {@link PackageRollbackInfo} stored in the {@link Rollback} + * according to {@code flags} passed + */ + int getPackageCount(@RollbackInfoFlags int flags) { synchronized (mLock) { - return info.getPackages().size(); + List<PackageRollbackInfo> packages = info.getPackages(); + if ((flags & MATCH_APK_IN_APEX) != 0) { + return packages.size(); + } + + int packagesWithoutApkInApex = 0; + for (PackageRollbackInfo rollbackInfo : packages) { + if (!rollbackInfo.isApkInApex()) { + packagesWithoutApkInApex++; + } + } + return packagesWithoutApkInApex; } } diff --git a/services/core/java/com/android/server/rollback/RollbackManagerServiceImpl.java b/services/core/java/com/android/server/rollback/RollbackManagerServiceImpl.java index e29d1a765d69..8f8a5c4b14e9 100644 --- a/services/core/java/com/android/server/rollback/RollbackManagerServiceImpl.java +++ b/services/core/java/com/android/server/rollback/RollbackManagerServiceImpl.java @@ -891,9 +891,36 @@ class RollbackManagerServiceImpl extends IRollbackManager.Stub { } ApplicationInfo appInfo = pkgInfo.applicationInfo; - return rollback.enableForPackage(packageName, newPackage.versionCode, + boolean success = rollback.enableForPackage(packageName, newPackage.versionCode, pkgInfo.getLongVersionCode(), isApex, appInfo.sourceDir, appInfo.splitSourceDirs, session.rollbackDataPolicy); + if (!success) { + return success; + } + + if (isApex) { + // Check if this apex contains apks inside it. If true, then they should be added as + // a RollbackPackageInfo into this rollback + final PackageManagerInternal pmi = LocalServices.getService( + PackageManagerInternal.class); + List<String> apksInApex = pmi.getApksInApex(packageName); + for (String apkInApex : apksInApex) { + // Get information about the currently installed package. + final PackageInfo apkPkgInfo; + try { + apkPkgInfo = getPackageInfo(apkInApex); + } catch (PackageManager.NameNotFoundException e) { + // TODO: Support rolling back fresh package installs rather than + // fail here. Test this case. + Slog.e(TAG, apkInApex + " is not installed"); + return false; + } + success = rollback.enableForPackageInApex( + apkInApex, apkPkgInfo.getLongVersionCode(), session.rollbackDataPolicy); + if (!success) return success; + } + } + return true; } @Override @@ -907,9 +934,13 @@ class RollbackManagerServiceImpl extends IRollbackManager.Stub { getHandler().post(() -> { snapshotUserDataInternal(packageName, userIds); restoreUserDataInternal(packageName, userIds, appId, seInfo); - final PackageManagerInternal pmi = LocalServices.getService( - PackageManagerInternal.class); - pmi.finishPackageInstall(token, false); + // When this method is called as part of the install flow, a positive token number is + // passed to it. Need to notify the PackageManager when we are done. + if (token > 0) { + final PackageManagerInternal pmi = LocalServices.getService( + PackageManagerInternal.class); + pmi.finishPackageInstall(token, false); + } }); } @@ -1195,7 +1226,11 @@ class RollbackManagerServiceImpl extends IRollbackManager.Stub { return null; } - if (rollback.getPackageCount() != newRollback.getPackageSessionIdCount()) { + // We are checking if number of packages (excluding apk-in-apex) we enabled for rollback is + // equal to the number of sessions we are installing, to ensure we didn't skip enabling + // of any sessions. If we successfully enable an apex, then we can assume we enabled + // rollback for the embedded apk-in-apex, if any. + if (rollback.getPackageCount(0 /*flags*/) != newRollback.getPackageSessionIdCount()) { Slog.e(TAG, "Failed to enable rollback for all packages in session."); rollback.delete(mAppDataRollbackHelper); return null; diff --git a/services/core/java/com/android/server/rollback/RollbackStore.java b/services/core/java/com/android/server/rollback/RollbackStore.java index df75a29edd79..bbcd0def05a8 100644 --- a/services/core/java/com/android/server/rollback/RollbackStore.java +++ b/services/core/java/com/android/server/rollback/RollbackStore.java @@ -341,6 +341,7 @@ class RollbackStore { json.put("pendingRestores", convertToJsonArray(pendingRestores)); json.put("isApex", info.isApex()); + json.put("isApkInApex", info.isApkInApex()); // Field is named 'installedUsers' for legacy reasons. json.put("installedUsers", convertToJsonArray(snapshottedUsers)); @@ -364,6 +365,7 @@ class RollbackStore { json.getJSONArray("pendingRestores")); final boolean isApex = json.getBoolean("isApex"); + final boolean isApkInApex = json.getBoolean("isApkInApex"); // Field is named 'installedUsers' for legacy reasons. final IntArray snapshottedUsers = convertToIntArray(json.getJSONArray("installedUsers")); @@ -375,8 +377,8 @@ class RollbackStore { PackageManager.RollbackDataPolicy.RESTORE); return new PackageRollbackInfo(versionRolledBackFrom, versionRolledBackTo, - pendingBackups, pendingRestores, isApex, snapshottedUsers, ceSnapshotInodes, - rollbackDataPolicy); + pendingBackups, pendingRestores, isApex, isApkInApex, snapshottedUsers, + ceSnapshotInodes, rollbackDataPolicy); } private static JSONArray versionedPackagesToJson(List<VersionedPackage> packages) diff --git a/services/core/java/com/android/server/soundtrigger_middleware/SoundTriggerMiddlewareService.java b/services/core/java/com/android/server/soundtrigger_middleware/SoundTriggerMiddlewareService.java index 987c05fd8c9d..5a06a2c4b388 100644 --- a/services/core/java/com/android/server/soundtrigger_middleware/SoundTriggerMiddlewareService.java +++ b/services/core/java/com/android/server/soundtrigger_middleware/SoundTriggerMiddlewareService.java @@ -20,6 +20,7 @@ import android.Manifest; import android.annotation.NonNull; import android.annotation.Nullable; import android.content.Context; +import android.content.PermissionChecker; import android.hardware.soundtrigger.V2_0.ISoundTriggerHw; import android.media.soundtrigger_middleware.ISoundTriggerCallback; import android.media.soundtrigger_middleware.ISoundTriggerMiddlewareService; @@ -32,6 +33,7 @@ import android.media.soundtrigger_middleware.RecognitionEvent; import android.media.soundtrigger_middleware.RecognitionStatus; import android.media.soundtrigger_middleware.SoundModel; import android.media.soundtrigger_middleware.SoundTriggerModuleDescriptor; +import android.media.soundtrigger_middleware.Status; import android.os.RemoteException; import android.os.ServiceSpecificException; import android.util.Log; @@ -223,23 +225,48 @@ public class SoundTriggerMiddlewareService extends ISoundTriggerMiddlewareServic } /** - * Throws a {@link SecurityException} if caller doesn't have the right permissions to use this - * service. + * Throws a {@link SecurityException} if caller permanently doesn't have the given permission, + * or a {@link ServiceSpecificException} with a {@link Status#TEMPORARY_PERMISSION_DENIED} if + * caller temporarily doesn't have the right permissions to use this service. */ private void checkPermissions() { - mContext.enforceCallingOrSelfPermission(Manifest.permission.RECORD_AUDIO, - "Caller must have the android.permission.RECORD_AUDIO permission."); - mContext.enforceCallingOrSelfPermission(Manifest.permission.CAPTURE_AUDIO_HOTWORD, - "Caller must have the android.permission.CAPTURE_AUDIO_HOTWORD permission."); + enforcePermission(Manifest.permission.RECORD_AUDIO); + enforcePermission(Manifest.permission.CAPTURE_AUDIO_HOTWORD); } /** - * Throws a {@link SecurityException} if caller doesn't have the right permissions to preempt - * active sound trigger sessions. + * Throws a {@link SecurityException} if caller permanently doesn't have the given permission, + * or a {@link ServiceSpecificException} with a {@link Status#TEMPORARY_PERMISSION_DENIED} if + * caller temporarily doesn't have the right permissions to preempt active sound trigger + * sessions. */ private void checkPreemptPermissions() { - mContext.enforceCallingOrSelfPermission(Manifest.permission.PREEMPT_SOUND_TRIGGER, - "Caller must have the android.permission.PREEMPT_SOUND_TRIGGER permission."); + enforcePermission(Manifest.permission.PREEMPT_SOUND_TRIGGER); + } + + /** + * Throws a {@link SecurityException} if caller permanently doesn't have the given permission, + * or a {@link ServiceSpecificException} with a {@link Status#TEMPORARY_PERMISSION_DENIED} if + * caller temporarily doesn't have the given permission. + * + * @param permission The permission to check. + */ + private void enforcePermission(String permission) { + final int status = PermissionChecker.checkCallingOrSelfPermissionForPreflight(mContext, + permission); + switch (status) { + case PermissionChecker.PERMISSION_GRANTED: + return; + case PermissionChecker.PERMISSION_DENIED: + throw new SecurityException( + String.format("Caller must have the %s permission.", permission)); + case PermissionChecker.PERMISSION_DENIED_APP_OP: + throw new ServiceSpecificException(Status.TEMPORARY_PERMISSION_DENIED, + String.format("Caller must have the %s permission.", permission)); + default: + throw new InternalServerError( + new RuntimeException("Unexpected perimission check result.")); + } } /** State of a sound model. */ diff --git a/services/core/java/com/android/server/wm/ActivityStack.java b/services/core/java/com/android/server/wm/ActivityStack.java index c95943904d1f..9fd3ea4fc090 100644 --- a/services/core/java/com/android/server/wm/ActivityStack.java +++ b/services/core/java/com/android/server/wm/ActivityStack.java @@ -163,6 +163,7 @@ import android.view.DisplayCutout; import android.view.DisplayInfo; import android.view.RemoteAnimationTarget; import android.view.SurfaceControl; +import android.view.ITaskOrganizer; import com.android.internal.annotations.GuardedBy; import com.android.internal.annotations.VisibleForTesting; @@ -803,6 +804,16 @@ class ActivityStack extends WindowContainer<WindowContainer> implements BoundsAn setWindowingMode(windowingMode, false /* animate */, false /* showRecents */, false /* enteringSplitScreenMode */, false /* deferEnsuringVisibility */, false /* creating */); + + windowingMode = getWindowingMode(); + /* + * Different windowing modes may be managed by different task organizers. If + * getTaskOrganizer returns null, we still call transferToTaskOrganizer to + * make sure we clear it. + */ + final ITaskOrganizer org = + mWmService.mAtmService.mTaskOrganizerController.getTaskOrganizer(windowingMode); + transferToTaskOrganizer(org); } /** @@ -1650,6 +1661,33 @@ class ActivityStack extends WindowContainer<WindowContainer> implements BoundsAn } /** + * Indicate whether the first task in this stack is controlled by a TaskOrganizer. We aren't + * expecting to use the TaskOrganizer in multiple task per stack scenarios so checking + * the first one is ok. + */ + boolean isControlledByTaskOrganizer() { + return getChildCount() > 0 && getTopMostTask().mTaskOrganizer != null; + } + + private static void transferSingleTaskToOrganizer(Task tr, ITaskOrganizer organizer) { + tr.setTaskOrganizer(organizer); + } + + /** + * Transfer control of the leashes and IWindowContainers to the given ITaskOrganizer. + * This will (or shortly there-after) invoke the taskAppeared callbacks. + * If the tasks had a previous TaskOrganizer, setTaskOrganizer will take care of + * emitting the taskVanished callbacks. + */ + void transferToTaskOrganizer(ITaskOrganizer organizer) { + final PooledConsumer c = PooledLambda.obtainConsumer( + ActivityStack::transferSingleTaskToOrganizer, + PooledLambda.__(Task.class), organizer); + forAllTasks(c); + c.recycle(); + } + + /** * Returns true if the stack should be visible. * * @param starting The currently starting activity or null if there is none. @@ -3577,6 +3615,15 @@ class ActivityStack extends WindowContainer<WindowContainer> implements BoundsAn void animateResizePinnedStack(Rect toBounds, Rect sourceHintBounds, int animationDuration, boolean fromFullscreen) { if (!inPinnedWindowingMode()) return; + + /** + * TODO(b/146594635): Remove all PIP animation code from WM once SysUI handles animation. + * If this PIP Task is controlled by a TaskOrganizer, the animation occurs entirely + * on the TaskOrganizer side, so we just hand over the leash without doing any animation. + * We have to be careful to not schedule the enter-pip callback as the TaskOrganizer + * needs to have flexibility to schedule that at an appropriate point in the animation. + */ + if (isControlledByTaskOrganizer()) return; if (toBounds == null /* toFullscreen */) { final Configuration parentConfig = getParent().getConfiguration(); final ActivityRecord top = topRunningNonOverlayTaskActivity(); diff --git a/services/core/java/com/android/server/wm/ActivityStackSupervisor.java b/services/core/java/com/android/server/wm/ActivityStackSupervisor.java index d63165adb2f8..aa90248b97f8 100644 --- a/services/core/java/com/android/server/wm/ActivityStackSupervisor.java +++ b/services/core/java/com/android/server/wm/ActivityStackSupervisor.java @@ -2548,6 +2548,7 @@ public class ActivityStackSupervisor implements RecentTasks.Callbacks { final PooledConsumer c = PooledLambda.obtainConsumer( ActivityRecord::updatePictureInPictureMode, PooledLambda.__(ActivityRecord.class), targetStackBounds, forceUpdate); + task.getStack().setBounds(targetStackBounds); task.forAllActivities(c); c.recycle(); } diff --git a/services/core/java/com/android/server/wm/ActivityTaskManagerService.java b/services/core/java/com/android/server/wm/ActivityTaskManagerService.java index 474c5c960eb0..ded603c9fd77 100644 --- a/services/core/java/com/android/server/wm/ActivityTaskManagerService.java +++ b/services/core/java/com/android/server/wm/ActivityTaskManagerService.java @@ -226,6 +226,7 @@ import android.util.StatsLog; import android.util.TimeUtils; import android.util.proto.ProtoOutputStream; import android.view.IRecentsAnimationRunner; +import android.view.ITaskOrganizer; import android.view.RemoteAnimationAdapter; import android.view.RemoteAnimationDefinition; import android.view.WindowContainerTransaction; @@ -662,6 +663,12 @@ public class ActivityTaskManagerService extends IActivityTaskManager.Stub { private FontScaleSettingObserver mFontScaleSettingObserver; + /** + * Stores the registration and state of TaskOrganizers in use. + */ + TaskOrganizerController mTaskOrganizerController = + new TaskOrganizerController(this, mGlobalLock); + private int mDeviceOwnerUid = Process.INVALID_UID; private final class FontScaleSettingObserver extends ContentObserver { @@ -1271,6 +1278,14 @@ public class ActivityTaskManagerService extends IActivityTaskManager.Stub { .execute(); } + @Override + public final void registerTaskOrganizer(ITaskOrganizer organizer, int windowingMode) { + enforceCallerIsRecentsOrHasPermission( + MANAGE_ACTIVITY_STACKS, "registerTaskOrganizer()"); + synchronized (mGlobalLock) { + mTaskOrganizerController.registerTaskOrganizer(organizer, windowingMode); + } + } @Override public IBinder requestStartActivityPermissionToken(IBinder delegatorToken) { @@ -3319,6 +3334,18 @@ public class ActivityTaskManagerService extends IActivityTaskManager.Stub { } } + private void applyWindowContainerChange(ConfigurationContainer cc, + WindowContainerTransaction.Change c) { + sanitizeAndApplyConfigChange(cc, c); + + Rect enterPipBounds = c.getEnterPipBounds(); + if (enterPipBounds != null) { + Task tr = (Task) cc; + mStackSupervisor.updatePictureInPictureMode(tr, + enterPipBounds, true); + } + } + @Override public void applyContainerTransaction(WindowContainerTransaction t) { mAmInternal.enforceCallingPermission(MANAGE_ACTIVITY_STACKS, "applyContainerTransaction()"); @@ -3335,7 +3362,7 @@ public class ActivityTaskManagerService extends IActivityTaskManager.Stub { entries.next(); final ConfigurationContainer cc = ConfigurationContainer.RemoteToken.fromBinder( entry.getKey()).getContainer(); - sanitizeAndApplyConfigChange(cc, entry.getValue()); + applyWindowContainerChange(cc, entry.getValue()); } } } finally { @@ -4057,7 +4084,11 @@ public class ActivityTaskManagerService extends IActivityTaskManager.Stub { throw new IllegalArgumentException("Stack: " + stack + " doesn't support animated resize."); } - if (animate) { + /** + * TODO(b/146594635): Remove all PIP animation code from WM + * once SysUI handles animation. Don't even try to animate TaskOrganized tasks. + */ + if (animate && !stack.isControlledByTaskOrganizer()) { stack.animateResizePinnedStack(null /* destBounds */, null /* sourceHintBounds */, animationDuration, false /* fromFullscreen */); diff --git a/services/core/java/com/android/server/wm/ConfigurationContainer.java b/services/core/java/com/android/server/wm/ConfigurationContainer.java index dd3365c900d7..d0310f1a7607 100644 --- a/services/core/java/com/android/server/wm/ConfigurationContainer.java +++ b/services/core/java/com/android/server/wm/ConfigurationContainer.java @@ -666,4 +666,8 @@ public abstract class ConfigurationContainer<E extends ConfigurationContainer> { return sb.toString(); } } + + RemoteToken getRemoteToken() { + return mRemoteToken; + } } diff --git a/services/core/java/com/android/server/wm/InputMonitor.java b/services/core/java/com/android/server/wm/InputMonitor.java index c4b67d76607e..091f66c0b19a 100644 --- a/services/core/java/com/android/server/wm/InputMonitor.java +++ b/services/core/java/com/android/server/wm/InputMonitor.java @@ -279,6 +279,19 @@ final class InputMonitor { // we avoid reintroducing this concept by just choosing one of them here. inputWindowHandle.surfaceInset = child.getAttrs().surfaceInsets.left; + /** + * If the window is in a TaskManaged by a TaskOrganizer then most cropping + * will be applied using the SurfaceControl hierarchy from the Organizer. + * This means we need to make sure that these changes in crop are reflected + * in the input windows, and so ensure this flag is set so that + * the input crop always reflects the surface hierarchy. + * we may have some issues with modal-windows, but I guess we can + * cross that bridge when we come to implementing full-screen TaskOrg + */ + if (child.getTask() != null && child.getTask().isControlledByTaskOrganizer()) { + inputWindowHandle.replaceTouchableRegionWithCrop(null /* Use this surfaces crop */); + } + if (child.mGlobalScale != 1) { // If we are scaling the window, input coordinates need // to be inversely scaled to map from what is on screen diff --git a/services/core/java/com/android/server/wm/RootWindowContainer.java b/services/core/java/com/android/server/wm/RootWindowContainer.java index a7bf6600d7b5..c3e815d10dda 100644 --- a/services/core/java/com/android/server/wm/RootWindowContainer.java +++ b/services/core/java/com/android/server/wm/RootWindowContainer.java @@ -2168,12 +2168,18 @@ class RootWindowContainer extends WindowContainer<DisplayContent> mService.continueWindowLayout(); } + // TODO(b/146594635): Remove all PIP animation code from WM once SysUI handles animation. // Notify the pinned stack controller to prepare the PiP animation, expect callback - // delivered from SystemUI to WM to start the animation. - final PinnedStackController pinnedStackController = + // delivered from SystemUI to WM to start the animation. Unless we are using + // the TaskOrganizer in which case the animation will be entirely handled + // on that side. + if (mService.mTaskOrganizerController.getTaskOrganizer(WINDOWING_MODE_PINNED) + == null) { + final PinnedStackController pinnedStackController = display.mDisplayContent.getPinnedStackController(); - pinnedStackController.prepareAnimation(sourceHintBounds, aspectRatio, - null /* stackBounds */); + pinnedStackController.prepareAnimation(sourceHintBounds, aspectRatio, + null /* stackBounds */); + } // TODO: revisit the following statement after the animation is moved from WM to SysUI. // Update the visibility of all activities after the they have been reparented to the new diff --git a/services/core/java/com/android/server/wm/Task.java b/services/core/java/com/android/server/wm/Task.java index 9a140daad417..5cb7091bbed0 100644 --- a/services/core/java/com/android/server/wm/Task.java +++ b/services/core/java/com/android/server/wm/Task.java @@ -117,9 +117,11 @@ import android.content.pm.ApplicationInfo; import android.content.pm.IPackageManager; import android.content.pm.PackageManager; import android.content.res.Configuration; +import android.graphics.Point; import android.graphics.Rect; import android.os.Debug; import android.os.IBinder; +import android.os.Parcel; import android.os.RemoteException; import android.os.SystemClock; import android.os.Trace; @@ -130,6 +132,7 @@ import android.util.DisplayMetrics; import android.util.Slog; import android.util.proto.ProtoOutputStream; import android.view.DisplayInfo; +import android.view.ITaskOrganizer; import android.view.RemoteAnimationTarget; import android.view.Surface; import android.view.SurfaceControl; @@ -425,6 +428,14 @@ class Task extends WindowContainer<WindowContainer> { } /** + * The TaskOrganizer which is delegated presentation of this task. If set the Task will + * emit an IWindowContainer (allowing access to it's SurfaceControl leash) to the organizers + * taskAppeared callback, and emit a taskRemoved callback when the Task is vanished. + */ + ITaskOrganizer mTaskOrganizer; + + + /** * Don't use constructor directly. Use {@link #create(ActivityTaskManagerService, int, * ActivityInfo, Intent, TaskDescription)} instead. */ @@ -445,6 +456,22 @@ class Task extends WindowContainer<WindowContainer> { _voiceSession, _voiceInteractor, stack); } + class TaskToken extends RemoteToken { + TaskToken(ConfigurationContainer container) { + super(container); + } + + @Override + public SurfaceControl getLeash() { + // We need to copy the SurfaceControl instead of returning the original + // because the Parcel FLAGS PARCELABLE_WRITE_RETURN_VALUE cause SurfaceControls + // to release themselves. + SurfaceControl sc = new SurfaceControl(); + sc.copyFrom(getSurfaceControl()); + return sc; + } + } + /** Don't use constructor directly. This is only used by XML parser. */ Task(ActivityTaskManagerService atmService, int _taskId, Intent _intent, Intent _affinityIntent, String _affinity, String _rootAffinity, @@ -469,7 +496,7 @@ class Task extends WindowContainer<WindowContainer> { mTaskDescription = _lastTaskDescription; // Tasks have no set orientation value (including SCREEN_ORIENTATION_UNSPECIFIED). setOrientation(SCREEN_ORIENTATION_UNSET); - mRemoteToken = new RemoteToken(this); + mRemoteToken = new TaskToken(this); affinityIntent = _affinityIntent; affinity = _affinity; rootAffinity = _rootAffinity; @@ -2179,6 +2206,10 @@ class Task extends WindowContainer<WindowContainer> { void removeImmediately() { if (DEBUG_STACK) Slog.i(TAG, "removeTask: removing taskId=" + mTaskId); EventLogTags.writeWmTaskRemoved(mTaskId, "removeTask"); + + // If applicable let the TaskOrganizer know the Task is vanishing. + setTaskOrganizer(null); + super.removeImmediately(); } @@ -2567,6 +2598,12 @@ class Task extends WindowContainer<WindowContainer> { } boolean shouldAnimate() { + /** + * Animations are handled by the TaskOrganizer implementation. + */ + if (isControlledByTaskOrganizer()) { + return false; + } // Don't animate while the task runs recents animation but only if we are in the mode // where we cancel with deferred screenshot, which means that the controller has // transformed the task. @@ -3444,4 +3481,91 @@ class Task extends WindowContainer<WindowContainer> { XmlUtils.skipCurrentTag(in); } } + + boolean isControlledByTaskOrganizer() { + return mTaskOrganizer != null; + } + + @Override + protected void reparentSurfaceControl(SurfaceControl.Transaction t, SurfaceControl newParent) { + /** + * Avoid yanking back control from the TaskOrganizer, which has presumably reparented the + * Surface in to its own hierarchy. + */ + if (isControlledByTaskOrganizer()) { + return; + } + super.reparentSurfaceControl(t, newParent); + } + + private void sendTaskAppeared() { + if (mSurfaceControl != null && mTaskOrganizer != null) { + mAtmService.mTaskOrganizerController.onTaskAppeared(mTaskOrganizer, this); + } + } + + private void sendTaskVanished() { + if (mTaskOrganizer != null) { + mAtmService.mTaskOrganizerController.onTaskVanished(mTaskOrganizer, this); + } + } + + void setTaskOrganizer(ITaskOrganizer organizer) { + // Let the old organizer know it has lost control. + if (mTaskOrganizer != null) { + sendTaskVanished(); + } + mTaskOrganizer = organizer; + sendTaskAppeared(); + } + + // Called on Binder death. + void taskOrganizerDied() { + mTaskOrganizer = null; + } + + @Override + void setSurfaceControl(SurfaceControl sc) { + super.setSurfaceControl(sc); + // If the TaskOrganizer was set before we created the SurfaceControl, we need to + // emit the callbacks now. + sendTaskAppeared(); + } + + @Override + public void updateSurfacePosition() { + // Avoid fighting with the TaskOrganizer over Surface position. + if (isControlledByTaskOrganizer()) { + getPendingTransaction().setPosition(mSurfaceControl, 0, 0); + scheduleAnimation(); + return; + } else { + super.updateSurfacePosition(); + } + } + + @Override + void getRelativeDisplayedPosition(Point outPos) { + // In addition to updateSurfacePosition, we keep other code that sets + // position from fighting with the TaskOrganizer + if (isControlledByTaskOrganizer()) { + outPos.set(0, 0); + return; + } + super.getRelativeDisplayedPosition(outPos); + } + + @Override + public void setWindowingMode(int windowingMode) { + super.setWindowingMode(windowingMode); + windowingMode = getWindowingMode(); + /* + * Different windowing modes may be managed by different task organizers. If + * getTaskOrganizer returns null, we still call transferToTaskOrganizer to + * make sure we clear it. + */ + final ITaskOrganizer org = + mAtmService.mTaskOrganizerController.getTaskOrganizer(windowingMode); + setTaskOrganizer(org); + } } diff --git a/services/core/java/com/android/server/wm/TaskOrganizerController.java b/services/core/java/com/android/server/wm/TaskOrganizerController.java new file mode 100644 index 000000000000..283be4010677 --- /dev/null +++ b/services/core/java/com/android/server/wm/TaskOrganizerController.java @@ -0,0 +1,167 @@ +/* + * Copyright (C) 2019 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.server.wm; + +import static android.app.WindowConfiguration.WINDOWING_MODE_PINNED; + +import android.os.IBinder; +import android.os.RemoteException; +import android.util.Slog; +import android.view.ITaskOrganizer; +import android.view.SurfaceControl; + +import java.util.ArrayList; +import java.util.HashMap; + +/** + * Stores the TaskOrganizers associated with a given windowing mode and + * their associated state. + */ +class TaskOrganizerController { + private static final String TAG = "TaskOrganizerController"; + + private WindowManagerGlobalLock mGlobalLock; + + private class DeathRecipient implements IBinder.DeathRecipient { + int mWindowingMode; + ITaskOrganizer mTaskOrganizer; + + DeathRecipient(ITaskOrganizer organizer, int windowingMode) { + mTaskOrganizer = organizer; + mWindowingMode = windowingMode; + } + + @Override + public void binderDied() { + synchronized (mGlobalLock) { + final TaskOrganizerState state = mTaskOrganizerStates.get(mTaskOrganizer); + for (int i = 0; i < state.mOrganizedTasks.size(); i++) { + state.mOrganizedTasks.get(i).taskOrganizerDied(); + } + mTaskOrganizerStates.remove(mTaskOrganizer); + if (mTaskOrganizersForWindowingMode.get(mWindowingMode) == mTaskOrganizer) { + mTaskOrganizersForWindowingMode.remove(mWindowingMode); + } + } + } + }; + + class TaskOrganizerState { + ITaskOrganizer mOrganizer; + DeathRecipient mDeathRecipient; + + ArrayList<Task> mOrganizedTasks = new ArrayList<>(); + + void addTask(Task t) { + mOrganizedTasks.add(t); + } + + void removeTask(Task t) { + mOrganizedTasks.remove(t); + } + + TaskOrganizerState(ITaskOrganizer organizer, DeathRecipient deathRecipient) { + mOrganizer = organizer; + mDeathRecipient = deathRecipient; + } + }; + + + final HashMap<Integer, TaskOrganizerState> mTaskOrganizersForWindowingMode = new HashMap(); + final HashMap<ITaskOrganizer, TaskOrganizerState> mTaskOrganizerStates = new HashMap(); + + final HashMap<Integer, ITaskOrganizer> mTaskOrganizersByPendingSyncId = new HashMap(); + + final ActivityTaskManagerService mService; + + TaskOrganizerController(ActivityTaskManagerService atm, WindowManagerGlobalLock lock) { + mService = atm; + mGlobalLock = lock; + } + + private void clearIfNeeded(int windowingMode) { + final TaskOrganizerState oldState = mTaskOrganizersForWindowingMode.get(windowingMode); + if (oldState != null) { + oldState.mOrganizer.asBinder().unlinkToDeath(oldState.mDeathRecipient, 0); + } + } + + /** + * Register a TaskOrganizer to manage tasks as they enter the given windowing mode. + * If there was already a TaskOrganizer for this windowing mode it will be evicted + * and receive taskVanished callbacks in the process. + */ + void registerTaskOrganizer(ITaskOrganizer organizer, int windowingMode) { + if (windowingMode != WINDOWING_MODE_PINNED) { + throw new UnsupportedOperationException( + "As of now only Pinned windowing mode is supported for registerTaskOrganizer"); + + } + clearIfNeeded(windowingMode); + DeathRecipient dr = new DeathRecipient(organizer, windowingMode); + try { + organizer.asBinder().linkToDeath(dr, 0); + } catch (RemoteException e) { + Slog.e(TAG, "TaskOrganizer failed to register death recipient"); + } + + final TaskOrganizerState state = new TaskOrganizerState(organizer, dr); + mTaskOrganizersForWindowingMode.put(windowingMode, state); + + mTaskOrganizerStates.put(organizer, state); + } + + ITaskOrganizer getTaskOrganizer(int windowingMode) { + final TaskOrganizerState state = mTaskOrganizersForWindowingMode.get(windowingMode); + if (state == null) { + return null; + } + return state.mOrganizer; + } + + private void sendTaskAppeared(ITaskOrganizer organizer, Task task) { + try { + organizer.taskAppeared(task.getRemoteToken(), task.getTaskInfo()); + } catch (Exception e) { + Slog.e(TAG, "Exception sending taskAppeared callback" + e); + } + } + + private void sendTaskVanished(ITaskOrganizer organizer, Task task) { + try { + organizer.taskVanished(task.getRemoteToken()); + } catch (Exception e) { + Slog.e(TAG, "Exception sending taskVanished callback" + e); + } + } + + void onTaskAppeared(ITaskOrganizer organizer, Task task) { + TaskOrganizerState state = mTaskOrganizerStates.get(organizer); + + state.addTask(task); + sendTaskAppeared(organizer, task); + } + + void onTaskVanished(ITaskOrganizer organizer, Task task) { + final TaskOrganizerState state = mTaskOrganizerStates.get(organizer); + sendTaskVanished(organizer, task); + + // This could trigger TaskAppeared for other tasks in the same stack so make sure + // we do this AFTER sending taskVanished. + state.removeTask(task); + } +} diff --git a/services/core/java/com/android/server/wm/WindowContainer.java b/services/core/java/com/android/server/wm/WindowContainer.java index 8ac212f7ef03..f3880fa5dd09 100644 --- a/services/core/java/com/android/server/wm/WindowContainer.java +++ b/services/core/java/com/android/server/wm/WindowContainer.java @@ -342,7 +342,7 @@ class WindowContainer<E extends WindowContainer> extends ConfigurationContainer< if (mSurfaceControl == null) { // If we don't yet have a surface, but we now have a parent, we should // build a surface. - mSurfaceControl = makeSurface().build(); + setSurfaceControl(makeSurface().build()); getPendingTransaction().show(mSurfaceControl); updateSurfacePosition(); } else { @@ -496,7 +496,7 @@ class WindowContainer<E extends WindowContainer> extends ConfigurationContainer< mParent.getPendingTransaction().merge(getPendingTransaction()); } - mSurfaceControl = null; + setSurfaceControl(null); mLastSurfacePosition.set(0, 0); scheduleAnimation(); } @@ -2209,4 +2209,8 @@ class WindowContainer<E extends WindowContainer> extends ConfigurationContainer< } return mParent.getDimmer(); } + + void setSurfaceControl(SurfaceControl sc) { + mSurfaceControl = sc; + } } diff --git a/services/core/jni/com_android_server_location_GnssLocationProvider.cpp b/services/core/jni/com_android_server_location_GnssLocationProvider.cpp index 6504e3188a0d..acb6bea53a62 100644 --- a/services/core/jni/com_android_server_location_GnssLocationProvider.cpp +++ b/services/core/jni/com_android_server_location_GnssLocationProvider.cpp @@ -129,7 +129,6 @@ using android::hardware::hidl_vec; using android::hardware::hidl_string; using android::hardware::hidl_death_recipient; -using android::hardware::gnss::V1_0::GnssConstellationType; using android::hardware::gnss::V1_0::GnssLocationFlags; using android::hardware::gnss::V1_0::IAGnssRilCallback; using android::hardware::gnss::V1_0::IGnssGeofenceCallback; @@ -149,6 +148,8 @@ using android::hardware::gnss::measurement_corrections::V1_0::ReflectingPlane; using android::hidl::base::V1_0::IBase; +using GnssConstellationType_V1_0 = android::hardware::gnss::V1_0::GnssConstellationType; +using GnssConstellationType_V2_0 = android::hardware::gnss::V2_0::GnssConstellationType; using GnssLocation_V1_0 = android::hardware::gnss::V1_0::GnssLocation; using GnssLocation_V2_0 = android::hardware::gnss::V2_0::GnssLocation; using IGnss_V1_0 = android::hardware::gnss::V1_0::IGnss; @@ -161,6 +162,7 @@ using IGnssCallback_V2_1 = android::hardware::gnss::V2_1::IGnssCallback; using IGnssConfiguration_V1_0 = android::hardware::gnss::V1_0::IGnssConfiguration; using IGnssConfiguration_V1_1 = android::hardware::gnss::V1_1::IGnssConfiguration; using IGnssConfiguration_V2_0 = android::hardware::gnss::V2_0::IGnssConfiguration; +using IGnssConfiguration_V2_1 = android::hardware::gnss::V2_1::IGnssConfiguration; using IGnssDebug_V1_0 = android::hardware::gnss::V1_0::IGnssDebug; using IGnssDebug_V2_0 = android::hardware::gnss::V2_0::IGnssDebug; using IGnssMeasurement_V1_0 = android::hardware::gnss::V1_0::IGnssMeasurement; @@ -221,6 +223,7 @@ sp<IGnssDebug_V2_0> gnssDebugIface_V2_0 = nullptr; sp<IGnssConfiguration_V1_0> gnssConfigurationIface = nullptr; sp<IGnssConfiguration_V1_1> gnssConfigurationIface_V1_1 = nullptr; sp<IGnssConfiguration_V2_0> gnssConfigurationIface_V2_0 = nullptr; +sp<IGnssConfiguration_V2_1> gnssConfigurationIface_V2_1 = nullptr; sp<IGnssNi> gnssNiIface = nullptr; sp<IGnssMeasurement_V1_0> gnssMeasurementIface = nullptr; sp<IGnssMeasurement_V1_1> gnssMeasurementIface_V1_1 = nullptr; @@ -1888,7 +1891,17 @@ static void android_location_GnssLocationProvider_init_once(JNIEnv* env, jclass gnssNiIface = gnssNi; } - if (gnssHal_V2_0 != nullptr) { + if (gnssHal_V2_1 != nullptr) { + auto gnssConfiguration = gnssHal_V2_1->getExtensionGnssConfiguration_2_1(); + if (!gnssConfiguration.isOk()) { + ALOGD("Unable to get a handle to GnssConfiguration_V2_1"); + } else { + gnssConfigurationIface_V2_1 = gnssConfiguration; + gnssConfigurationIface_V2_0 = gnssConfigurationIface_V2_1; + gnssConfigurationIface_V1_1 = gnssConfigurationIface_V2_1; + gnssConfigurationIface = gnssConfigurationIface_V2_1; + } + } else if (gnssHal_V2_0 != nullptr) { auto gnssConfiguration = gnssHal_V2_0->getExtensionGnssConfiguration_2_0(); if (!gnssConfiguration.isOk()) { ALOGD("Unable to get a handle to GnssConfiguration_V2_0"); @@ -1962,7 +1975,11 @@ static jboolean android_location_GnssNetworkConnectivityHandler_is_agps_ril_supp static jobject android_location_GnssConfiguration_get_gnss_configuration_version( JNIEnv* env, jclass /* jclazz */) { jint major, minor; - if (gnssConfigurationIface_V2_0 != nullptr) { + if (gnssConfigurationIface_V2_1 != nullptr) { + major = 2; + minor = 1; + } + else if (gnssConfigurationIface_V2_0 != nullptr) { major = 2; minor = 0; } else if (gnssConfigurationIface_V1_1 != nullptr) { @@ -2768,7 +2785,7 @@ static jboolean SingleSatCorrection singleSatCorrection = { .singleSatCorrectionFlags = corrFlags, - .constellation = static_cast<GnssConstellationType>(constType), + .constellation = static_cast<GnssConstellationType_V1_0>(constType), .svid = static_cast<uint16_t>(satId), .carrierFrequencyHz = carrierFreqHz, .probSatIsLos = probSatIsLos, @@ -2863,8 +2880,8 @@ static jboolean android_location_GnssConfiguration_set_supl_version(JNIEnv*, static jboolean android_location_GnssConfiguration_set_supl_es(JNIEnv*, jobject, jint suplEs) { - if (gnssConfigurationIface_V2_0 != nullptr) { - ALOGI("Config parameter SUPL_ES is deprecated in IGnssConfiguration.hal version 2.0."); + if (gnssConfigurationIface_V2_0 != nullptr || gnssConfigurationIface_V2_1 != nullptr) { + ALOGI("Config parameter SUPL_ES is deprecated in IGnssConfiguration.hal version 2.0 and higher."); return JNI_FALSE; } @@ -2892,7 +2909,7 @@ static jboolean android_location_GnssConfiguration_set_supl_mode(JNIEnv*, static jboolean android_location_GnssConfiguration_set_gps_lock(JNIEnv*, jobject, jint gpsLock) { - if (gnssConfigurationIface_V2_0 != nullptr) { + if (gnssConfigurationIface_V2_0 != nullptr || gnssConfigurationIface_V2_1 != nullptr) { ALOGI("Config parameter GPS_LOCK is deprecated in IGnssConfiguration.hal version 2.0."); return JNI_FALSE; } @@ -2932,7 +2949,7 @@ static jboolean android_location_GnssConfiguration_set_gnss_pos_protocol_select( static jboolean android_location_GnssConfiguration_set_satellite_blacklist( JNIEnv* env, jobject, jintArray constellations, jintArray sv_ids) { - if (gnssConfigurationIface_V1_1 == nullptr) { + if (gnssConfigurationIface_V1_1 == nullptr && gnssConfigurationIface_V2_1 == nullptr) { ALOGI("IGnssConfiguration interface does not support satellite blacklist."); return JNI_FALSE; } @@ -2955,11 +2972,24 @@ static jboolean android_location_GnssConfiguration_set_satellite_blacklist( return JNI_FALSE; } + if (gnssConfigurationIface_V2_1 != nullptr) { + hidl_vec<IGnssConfiguration_V2_1::BlacklistedSource> sources; + sources.resize(length); + + for (int i = 0; i < length; i++) { + sources[i].constellation = static_cast<GnssConstellationType_V2_0>(constellation_array[i]); + sources[i].svid = sv_id_array[i]; + } + + auto result = gnssConfigurationIface_V2_1->setBlacklist_2_1(sources); + return checkHidlReturn(result, "IGnssConfiguration_V2_1 setBlacklist_2_1() failed."); + } + hidl_vec<IGnssConfiguration_V1_1::BlacklistedSource> sources; sources.resize(length); for (int i = 0; i < length; i++) { - sources[i].constellation = static_cast<GnssConstellationType>(constellation_array[i]); + sources[i].constellation = static_cast<GnssConstellationType_V1_0>(constellation_array[i]); sources[i].svid = sv_id_array[i]; } diff --git a/services/tests/mockingservicestests/src/com/android/server/location/UserInfoStoreTest.java b/services/tests/mockingservicestests/src/com/android/server/location/UserInfoStoreTest.java new file mode 100644 index 000000000000..06fb10257a37 --- /dev/null +++ b/services/tests/mockingservicestests/src/com/android/server/location/UserInfoStoreTest.java @@ -0,0 +1,177 @@ +/* + * Copyright (C) 2020 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.android.server.location; + +import static com.android.dx.mockito.inline.extended.ExtendedMockito.doReturn; +import static com.android.dx.mockito.inline.extended.ExtendedMockito.mockitoSession; + +import static com.google.common.truth.Truth.assertThat; + +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.ArgumentMatchers.anyInt; +import static org.mockito.ArgumentMatchers.isNull; +import static org.mockito.Mockito.doAnswer; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.never; +import static org.mockito.Mockito.verify; + +import android.app.ActivityManager; +import android.content.BroadcastReceiver; +import android.content.Context; +import android.content.Intent; +import android.content.IntentFilter; +import android.content.pm.UserInfo; +import android.os.Handler; +import android.os.UserHandle; +import android.os.UserManager; +import android.platform.test.annotations.Presubmit; + +import androidx.test.filters.SmallTest; +import androidx.test.runner.AndroidJUnit4; + +import com.android.dx.mockito.inline.extended.StaticMockitoSession; + +import org.junit.After; +import org.junit.Before; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.mockito.Mock; +import org.mockito.quality.Strictness; + +import java.util.ArrayList; +import java.util.List; + +@Presubmit +@SmallTest +@RunWith(AndroidJUnit4.class) +public class UserInfoStoreTest { + + private static final int USER1_ID = 1; + private static final int USER1_MANAGED_ID = 11; + private static final int[] USER1_PROFILES = new int[]{USER1_ID, USER1_MANAGED_ID}; + private static final int USER2_ID = 2; + private static final int USER2_MANAGED_ID = 12; + private static final int[] USER2_PROFILES = new int[]{USER2_ID, USER2_MANAGED_ID}; + + @Mock private Context mContext; + @Mock private UserManager mUserManager; + + private StaticMockitoSession mMockingSession; + private List<BroadcastReceiver> mBroadcastReceivers = new ArrayList<>(); + + private UserInfoStore mStore; + + @Before + public void setUp() { + mMockingSession = mockitoSession() + .initMocks(this) + .spyStatic(ActivityManager.class) + .strictness(Strictness.WARN) + .startMocking(); + + doReturn(mUserManager).when(mContext).getSystemService(UserManager.class); + doAnswer(invocation -> { + mBroadcastReceivers.add(invocation.getArgument(0)); + return null; + }).when(mContext).registerReceiverAsUser(any(BroadcastReceiver.class), any( + UserHandle.class), any(IntentFilter.class), isNull(), any(Handler.class)); + doReturn(USER1_PROFILES).when(mUserManager).getProfileIdsWithDisabled(USER1_ID); + doReturn(USER2_PROFILES).when(mUserManager).getProfileIdsWithDisabled(USER2_ID); + doReturn(new UserInfo(USER1_ID, "", 0)).when(mUserManager).getProfileParent( + USER1_MANAGED_ID); + doReturn(new UserInfo(USER2_ID, "", 0)).when(mUserManager).getProfileParent( + USER2_MANAGED_ID); + + doReturn(USER1_ID).when(ActivityManager::getCurrentUser); + + mStore = new UserInfoStore(mContext); + mStore.onSystemReady(); + } + + @After + public void tearDown() { + if (mMockingSession != null) { + mMockingSession.finishMocking(); + } + } + + private void switchUser(int userId) { + doReturn(userId).when(ActivityManager::getCurrentUser); + Intent intent = new Intent(Intent.ACTION_USER_SWITCHED).putExtra(Intent.EXTRA_USER_HANDLE, + userId); + for (BroadcastReceiver broadcastReceiver : mBroadcastReceivers) { + broadcastReceiver.onReceive(mContext, intent); + } + } + + @Test + public void testListeners() { + UserInfoStore.UserChangedListener listener = mock(UserInfoStore.UserChangedListener.class); + mStore.addListener(listener); + + switchUser(USER1_ID); + verify(listener, never()).onUserChanged(anyInt(), anyInt()); + + switchUser(USER2_ID); + verify(listener).onUserChanged(USER1_ID, USER2_ID); + + switchUser(USER1_ID); + verify(listener).onUserChanged(USER2_ID, USER1_ID); + } + + @Test + public void testCurrentUser() { + assertThat(mStore.getCurrentUserId()).isEqualTo(USER1_ID); + + switchUser(USER2_ID); + + assertThat(mStore.getCurrentUserId()).isEqualTo(USER2_ID); + + switchUser(USER1_ID); + + assertThat(mStore.getCurrentUserId()).isEqualTo(USER1_ID); + } + + @Test + public void testIsCurrentUserOrProfile() { + assertThat(mStore.isCurrentUserOrProfile(USER1_ID)).isTrue(); + assertThat(mStore.isCurrentUserOrProfile(USER1_MANAGED_ID)).isTrue(); + assertThat(mStore.isCurrentUserOrProfile(USER2_ID)).isFalse(); + assertThat(mStore.isCurrentUserOrProfile(USER2_MANAGED_ID)).isFalse(); + + switchUser(USER2_ID); + + assertThat(mStore.isCurrentUserOrProfile(USER1_ID)).isFalse(); + assertThat(mStore.isCurrentUserOrProfile(USER2_ID)).isTrue(); + assertThat(mStore.isCurrentUserOrProfile(USER1_MANAGED_ID)).isFalse(); + assertThat(mStore.isCurrentUserOrProfile(USER2_MANAGED_ID)).isTrue(); + } + + @Test + public void testGetParentUserId() { + assertThat(mStore.getParentUserId(USER1_ID)).isEqualTo(USER1_ID); + assertThat(mStore.getParentUserId(USER1_MANAGED_ID)).isEqualTo(USER1_ID); + assertThat(mStore.getParentUserId(USER2_ID)).isEqualTo(USER2_ID); + assertThat(mStore.getParentUserId(USER2_MANAGED_ID)).isEqualTo(USER2_ID); + + switchUser(USER2_ID); + + assertThat(mStore.getParentUserId(USER1_ID)).isEqualTo(USER1_ID); + assertThat(mStore.getParentUserId(USER2_ID)).isEqualTo(USER2_ID); + assertThat(mStore.getParentUserId(USER1_MANAGED_ID)).isEqualTo(USER1_ID); + assertThat(mStore.getParentUserId(USER2_MANAGED_ID)).isEqualTo(USER2_ID); + } +} diff --git a/services/tests/servicestests/src/com/android/server/NetworkScoreServiceTest.java b/services/tests/servicestests/src/com/android/server/NetworkScoreServiceTest.java index 1829fb79699f..2fb2021de200 100644 --- a/services/tests/servicestests/src/com/android/server/NetworkScoreServiceTest.java +++ b/services/tests/servicestests/src/com/android/server/NetworkScoreServiceTest.java @@ -29,6 +29,7 @@ import static org.mockito.Matchers.anyString; import static org.mockito.Matchers.eq; import static org.mockito.Mockito.atLeastOnce; import static org.mockito.Mockito.doThrow; +import static org.mockito.Mockito.mock; import static org.mockito.Mockito.never; import static org.mockito.Mockito.timeout; import static org.mockito.Mockito.times; @@ -56,7 +57,6 @@ import android.net.wifi.ScanResult; import android.net.wifi.WifiConfiguration; import android.net.wifi.WifiInfo; import android.net.wifi.WifiManager; -import android.net.wifi.WifiSsid; import android.os.Binder; import android.os.Handler; import android.os.HandlerThread; @@ -182,8 +182,8 @@ public class NetworkScoreServiceTest { } private ScanResult createScanResult(String ssid, String bssid) { - ScanResult result = new ScanResult(); - result.wifiSsid = WifiSsid.createFromAsciiEncoded(ssid); + ScanResult result = mock(ScanResult.class); + result.SSID = ssid; result.BSSID = bssid; return result; } @@ -794,7 +794,7 @@ public class NetworkScoreServiceTest { @Test public void testScanResultsScoreCacheFilter_invalidScanResults() throws Exception { List<ScanResult> invalidScanResults = Lists.newArrayList( - new ScanResult(), + mock(ScanResult.class), createScanResult("", SCORED_NETWORK.networkKey.wifiKey.bssid), createScanResult(WifiManager.UNKNOWN_SSID, SCORED_NETWORK.networkKey.wifiKey.bssid), createScanResult(SSID, null), diff --git a/services/tests/servicestests/src/com/android/server/locksettings/recoverablekeystore/PlatformKeyManagerTest.java b/services/tests/servicestests/src/com/android/server/locksettings/recoverablekeystore/PlatformKeyManagerTest.java index 821d97acc230..670bd8107bed 100644 --- a/services/tests/servicestests/src/com/android/server/locksettings/recoverablekeystore/PlatformKeyManagerTest.java +++ b/services/tests/servicestests/src/com/android/server/locksettings/recoverablekeystore/PlatformKeyManagerTest.java @@ -67,13 +67,25 @@ import javax.crypto.KeyGenerator; public class PlatformKeyManagerTest { private static final String DATABASE_FILE_NAME = "recoverablekeystore.db"; - private static final int USER_AUTHENTICATION_VALIDITY_DURATION_SECONDS = 15; + private static final int MIN_GENERATION_ID = 1000000; + private static final int PRIMARY_USER_ID_FIXTURE = 0; private static final int USER_ID_FIXTURE = 42; private static final long USER_SID = 4200L; private static final String KEY_ALGORITHM = "AES"; private static final String ANDROID_KEY_STORE_PROVIDER = "AndroidKeyStore"; private static final String TESTING_KEYSTORE_KEY_ALIAS = "testing-key-store-key-alias"; + private static final String ENCRYPTION_KEY_ALIAS_1 = + "com.android.server.locksettings.recoverablekeystore/platform/42/1000000/encrypt"; + private static final String DECRYPTION_KEY_ALIAS_1 = + "com.android.server.locksettings.recoverablekeystore/platform/42/1000000/decrypt"; + private static final String DECRYPTION_KEY_FOR_ALIAS_PRIMARY_USER_1 = + "com.android.server.locksettings.recoverablekeystore/platform/0/1000000/decrypt"; + private static final String ENCRYPTION_KEY_ALIAS_2 = + "com.android.server.locksettings.recoverablekeystore/platform/42/1000001/encrypt"; + private static final String DECRYPTION_KEY_ALIAS_2 = + "com.android.server.locksettings.recoverablekeystore/platform/42/1000001/decrypt"; + @Mock private Context mContext; @Mock private KeyStoreProxy mKeyStoreProxy; @Mock private KeyguardManager mKeyguardManager; @@ -114,7 +126,7 @@ public class PlatformKeyManagerTest { mPlatformKeyManager.init(USER_ID_FIXTURE); verify(mKeyStoreProxy).setEntry( - eq("com.android.server.locksettings.recoverablekeystore/platform/42/1/encrypt"), + eq(ENCRYPTION_KEY_ALIAS_1), any(), any()); } @@ -156,7 +168,7 @@ public class PlatformKeyManagerTest { mPlatformKeyManager.init(USER_ID_FIXTURE); verify(mKeyStoreProxy).setEntry( - eq("com.android.server.locksettings.recoverablekeystore/platform/42/1/decrypt"), + eq(DECRYPTION_KEY_ALIAS_1), any(), any()); } @@ -187,19 +199,33 @@ public class PlatformKeyManagerTest { } @Test - public void init_createsDecryptKeyWithAuthenticationRequired() throws Exception { + public void init_primaryUser_createsDecryptKeyWithUnlockedDeviceRequired() throws Exception { + mPlatformKeyManager.init(PRIMARY_USER_ID_FIXTURE); + + assertTrue(getDecryptKeyProtectionForPrimaryUser().isUnlockedDeviceRequired()); + } + + @Test + public void init_primaryUser_createsDecryptKeyWithoutAuthenticationRequired() throws Exception { + mPlatformKeyManager.init(PRIMARY_USER_ID_FIXTURE); + + assertFalse(getDecryptKeyProtectionForPrimaryUser().isUserAuthenticationRequired()); + } + + @Test + public void init_secondaryUser_createsDecryptKeyWithoutUnlockedDeviceRequired() + throws Exception { mPlatformKeyManager.init(USER_ID_FIXTURE); - assertTrue(getDecryptKeyProtection().isUserAuthenticationRequired()); + assertFalse(getDecryptKeyProtection().isUnlockedDeviceRequired()); } @Test - public void init_createsDecryptKeyWithAuthenticationValidFor15Seconds() throws Exception { + public void init_secondaryUserUser_createsDecryptKeyWithAuthenticationRequired() + throws Exception { mPlatformKeyManager.init(USER_ID_FIXTURE); - assertEquals( - USER_AUTHENTICATION_VALIDITY_DURATION_SECONDS, - getDecryptKeyProtection().getUserAuthenticationValidityDurationSeconds()); + assertTrue(getDecryptKeyProtection().isUserAuthenticationRequired()); } @Test @@ -219,7 +245,7 @@ public class PlatformKeyManagerTest { mPlatformKeyManager.init(USER_ID_FIXTURE); verify(mKeyStoreProxy, never()).setEntry( - eq("com.android.server.locksettings.recoverablekeystore/platform/42/1/decrypt"), + eq(DECRYPTION_KEY_ALIAS_1), any(), any()); } @@ -231,7 +257,7 @@ public class PlatformKeyManagerTest { expectThrows(RemoteException.class, () -> mPlatformKeyManager.init(USER_ID_FIXTURE)); verify(mKeyStoreProxy, never()).setEntry( - eq("com.android.server.locksettings.recoverablekeystore/platform/42/1/decrypt"), + eq(DECRYPTION_KEY_ALIAS_1), any(), any()); } @@ -251,15 +277,15 @@ public class PlatformKeyManagerTest { public void init_savesGenerationIdToDatabase() throws Exception { mPlatformKeyManager.init(USER_ID_FIXTURE); - assertEquals(1, + assertEquals(MIN_GENERATION_ID, mRecoverableKeyStoreDb.getPlatformKeyGenerationId(USER_ID_FIXTURE)); } @Test - public void init_setsGenerationIdTo1() throws Exception { + public void init_setsGenerationId() throws Exception { mPlatformKeyManager.init(USER_ID_FIXTURE); - assertEquals(1, mPlatformKeyManager.getGenerationId(USER_ID_FIXTURE)); + assertEquals(MIN_GENERATION_ID, mPlatformKeyManager.getGenerationId(USER_ID_FIXTURE)); } @Test @@ -268,22 +294,20 @@ public class PlatformKeyManagerTest { mPlatformKeyManager.init(USER_ID_FIXTURE); - assertEquals(2, mPlatformKeyManager.getGenerationId(USER_ID_FIXTURE)); + assertEquals(MIN_GENERATION_ID + 1, mPlatformKeyManager.getGenerationId(USER_ID_FIXTURE)); } @Test public void init_doesNotIncrementGenerationIdIfKeyAvailable() throws Exception { mPlatformKeyManager.init(USER_ID_FIXTURE); when(mKeyStoreProxy - .containsAlias("com.android.server.locksettings.recoverablekeystore/" - + "platform/42/1/decrypt")).thenReturn(true); + .containsAlias(DECRYPTION_KEY_ALIAS_1)).thenReturn(true); when(mKeyStoreProxy - .containsAlias("com.android.server.locksettings.recoverablekeystore/" - + "platform/42/1/encrypt")).thenReturn(true); + .containsAlias(ENCRYPTION_KEY_ALIAS_1)).thenReturn(true); mPlatformKeyManager.init(USER_ID_FIXTURE); - assertEquals(1, mPlatformKeyManager.getGenerationId(USER_ID_FIXTURE)); + assertEquals(MIN_GENERATION_ID, mPlatformKeyManager.getGenerationId(USER_ID_FIXTURE)); } @Test @@ -294,226 +318,194 @@ public class PlatformKeyManagerTest { @Test public void getDecryptKey_getsDecryptKeyWithCorrectAlias() throws Exception { when(mKeyStoreProxy - .containsAlias("com.android.server.locksettings.recoverablekeystore/" - + "platform/42/1/decrypt")).thenReturn(true); + .containsAlias(DECRYPTION_KEY_ALIAS_1)).thenReturn(true); when(mKeyStoreProxy - .containsAlias("com.android.server.locksettings.recoverablekeystore/" - + "platform/42/1/encrypt")).thenReturn(true); + .containsAlias(ENCRYPTION_KEY_ALIAS_1)).thenReturn(true); when(mKeyStoreProxy.getKey( - eq("com.android.server.locksettings.recoverablekeystore/platform/42/1/decrypt"), + eq(DECRYPTION_KEY_ALIAS_1), any())).thenReturn(generateAndroidKeyStoreKey()); mPlatformKeyManager.getDecryptKey(USER_ID_FIXTURE); verify(mKeyStoreProxy).getKey( - eq("com.android.server.locksettings.recoverablekeystore/platform/42/1/decrypt"), + eq(DECRYPTION_KEY_ALIAS_1), any()); } @Test public void getDecryptKey_generatesNewKeyIfOldDecryptKeyWasRemoved() throws Exception { when(mKeyStoreProxy - .containsAlias("com.android.server.locksettings.recoverablekeystore/" - + "platform/42/1/encrypt")).thenReturn(true); + .containsAlias(ENCRYPTION_KEY_ALIAS_1)).thenReturn(true); when(mKeyStoreProxy - .containsAlias("com.android.server.locksettings.recoverablekeystore/" - + "platform/42/1/decrypt")).thenReturn(false); // was removed + .containsAlias(DECRYPTION_KEY_ALIAS_1)).thenReturn(false); // was removed when(mKeyStoreProxy - .containsAlias("com.android.server.locksettings.recoverablekeystore/" - + "platform/42/2/encrypt")).thenReturn(true); // new version is available + .containsAlias(ENCRYPTION_KEY_ALIAS_2)).thenReturn(true); // new version when(mKeyStoreProxy - .containsAlias("com.android.server.locksettings.recoverablekeystore/" - + "platform/42/2/decrypt")).thenReturn(true); + .containsAlias(DECRYPTION_KEY_ALIAS_2)).thenReturn(true); when(mKeyStoreProxy.getKey( - eq("com.android.server.locksettings.recoverablekeystore/platform/42/2/decrypt"), + eq(DECRYPTION_KEY_ALIAS_2), any())).thenReturn(generateAndroidKeyStoreKey()); mPlatformKeyManager.getDecryptKey(USER_ID_FIXTURE); verify(mKeyStoreProxy).containsAlias( - eq("com.android.server.locksettings.recoverablekeystore/platform/42/1/decrypt")); + eq(DECRYPTION_KEY_ALIAS_1)); // Attempt to get regenerated key. verify(mKeyStoreProxy).getKey( - eq("com.android.server.locksettings.recoverablekeystore/platform/42/2/decrypt"), + eq(DECRYPTION_KEY_ALIAS_2), any()); } @Test public void getDecryptKey_generatesNewKeyIfOldEncryptKeyWasRemoved() throws Exception { when(mKeyStoreProxy - .containsAlias("com.android.server.locksettings.recoverablekeystore/" - + "platform/42/1/encrypt")).thenReturn(false); // was removed + .containsAlias(ENCRYPTION_KEY_ALIAS_1)).thenReturn(false); // was removed when(mKeyStoreProxy - .containsAlias("com.android.server.locksettings.recoverablekeystore/" - + "platform/42/1/decrypt")).thenReturn(true); + .containsAlias(DECRYPTION_KEY_ALIAS_1)).thenReturn(true); when(mKeyStoreProxy - .containsAlias("com.android.server.locksettings.recoverablekeystore/" - + "platform/42/2/encrypt")).thenReturn(true); + .containsAlias(ENCRYPTION_KEY_ALIAS_2)).thenReturn(true); when(mKeyStoreProxy - .containsAlias("com.android.server.locksettings.recoverablekeystore/" - + "platform/42/2/decrypt")).thenReturn(true); + .containsAlias(DECRYPTION_KEY_ALIAS_2)).thenReturn(true); mPlatformKeyManager.getDecryptKey(USER_ID_FIXTURE); verify(mKeyStoreProxy).containsAlias( - eq("com.android.server.locksettings.recoverablekeystore/platform/42/1/encrypt")); + eq(ENCRYPTION_KEY_ALIAS_1)); // Attempt to get regenerated key. verify(mKeyStoreProxy).getKey( - eq("com.android.server.locksettings.recoverablekeystore/platform/42/2/decrypt"), + eq(DECRYPTION_KEY_ALIAS_2), any()); } @Test public void getEncryptKey_generatesNewKeyIfOldOneIsInvalid() throws Exception { doThrow(new UnrecoverableKeyException()).when(mKeyStoreProxy).getKey( - eq("com.android.server.locksettings.recoverablekeystore/platform/42/1/encrypt"), + eq(ENCRYPTION_KEY_ALIAS_1), any()); when(mKeyStoreProxy - .containsAlias("com.android.server.locksettings.recoverablekeystore/" - + "platform/42/1/encrypt")).thenReturn(true); + .containsAlias(ENCRYPTION_KEY_ALIAS_1)).thenReturn(true); when(mKeyStoreProxy - .containsAlias("com.android.server.locksettings.recoverablekeystore/" - + "platform/42/1/decrypt")).thenReturn(true); + .containsAlias(DECRYPTION_KEY_ALIAS_1)).thenReturn(true); when(mKeyStoreProxy - .containsAlias("com.android.server.locksettings.recoverablekeystore/" - + "platform/42/2/encrypt")).thenReturn(true); + .containsAlias(ENCRYPTION_KEY_ALIAS_2)).thenReturn(true); when(mKeyStoreProxy - .containsAlias("com.android.server.locksettings.recoverablekeystore/" - + "platform/42/2/decrypt")).thenReturn(true); + .containsAlias(DECRYPTION_KEY_ALIAS_2)).thenReturn(true); mPlatformKeyManager.getEncryptKey(USER_ID_FIXTURE); verify(mKeyStoreProxy).getKey( - eq("com.android.server.locksettings.recoverablekeystore/platform/42/1/encrypt"), + eq(ENCRYPTION_KEY_ALIAS_1), any()); // Attempt to get regenerated key. verify(mKeyStoreProxy).getKey( - eq("com.android.server.locksettings.recoverablekeystore/platform/42/2/encrypt"), + eq(ENCRYPTION_KEY_ALIAS_2), any()); } @Test public void getDecryptKey_generatesNewKeyIfOldOneIsInvalid() throws Exception { doThrow(new UnrecoverableKeyException()).when(mKeyStoreProxy).getKey( - eq("com.android.server.locksettings.recoverablekeystore/platform/42/1/decrypt"), + eq(DECRYPTION_KEY_ALIAS_1), any()); when(mKeyStoreProxy.getKey( - eq("com.android.server.locksettings.recoverablekeystore/platform/42/2/decrypt"), + eq(DECRYPTION_KEY_ALIAS_2), any())).thenReturn(generateAndroidKeyStoreKey()); when(mKeyStoreProxy - .containsAlias("com.android.server.locksettings.recoverablekeystore/" - + "platform/42/1/encrypt")).thenReturn(true); + .containsAlias(ENCRYPTION_KEY_ALIAS_1)).thenReturn(true); when(mKeyStoreProxy - .containsAlias("com.android.server.locksettings.recoverablekeystore/" - + "platform/42/1/decrypt")).thenReturn(true); + .containsAlias(DECRYPTION_KEY_ALIAS_1)).thenReturn(true); when(mKeyStoreProxy - .containsAlias("com.android.server.locksettings.recoverablekeystore/" - + "platform/42/2/encrypt")).thenReturn(true); + .containsAlias(ENCRYPTION_KEY_ALIAS_2)).thenReturn(true); when(mKeyStoreProxy - .containsAlias("com.android.server.locksettings.recoverablekeystore/" - + "platform/42/2/decrypt")).thenReturn(true); + .containsAlias(DECRYPTION_KEY_ALIAS_2)).thenReturn(true); mPlatformKeyManager.getDecryptKey(USER_ID_FIXTURE); verify(mKeyStoreProxy).containsAlias( - eq("com.android.server.locksettings.recoverablekeystore/platform/42/1/decrypt")); + eq(DECRYPTION_KEY_ALIAS_1)); // Attempt to get regenerated key. verify(mKeyStoreProxy).getKey( - eq("com.android.server.locksettings.recoverablekeystore/platform/42/2/decrypt"), + eq(DECRYPTION_KEY_ALIAS_2), any()); } @Test public void getEncryptKey_generatesNewKeyIfDecryptKeyIsUnrecoverable() throws Exception { doThrow(new UnrecoverableKeyException()).when(mKeyStoreProxy).getKey( - eq("com.android.server.locksettings.recoverablekeystore/platform/42/1/decrypt"), + eq(DECRYPTION_KEY_ALIAS_1), any()); when(mKeyStoreProxy - .containsAlias("com.android.server.locksettings.recoverablekeystore/" - + "platform/42/1/encrypt")).thenReturn(true); + .containsAlias(ENCRYPTION_KEY_ALIAS_1)).thenReturn(true); when(mKeyStoreProxy - .containsAlias("com.android.server.locksettings.recoverablekeystore/" - + "platform/42/1/decrypt")).thenReturn(false); // was removed + .containsAlias(DECRYPTION_KEY_ALIAS_1)).thenReturn(false); // was removed when(mKeyStoreProxy - .containsAlias("com.android.server.locksettings.recoverablekeystore/" - + "platform/42/2/encrypt")).thenReturn(true); // new version is available + .containsAlias(ENCRYPTION_KEY_ALIAS_2)).thenReturn(true); // new version when(mKeyStoreProxy - .containsAlias("com.android.server.locksettings.recoverablekeystore/" - + "platform/42/2/decrypt")).thenReturn(true); + .containsAlias(DECRYPTION_KEY_ALIAS_2)).thenReturn(true); mPlatformKeyManager.getEncryptKey(USER_ID_FIXTURE); // Attempt to get regenerated key. verify(mKeyStoreProxy).getKey( - eq("com.android.server.locksettings.recoverablekeystore/platform/42/2/encrypt"), + eq(ENCRYPTION_KEY_ALIAS_2), any()); } @Test public void getEncryptKey_generatesNewKeyIfOldDecryptKeyWasRemoved() throws Exception { when(mKeyStoreProxy - .containsAlias("com.android.server.locksettings.recoverablekeystore/" - + "platform/42/1/encrypt")).thenReturn(true); + .containsAlias(ENCRYPTION_KEY_ALIAS_1)).thenReturn(true); when(mKeyStoreProxy - .containsAlias("com.android.server.locksettings.recoverablekeystore/" - + "platform/42/1/decrypt")).thenReturn(false); // was removed + .containsAlias(DECRYPTION_KEY_ALIAS_1)).thenReturn(false); // was removed when(mKeyStoreProxy - .containsAlias("com.android.server.locksettings.recoverablekeystore/" - + "platform/42/2/encrypt")).thenReturn(true); // new version is available + .containsAlias(ENCRYPTION_KEY_ALIAS_2)).thenReturn(true); // new version when(mKeyStoreProxy - .containsAlias("com.android.server.locksettings.recoverablekeystore/" - + "platform/42/2/decrypt")).thenReturn(true); + .containsAlias(DECRYPTION_KEY_ALIAS_2)).thenReturn(true); mPlatformKeyManager.getEncryptKey(USER_ID_FIXTURE); verify(mKeyStoreProxy).containsAlias( - eq("com.android.server.locksettings.recoverablekeystore/platform/42/1/encrypt")); + eq(ENCRYPTION_KEY_ALIAS_1)); // Attempt to get regenerated key. verify(mKeyStoreProxy).getKey( - eq("com.android.server.locksettings.recoverablekeystore/platform/42/2/encrypt"), + eq(ENCRYPTION_KEY_ALIAS_2), any()); } @Test public void getEncryptKey_generatesNewKeyIfOldEncryptKeyWasRemoved() throws Exception { when(mKeyStoreProxy - .containsAlias("com.android.server.locksettings.recoverablekeystore/" - + "platform/42/1/encrypt")).thenReturn(false); // was removed + .containsAlias(ENCRYPTION_KEY_ALIAS_1)).thenReturn(false); // was removed when(mKeyStoreProxy - .containsAlias("com.android.server.locksettings.recoverablekeystore/" - + "platform/42/1/decrypt")).thenReturn(true); + .containsAlias(DECRYPTION_KEY_ALIAS_1)).thenReturn(true); when(mKeyStoreProxy - .containsAlias("com.android.server.locksettings.recoverablekeystore/" - + "platform/42/2/encrypt")).thenReturn(true); + .containsAlias(ENCRYPTION_KEY_ALIAS_2)).thenReturn(true); when(mKeyStoreProxy - .containsAlias("com.android.server.locksettings.recoverablekeystore/" - + "platform/42/2/decrypt")).thenReturn(true); + .containsAlias(DECRYPTION_KEY_ALIAS_2)).thenReturn(true); mPlatformKeyManager.getEncryptKey(USER_ID_FIXTURE); verify(mKeyStoreProxy).containsAlias( - eq("com.android.server.locksettings.recoverablekeystore/platform/42/1/encrypt")); + eq(ENCRYPTION_KEY_ALIAS_1)); // Attempt to get regenerated key. verify(mKeyStoreProxy).getKey( - eq("com.android.server.locksettings.recoverablekeystore/platform/42/2/encrypt"), + eq(ENCRYPTION_KEY_ALIAS_2), any()); } @Test public void getEncryptKey_getsEncryptKeyWithCorrectAlias() throws Exception { when(mKeyStoreProxy - .containsAlias("com.android.server.locksettings.recoverablekeystore/" - + "platform/42/1/encrypt")).thenReturn(true); + .containsAlias(ENCRYPTION_KEY_ALIAS_1)).thenReturn(true); when(mKeyStoreProxy - .containsAlias("com.android.server.locksettings.recoverablekeystore/" - + "platform/42/1/decrypt")).thenReturn(true); + .containsAlias(DECRYPTION_KEY_ALIAS_1)).thenReturn(true); mPlatformKeyManager.getEncryptKey(USER_ID_FIXTURE); verify(mKeyStoreProxy).getKey( - eq("com.android.server.locksettings.recoverablekeystore/platform/42/1/encrypt"), + eq(ENCRYPTION_KEY_ALIAS_1), any()); } @@ -523,7 +515,7 @@ public class PlatformKeyManagerTest { mPlatformKeyManager.regenerate(USER_ID_FIXTURE); - assertEquals(2, mPlatformKeyManager.getGenerationId(USER_ID_FIXTURE)); + assertEquals(MIN_GENERATION_ID + 1, mPlatformKeyManager.getGenerationId(USER_ID_FIXTURE)); } @Test @@ -533,17 +525,17 @@ public class PlatformKeyManagerTest { mPlatformKeyManager.regenerate(USER_ID_FIXTURE); verify(mKeyStoreProxy).deleteEntry( - eq("com.android.server.locksettings.recoverablekeystore/platform/42/1/encrypt")); + eq(ENCRYPTION_KEY_ALIAS_1)); verify(mKeyStoreProxy).deleteEntry( - eq("com.android.server.locksettings.recoverablekeystore/platform/42/1/decrypt")); + eq(DECRYPTION_KEY_ALIAS_1)); mPlatformKeyManager.regenerate(USER_ID_FIXTURE); // Removes second generation keys. verify(mKeyStoreProxy).deleteEntry( - eq("com.android.server.locksettings.recoverablekeystore/platform/42/2/encrypt")); + eq(ENCRYPTION_KEY_ALIAS_2)); verify(mKeyStoreProxy).deleteEntry( - eq("com.android.server.locksettings.recoverablekeystore/platform/42/2/decrypt")); + eq(DECRYPTION_KEY_ALIAS_2)); } @Test @@ -553,7 +545,7 @@ public class PlatformKeyManagerTest { mPlatformKeyManager.regenerate(USER_ID_FIXTURE); verify(mKeyStoreProxy).setEntry( - eq("com.android.server.locksettings.recoverablekeystore/platform/42/2/encrypt"), + eq(ENCRYPTION_KEY_ALIAS_2), any(), any()); } @@ -565,14 +557,14 @@ public class PlatformKeyManagerTest { mPlatformKeyManager.regenerate(USER_ID_FIXTURE); verify(mKeyStoreProxy).setEntry( - eq("com.android.server.locksettings.recoverablekeystore/platform/42/2/decrypt"), + eq(DECRYPTION_KEY_ALIAS_2), any(), any()); } private KeyProtection getEncryptKeyProtection() throws Exception { verify(mKeyStoreProxy).setEntry( - eq("com.android.server.locksettings.recoverablekeystore/platform/42/1/encrypt"), + eq(ENCRYPTION_KEY_ALIAS_1), any(), mProtectionParameterCaptor.capture()); return (KeyProtection) mProtectionParameterCaptor.getValue(); @@ -580,7 +572,15 @@ public class PlatformKeyManagerTest { private KeyProtection getDecryptKeyProtection() throws Exception { verify(mKeyStoreProxy).setEntry( - eq("com.android.server.locksettings.recoverablekeystore/platform/42/1/decrypt"), + eq(DECRYPTION_KEY_ALIAS_1), + any(), + mProtectionParameterCaptor.capture()); + return (KeyProtection) mProtectionParameterCaptor.getValue(); + } + + private KeyProtection getDecryptKeyProtectionForPrimaryUser() throws Exception { + verify(mKeyStoreProxy).setEntry( + eq(DECRYPTION_KEY_FOR_ALIAS_PRIMARY_USER_1), any(), mProtectionParameterCaptor.capture()); return (KeyProtection) mProtectionParameterCaptor.getValue(); diff --git a/services/tests/servicestests/src/com/android/server/locksettings/recoverablekeystore/RecoverableKeyStoreManagerTest.java b/services/tests/servicestests/src/com/android/server/locksettings/recoverablekeystore/RecoverableKeyStoreManagerTest.java index 68900175cc8f..ac7447006444 100644 --- a/services/tests/servicestests/src/com/android/server/locksettings/recoverablekeystore/RecoverableKeyStoreManagerTest.java +++ b/services/tests/servicestests/src/com/android/server/locksettings/recoverablekeystore/RecoverableKeyStoreManagerTest.java @@ -29,6 +29,7 @@ import static org.junit.Assert.assertEquals; import static org.junit.Assert.fail; import static org.mockito.ArgumentMatchers.any; import static org.mockito.ArgumentMatchers.anyInt; +import static org.mockito.ArgumentMatchers.anyLong; import static org.mockito.ArgumentMatchers.anyString; import static org.mockito.ArgumentMatchers.eq; import static org.mockito.Mockito.atLeast; @@ -84,7 +85,7 @@ import java.security.cert.X509Certificate; import java.util.ArrayList; import java.util.Map; import java.util.Random; -import java.util.concurrent.ExecutorService; +import java.util.concurrent.ScheduledExecutorService; import javax.crypto.KeyGenerator; import javax.crypto.SecretKey; @@ -157,7 +158,7 @@ public class RecoverableKeyStoreManagerTest { @Mock private PlatformKeyManager mPlatformKeyManager; @Mock private ApplicationKeyStorage mApplicationKeyStorage; @Mock private CleanupManager mCleanupManager; - @Mock private ExecutorService mExecutorService; + @Mock private ScheduledExecutorService mExecutorService; @Spy private TestOnlyInsecureCertificateHelper mTestOnlyInsecureCertificateHelper; private RecoverableKeyStoreDb mRecoverableKeyStoreDb; @@ -1253,7 +1254,7 @@ public class RecoverableKeyStoreManagerTest { mRecoverableKeyStoreManager.lockScreenSecretAvailable( LockPatternUtils.CREDENTIAL_TYPE_PATTERN, "password".getBytes(), 11); - verify(mExecutorService).execute(any()); + verify(mExecutorService).schedule(any(Runnable.class), anyLong(), any()); } @Test @@ -1263,7 +1264,7 @@ public class RecoverableKeyStoreManagerTest { "password".getBytes(), 11); - verify(mExecutorService).execute(any()); + verify(mExecutorService).schedule(any(Runnable.class), anyLong(), any()); } private static byte[] encryptedApplicationKey( diff --git a/services/tests/servicestests/src/com/android/server/rollback/AppDataRollbackHelperTest.java b/services/tests/servicestests/src/com/android/server/rollback/AppDataRollbackHelperTest.java index a83d94001cf8..f871203728c0 100644 --- a/services/tests/servicestests/src/com/android/server/rollback/AppDataRollbackHelperTest.java +++ b/services/tests/servicestests/src/com/android/server/rollback/AppDataRollbackHelperTest.java @@ -98,7 +98,7 @@ public class AppDataRollbackHelperTest { final int[] installedUsers) { return new PackageRollbackInfo( new VersionedPackage(packageName, 2), new VersionedPackage(packageName, 1), - new IntArray(), new ArrayList<>(), false, IntArray.wrap(installedUsers), + new IntArray(), new ArrayList<>(), false, false, IntArray.wrap(installedUsers), new SparseLongArray()); } diff --git a/services/tests/servicestests/src/com/android/server/rollback/RollbackStoreTest.java b/services/tests/servicestests/src/com/android/server/rollback/RollbackStoreTest.java index 757a884f8ded..d0d2edc59861 100644 --- a/services/tests/servicestests/src/com/android/server/rollback/RollbackStoreTest.java +++ b/services/tests/servicestests/src/com/android/server/rollback/RollbackStoreTest.java @@ -87,13 +87,15 @@ public class RollbackStoreTest { + "[{'versionRolledBackFrom':{'packageName':'blah','longVersionCode':55}," + "'versionRolledBackTo':{'packageName':'blah1','longVersionCode':50},'pendingBackups':" + "[59,1245,124544],'pendingRestores':[{'userId':498,'appId':32322,'seInfo':'wombles'}," - + "{'userId':-895,'appId':1,'seInfo':'pingu'}],'isApex':false,'installedUsers':" + + "{'userId':-895,'appId':1,'seInfo':'pingu'}],'isApex':false,'isApkInApex':false," + + "'installedUsers':" + "[498468432,1111,98464],'ceSnapshotInodes':[{'userId':1,'ceSnapshotInode':-6}," + "{'userId':2222,'ceSnapshotInode':81641654445},{'userId':546546," + "'ceSnapshotInode':345689375}]},{'versionRolledBackFrom':{'packageName':'chips'," + "'longVersionCode':28},'versionRolledBackTo':{'packageName':'com.chips.test'," + "'longVersionCode':48},'pendingBackups':[5],'pendingRestores':[{'userId':18," - + "'appId':-12,'seInfo':''}],'isApex':false,'installedUsers':[55,79]," + + "'appId':-12,'seInfo':''}],'isApex':false,'isApkInApex':false," + + "'installedUsers':[55,79]," + "'ceSnapshotInodes':[]}],'isStaged':false,'causePackages':[{'packageName':'hello'," + "'longVersionCode':23},{'packageName':'something','longVersionCode':999}]," + "'committedSessionId':45654465},'timestamp':'2019-10-01T12:29:08.855Z'," @@ -155,7 +157,7 @@ public class RollbackStoreTest { PackageRollbackInfo pkgInfo1 = new PackageRollbackInfo(new VersionedPackage("com.made.up", 18), new VersionedPackage("com.something.else", 5), new IntArray(), - new ArrayList<>(), false, new IntArray(), new SparseLongArray()); + new ArrayList<>(), false, false, new IntArray(), new SparseLongArray()); pkgInfo1.getPendingBackups().add(8); pkgInfo1.getPendingBackups().add(888); pkgInfo1.getPendingBackups().add(88885); @@ -175,7 +177,7 @@ public class RollbackStoreTest { PackageRollbackInfo pkgInfo2 = new PackageRollbackInfo( new VersionedPackage("another.package", 2), new VersionedPackage("com.test.ing", 48888), new IntArray(), new ArrayList<>(), - false, new IntArray(), new SparseLongArray()); + false, false, new IntArray(), new SparseLongArray()); pkgInfo2.getPendingBackups().add(57); pkgInfo2.getPendingRestores().add( @@ -205,7 +207,7 @@ public class RollbackStoreTest { PackageRollbackInfo pkgInfo1 = new PackageRollbackInfo(new VersionedPackage("blah", 55), new VersionedPackage("blah1", 50), new IntArray(), new ArrayList<>(), - false, new IntArray(), new SparseLongArray()); + false, false, new IntArray(), new SparseLongArray()); pkgInfo1.getPendingBackups().add(59); pkgInfo1.getPendingBackups().add(1245); pkgInfo1.getPendingBackups().add(124544); @@ -224,7 +226,7 @@ public class RollbackStoreTest { PackageRollbackInfo pkgInfo2 = new PackageRollbackInfo(new VersionedPackage("chips", 28), new VersionedPackage("com.chips.test", 48), new IntArray(), new ArrayList<>(), - false, new IntArray(), new SparseLongArray()); + false, false, new IntArray(), new SparseLongArray()); pkgInfo2.getPendingBackups().add(5); pkgInfo2.getPendingRestores().add( diff --git a/services/tests/servicestests/src/com/android/server/rollback/RollbackUnitTest.java b/services/tests/servicestests/src/com/android/server/rollback/RollbackUnitTest.java index e368d634b968..164c88382828 100644 --- a/services/tests/servicestests/src/com/android/server/rollback/RollbackUnitTest.java +++ b/services/tests/servicestests/src/com/android/server/rollback/RollbackUnitTest.java @@ -295,7 +295,8 @@ public class RollbackUnitTest { String packageName, long fromVersion, long toVersion, boolean isApex) { return new PackageRollbackInfo(new VersionedPackage(packageName, fromVersion), new VersionedPackage(packageName, toVersion), - new IntArray(), new ArrayList<>(), isApex, new IntArray(), new SparseLongArray()); + new IntArray(), new ArrayList<>(), isApex, false, new IntArray(), + new SparseLongArray()); } private static class PackageRollbackInfoForPackage implements diff --git a/services/tests/uiservicestests/src/com/android/server/notification/NotificationManagerServiceTest.java b/services/tests/uiservicestests/src/com/android/server/notification/NotificationManagerServiceTest.java index ae597e36a367..62f52306ea49 100755 --- a/services/tests/uiservicestests/src/com/android/server/notification/NotificationManagerServiceTest.java +++ b/services/tests/uiservicestests/src/com/android/server/notification/NotificationManagerServiceTest.java @@ -3213,9 +3213,11 @@ public class NotificationManagerServiceTest extends UiServiceTestCase { final NotificationVisibility nv = NotificationVisibility.obtain(r.getKey(), 1, 2, true); mService.mNotificationDelegate.onNotificationVisibilityChanged( new NotificationVisibility[] {nv}, new NotificationVisibility[]{}); + verify(mAssistants).notifyAssistantVisibilityChangedLocked(eq(r.sbn), eq(true)); assertTrue(mService.getNotificationRecord(r.getKey()).getStats().hasSeen()); mService.mNotificationDelegate.onNotificationVisibilityChanged( new NotificationVisibility[] {}, new NotificationVisibility[]{nv}); + verify(mAssistants).notifyAssistantVisibilityChangedLocked(eq(r.sbn), eq(false)); assertTrue(mService.getNotificationRecord(r.getKey()).getStats().hasSeen()); } @@ -4466,6 +4468,16 @@ public class NotificationManagerServiceTest extends UiServiceTestCase { } @Test + public void testOnPanelRevealedAndHidden() { + int items = 5; + mService.mNotificationDelegate.onPanelRevealed(false, items); + verify(mAssistants, times(1)).onPanelRevealed(eq(items)); + + mService.mNotificationDelegate.onPanelHidden(); + verify(mAssistants, times(1)).onPanelHidden(); + } + + @Test public void testOnNotificationSmartReplySent() { final int replyIndex = 2; final String reply = "Hello"; diff --git a/services/tests/wmtests/src/com/android/server/wm/TaskOrganizerTests.java b/services/tests/wmtests/src/com/android/server/wm/TaskOrganizerTests.java new file mode 100644 index 000000000000..8d2da1e6cb5b --- /dev/null +++ b/services/tests/wmtests/src/com/android/server/wm/TaskOrganizerTests.java @@ -0,0 +1,170 @@ +/* + * Copyright (C) 2017 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.server.wm; + +import static android.app.WindowConfiguration.WINDOWING_MODE_FREEFORM; +import static android.app.WindowConfiguration.WINDOWING_MODE_FULLSCREEN; +import static android.app.WindowConfiguration.WINDOWING_MODE_PINNED; + +import static com.android.dx.mockito.inline.extended.ExtendedMockito.doReturn; +import static com.android.dx.mockito.inline.extended.ExtendedMockito.times; +import static com.android.dx.mockito.inline.extended.ExtendedMockito.verify; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertNotEquals; +import static org.junit.Assert.assertNotNull; +import static org.junit.Assert.assertNull; +import static org.junit.Assert.assertTrue; +import static org.junit.Assert.assertFalse; +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.ArgumentMatchers.anyInt; +import static com.android.dx.mockito.inline.extended.ExtendedMockito.mock; +import static com.android.dx.mockito.inline.extended.ExtendedMockito.never; +import static com.android.dx.mockito.inline.extended.ExtendedMockito.times; +import static com.android.dx.mockito.inline.extended.ExtendedMockito.verify; +import static com.android.dx.mockito.inline.extended.ExtendedMockito.verify; +import static com.android.dx.mockito.inline.extended.ExtendedMockito.when; + +import android.graphics.Point; +import android.graphics.Rect; +import android.platform.test.annotations.Presubmit; +import android.os.Binder; +import android.os.RemoteException; +import android.view.ITaskOrganizer; +import android.view.SurfaceControl; +import static android.view.WindowManager.LayoutParams.TYPE_APPLICATION; + +import androidx.test.filters.SmallTest; + +import org.junit.Test; +import org.junit.runner.RunWith; + +/** + * Test class for {@link TaskOrganizer}. + * + * Build/Install/Run: + * atest WmTests:TaskOrganizerTests + */ +@SmallTest +@Presubmit +@RunWith(WindowTestRunner.class) +public class TaskOrganizerTests extends WindowTestsBase { + private ITaskOrganizer makeAndRegisterMockOrganizer() { + final ITaskOrganizer organizer = mock(ITaskOrganizer.class); + when(organizer.asBinder()).thenReturn(new Binder()); + + mWm.mAtmService.registerTaskOrganizer(organizer, WINDOWING_MODE_PINNED); + + return organizer; + } + + @Test + public void testAppearVanish() throws RemoteException { + final ActivityStack stack = createTaskStackOnDisplay(mDisplayContent); + final Task task = createTaskInStack(stack, 0 /* userId */); + final ITaskOrganizer organizer = makeAndRegisterMockOrganizer(); + + task.setTaskOrganizer(organizer); + verify(organizer).taskAppeared(any(), any()); + assertTrue(task.isControlledByTaskOrganizer()); + + task.removeImmediately(); + verify(organizer).taskVanished(any()); + } + + @Test + public void testSwapOrganizer() throws RemoteException { + final ActivityStack stack = createTaskStackOnDisplay(mDisplayContent); + final Task task = createTaskInStack(stack, 0 /* userId */); + final ITaskOrganizer organizer = makeAndRegisterMockOrganizer(); + final ITaskOrganizer organizer2 = makeAndRegisterMockOrganizer(); + + task.setTaskOrganizer(organizer); + verify(organizer).taskAppeared(any(), any()); + task.setTaskOrganizer(organizer2); + verify(organizer).taskVanished(any()); + verify(organizer2).taskAppeared(any(), any()); + } + + @Test + public void testClearOrganizer() throws RemoteException { + final ActivityStack stack = createTaskStackOnDisplay(mDisplayContent); + final Task task = createTaskInStack(stack, 0 /* userId */); + final ITaskOrganizer organizer = makeAndRegisterMockOrganizer(); + + task.setTaskOrganizer(organizer); + verify(organizer).taskAppeared(any(), any()); + assertTrue(task.isControlledByTaskOrganizer()); + + task.setTaskOrganizer(null); + verify(organizer).taskVanished(any()); + assertFalse(task.isControlledByTaskOrganizer()); + } + + @Test + public void testTransferStackToOrganizer() throws RemoteException { + final ActivityStack stack = createTaskStackOnDisplay(mDisplayContent); + final Task task = createTaskInStack(stack, 0 /* userId */); + final Task task2 = createTaskInStack(stack, 0 /* userId */); + final ITaskOrganizer organizer = makeAndRegisterMockOrganizer(); + + stack.transferToTaskOrganizer(organizer); + + verify(organizer, times(2)).taskAppeared(any(), any()); + assertTrue(task.isControlledByTaskOrganizer()); + assertTrue(task2.isControlledByTaskOrganizer()); + + stack.transferToTaskOrganizer(null); + + verify(organizer, times(2)).taskVanished(any()); + assertFalse(task.isControlledByTaskOrganizer()); + assertFalse(task2.isControlledByTaskOrganizer()); + } + + @Test + public void testRegisterTaskOrganizerTaskWindowingModeChanges() throws RemoteException { + final ITaskOrganizer organizer = makeAndRegisterMockOrganizer(); + + final ActivityStack stack = createTaskStackOnDisplay(mDisplayContent); + final Task task = createTaskInStack(stack, 0 /* userId */); + task.setWindowingMode(WINDOWING_MODE_PINNED); + verify(organizer).taskAppeared(any(), any()); + assertTrue(task.isControlledByTaskOrganizer()); + + task.setWindowingMode(WINDOWING_MODE_FULLSCREEN); + verify(organizer).taskVanished(any()); + assertFalse(task.isControlledByTaskOrganizer()); + } + + @Test + public void testRegisterTaskOrganizerStackWindowingModeChanges() throws RemoteException { + final ITaskOrganizer organizer = makeAndRegisterMockOrganizer(); + + final ActivityStack stack = createTaskStackOnDisplay(mDisplayContent); + final Task task = createTaskInStack(stack, 0 /* userId */); + final Task task2 = createTaskInStack(stack, 0 /* userId */); + stack.setWindowingMode(WINDOWING_MODE_PINNED); + verify(organizer, times(2)).taskAppeared(any(), any()); + assertTrue(task.isControlledByTaskOrganizer()); + assertTrue(task2.isControlledByTaskOrganizer()); + + stack.setWindowingMode(WINDOWING_MODE_FULLSCREEN); + verify(organizer, times(2)).taskVanished(any()); + assertFalse(task.isControlledByTaskOrganizer()); + assertFalse(task2.isControlledByTaskOrganizer()); + } +} diff --git a/telephony/common/com/android/internal/telephony/CarrierAppUtils.java b/telephony/common/com/android/internal/telephony/CarrierAppUtils.java index 368f8f1dab2e..97bcbc061f8a 100644 --- a/telephony/common/com/android/internal/telephony/CarrierAppUtils.java +++ b/telephony/common/com/android/internal/telephony/CarrierAppUtils.java @@ -18,11 +18,13 @@ package com.android.internal.telephony; import android.annotation.Nullable; import android.content.ContentResolver; +import android.content.Context; import android.content.pm.ApplicationInfo; import android.content.pm.IPackageManager; import android.content.pm.PackageManager; import android.content.res.Resources; import android.os.RemoteException; +import android.os.UserHandle; import android.permission.IPermissionManager; import android.provider.Settings; import com.android.telephony.Rlog; @@ -76,7 +78,7 @@ public final class CarrierAppUtils { */ public static synchronized void disableCarrierAppsUntilPrivileged(String callingPackage, IPackageManager packageManager, IPermissionManager permissionManager, - TelephonyManager telephonyManager, ContentResolver contentResolver, int userId) { + TelephonyManager telephonyManager, int userId, Context context) { if (DEBUG) { Rlog.d(TAG, "disableCarrierAppsUntilPrivileged"); } @@ -85,6 +87,7 @@ public final class CarrierAppUtils { config.getDisabledUntilUsedPreinstalledCarrierApps(); ArrayMap<String, List<String>> systemCarrierAssociatedAppsDisabledUntilUsed = config.getDisabledUntilUsedPreinstalledCarrierAssociatedApps(); + ContentResolver contentResolver = getContentResolverForUser(context, userId); disableCarrierAppsUntilPrivileged(callingPackage, packageManager, permissionManager, telephonyManager, contentResolver, userId, systemCarrierAppsDisabledUntilUsed, systemCarrierAssociatedAppsDisabledUntilUsed); @@ -102,8 +105,8 @@ public final class CarrierAppUtils { * Manager can kill it, and this can lead to crashes as the app is in an unexpected state. */ public static synchronized void disableCarrierAppsUntilPrivileged(String callingPackage, - IPackageManager packageManager, IPermissionManager permissionManager, - ContentResolver contentResolver, int userId) { + IPackageManager packageManager, IPermissionManager permissionManager, int userId, + Context context) { if (DEBUG) { Rlog.d(TAG, "disableCarrierAppsUntilPrivileged"); } @@ -114,15 +117,23 @@ public final class CarrierAppUtils { ArrayMap<String, List<String>> systemCarrierAssociatedAppsDisabledUntilUsed = config.getDisabledUntilUsedPreinstalledCarrierAssociatedApps(); + ContentResolver contentResolver = getContentResolverForUser(context, userId); disableCarrierAppsUntilPrivileged(callingPackage, packageManager, permissionManager, null /* telephonyManager */, contentResolver, userId, systemCarrierAppsDisabledUntilUsed, systemCarrierAssociatedAppsDisabledUntilUsed); } + private static ContentResolver getContentResolverForUser(Context context, int userId) { + Context userContext = context.createContextAsUser(UserHandle.getUserHandleForUid(userId), + 0); + return userContext.getContentResolver(); + } + /** * Disable carrier apps until they are privileged * Must be public b/c framework unit tests can't access package-private methods. */ + // Must be public b/c framework unit tests can't access package-private methods. @VisibleForTesting public static void disableCarrierAppsUntilPrivileged(String callingPackage, IPackageManager packageManager, IPermissionManager permissionManager, @@ -142,9 +153,8 @@ public final class CarrierAppUtils { systemCarrierAssociatedAppsDisabledUntilUsed); List<String> enabledCarrierPackages = new ArrayList<>(); - - boolean hasRunOnce = Settings.Secure.getIntForUser( - contentResolver, Settings.Secure.CARRIER_APPS_HANDLED, 0, userId) == 1; + boolean hasRunOnce = Settings.Secure.getInt(contentResolver, + Settings.Secure.CARRIER_APPS_HANDLED, 0) == 1; try { for (ApplicationInfo ai : candidates) { @@ -259,8 +269,7 @@ public final class CarrierAppUtils { // Mark the execution so we do not disable apps again. if (!hasRunOnce) { - Settings.Secure.putIntForUser( - contentResolver, Settings.Secure.CARRIER_APPS_HANDLED, 1, userId); + Settings.Secure.putInt(contentResolver, Settings.Secure.CARRIER_APPS_HANDLED, 1); } if (!enabledCarrierPackages.isEmpty()) { diff --git a/telephony/java/android/telephony/CellLocation.java b/telephony/java/android/telephony/CellLocation.java index 11931996009c..2d0bd52f84ee 100644 --- a/telephony/java/android/telephony/CellLocation.java +++ b/telephony/java/android/telephony/CellLocation.java @@ -53,8 +53,7 @@ public abstract class CellLocation { /** * Create a new CellLocation from a intent notifier Bundle * - * This method is used by PhoneStateIntentReceiver and maybe by - * external applications. + * This method maybe used by external applications. * * @param bundle Bundle from intent notifier * @return newly created CellLocation diff --git a/telephony/java/android/telephony/SignalStrength.java b/telephony/java/android/telephony/SignalStrength.java index 1f7d55f2758a..1c58f8faf7cf 100644 --- a/telephony/java/android/telephony/SignalStrength.java +++ b/telephony/java/android/telephony/SignalStrength.java @@ -87,8 +87,7 @@ public class SignalStrength implements Parcelable { /** * Create a new SignalStrength from a intent notifier Bundle * - * This method is used by PhoneStateIntentReceiver and maybe by - * external applications. + * This method may be used by external applications. * * @param m Bundle from intent notifier * @return newly created SignalStrength diff --git a/telephony/java/android/telephony/SmsManager.java b/telephony/java/android/telephony/SmsManager.java index db33be313941..eb328a705e56 100644 --- a/telephony/java/android/telephony/SmsManager.java +++ b/telephony/java/android/telephony/SmsManager.java @@ -2448,18 +2448,14 @@ public final class SmsManager { * @param sentIntent if not NULL this <code>PendingIntent</code> is * broadcast when the message is successfully sent, or failed * @throws IllegalArgumentException if contentUri is empty - * @deprecated use {@link MmsManager#sendMultimediaMessage} instead. */ public void sendMultimediaMessage(Context context, Uri contentUri, String locationUrl, Bundle configOverrides, PendingIntent sentIntent) { if (contentUri == null) { throw new IllegalArgumentException("Uri contentUri null"); } - MmsManager m = (MmsManager) context.getSystemService(Context.MMS_SERVICE); - if (m != null) { - m.sendMultimediaMessage(getSubscriptionId(), contentUri, locationUrl, configOverrides, - sentIntent); - } + MmsManager.getInstance().sendMultimediaMessage(getSubscriptionId(), contentUri, + locationUrl, configOverrides, sentIntent); } /** @@ -2483,7 +2479,6 @@ public final class SmsManager { * @param downloadedIntent if not NULL this <code>PendingIntent</code> is * broadcast when the message is downloaded, or the download is failed * @throws IllegalArgumentException if locationUrl or contentUri is empty - * @deprecated use {@link MmsManager#downloadMultimediaMessage} instead. */ public void downloadMultimediaMessage(Context context, String locationUrl, Uri contentUri, Bundle configOverrides, PendingIntent downloadedIntent) { @@ -2493,11 +2488,8 @@ public final class SmsManager { if (contentUri == null) { throw new IllegalArgumentException("Uri contentUri null"); } - MmsManager m = (MmsManager) context.getSystemService(Context.MMS_SERVICE); - if (m != null) { - m.downloadMultimediaMessage(getSubscriptionId(), locationUrl, contentUri, - configOverrides, downloadedIntent); - } + MmsManager.getInstance().downloadMultimediaMessage(getSubscriptionId(), locationUrl, + contentUri, configOverrides, downloadedIntent); } // MMS send/download failure result codes @@ -2539,9 +2531,9 @@ public final class SmsManager { * </p> * * @return the bundle key/values pairs that contains MMS configuration values - * or an empty bundle if they cannot be found. */ - @NonNull public Bundle getCarrierConfigValues() { + @Nullable + public Bundle getCarrierConfigValues() { try { ISms iSms = getISmsService(); if (iSms != null) { @@ -2550,7 +2542,7 @@ public final class SmsManager { } catch (RemoteException ex) { // ignore it } - return new Bundle(); + return null; } /** diff --git a/telephony/java/android/telephony/TelephonyManager.java b/telephony/java/android/telephony/TelephonyManager.java index 843c0656efc3..405b3a583247 100644 --- a/telephony/java/android/telephony/TelephonyManager.java +++ b/telephony/java/android/telephony/TelephonyManager.java @@ -46,6 +46,7 @@ import android.compat.annotation.UnsupportedAppUsage; import android.content.ComponentName; import android.content.Context; import android.content.Intent; +import android.content.pm.PackageManager; import android.database.Cursor; import android.net.ConnectivityManager; import android.net.NetworkStats; @@ -449,12 +450,8 @@ public class TelephonyManager { case UNKNOWN: modemCount = MODEM_COUNT_SINGLE_MODEM; // check for voice and data support, 0 if not supported - if (!isVoiceCapable() && !isSmsCapable() && mContext != null) { - ConnectivityManager cm = (ConnectivityManager) mContext - .getSystemService(Context.CONNECTIVITY_SERVICE); - if (cm != null && !cm.isNetworkSupported(ConnectivityManager.TYPE_MOBILE)) { - modemCount = MODEM_COUNT_NO_MODEM; - } + if (!isVoiceCapable() && !isSmsCapable() && !isDataCapable()) { + modemCount = MODEM_COUNT_NO_MODEM; } break; case DSDS: @@ -10669,12 +10666,21 @@ public class TelephonyManager { } /** + * Checks whether cellular data connection is enabled in the device. + * + * Whether cellular data connection is enabled, meaning upon request whether will try to setup + * metered data connection considering all factors below: + * 1) User turned on data setting {@link #isDataEnabled}. + * 2) Carrier allows data to be on. + * 3) Network policy. + * And possibly others. + * + * @return {@code true} if the overall data connection is capable; {@code false} if not. * @hide - * It's similar to isDataEnabled, but unlike isDataEnabled, this API also evaluates - * carrierDataEnabled, policyDataEnabled etc to give a final decision of whether mobile data is - * capable of using. */ - public boolean isDataCapable() { + @SystemApi + @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) + public boolean isDataConnectionEnabled() { boolean retVal = false; try { int subId = getSubId(SubscriptionManager.getDefaultDataSubscriptionId()); @@ -10682,13 +10688,24 @@ public class TelephonyManager { if (telephony != null) retVal = telephony.isDataEnabled(subId); } catch (RemoteException e) { - Log.e(TAG, "Error calling ITelephony#isDataEnabled", e); + Log.e(TAG, "Error isDataConnectionEnabled", e); } catch (NullPointerException e) { } return retVal; } /** + * Checks if FEATURE_TELEPHONY_DATA is enabled. + * + * @hide + */ + public boolean isDataCapable() { + if (mContext == null) return true; + return mContext.getPackageManager().hasSystemFeature( + PackageManager.FEATURE_TELEPHONY_DATA); + } + + /** * In this mode, modem will not send specified indications when screen is off. * @hide */ diff --git a/telephony/java/android/telephony/VoLteServiceState.java b/telephony/java/android/telephony/VoLteServiceState.java index 121401277ce9..d4a27d925208 100644 --- a/telephony/java/android/telephony/VoLteServiceState.java +++ b/telephony/java/android/telephony/VoLteServiceState.java @@ -53,8 +53,7 @@ public final class VoLteServiceState implements Parcelable { /** * Create a new VoLteServiceState from a intent notifier Bundle * - * This method is used by PhoneStateIntentReceiver and maybe by - * external applications. + * This method is maybe used by external applications. * * @param m Bundle from intent notifier * @return newly created VoLteServiceState diff --git a/telephony/java/com/android/internal/telephony/TelephonyIntents.java b/telephony/java/com/android/internal/telephony/TelephonyIntents.java index f78c65ffb3aa..54c07cfd3428 100644 --- a/telephony/java/com/android/internal/telephony/TelephonyIntents.java +++ b/telephony/java/com/android/internal/telephony/TelephonyIntents.java @@ -123,32 +123,6 @@ public class TelephonyIntents { public static final String ACTION_EMERGENCY_CALL_STATE_CHANGED = TelephonyManager.ACTION_EMERGENCY_CALL_STATE_CHANGED; - /** - * Broadcast Action: The phone's signal strength has changed. The intent will have the - * following extra values:</p> - * <ul> - * <li><em>phoneName</em> - A string version of the phone name.</li> - * <li><em>asu</em> - A numeric value for the signal strength. - * An ASU is 0-31 or -1 if unknown (for GSM, dBm = -113 - 2 * asu). - * The following special values are defined: - * <ul><li>0 means "-113 dBm or less".</li><li>31 means "-51 dBm or greater".</li></ul> - * </li> - * </ul> - * - * <p class="note"> - * You can <em>not</em> receive this through components declared - * in manifests, only by exlicitly registering for it with - * {@link android.content.Context#registerReceiver(android.content.BroadcastReceiver, - * android.content.IntentFilter) Context.registerReceiver()}. - * - * <p class="note"> - * Requires the READ_PHONE_STATE permission. - * - * <p class="note">This is a protected intent that can only be sent - * by the system. - */ - public static final String ACTION_SIGNAL_STRENGTH_CHANGED = "android.intent.action.SIG_STR"; - /** * Broadcast Action: The data connection state has changed for any one of the diff --git a/tests/RollbackTest/Android.bp b/tests/RollbackTest/Android.bp index 2bc129ae4840..091edd4dc0d9 100644 --- a/tests/RollbackTest/Android.bp +++ b/tests/RollbackTest/Android.bp @@ -19,15 +19,17 @@ android_test { static_libs: ["androidx.test.rules", "cts-rollback-lib", "cts-install-lib"], test_suites: ["general-tests"], test_config: "RollbackTest.xml", + java_resources: [":com.android.apex.apkrollback.test_v2"], } java_test_host { name: "StagedRollbackTest", srcs: ["StagedRollbackTest/src/**/*.java"], libs: ["tradefed"], - static_libs: ["testng"], + static_libs: ["testng", "compatibility-tradefed"], test_suites: ["general-tests"], test_config: "StagedRollbackTest.xml", + data: [":com.android.apex.apkrollback.test_v1"], } java_test_host { @@ -37,3 +39,44 @@ java_test_host { test_suites: ["general-tests"], test_config: "MultiUserRollbackTest.xml", } + +genrule { + name: "com.android.apex.apkrollback.test.pem", + out: ["com.android.apex.apkrollback.test.pem"], + cmd: "openssl genrsa -out $(out) 4096", +} + +genrule { + name: "com.android.apex.apkrollback.test.pubkey", + srcs: [":com.android.apex.apkrollback.test.pem"], + out: ["com.android.apex.apkrollback.test.pubkey"], + tools: ["avbtool"], + cmd: "$(location avbtool) extract_public_key --key $(in) --output $(out)", +} + +apex_key { + name: "com.android.apex.apkrollback.test.key", + private_key: ":com.android.apex.apkrollback.test.pem", + public_key: ":com.android.apex.apkrollback.test.pubkey", + installable: false, +} + +apex { + name: "com.android.apex.apkrollback.test_v1", + manifest: "testdata/manifest_v1.json", + androidManifest: "testdata/AndroidManifest.xml", + file_contexts: ":apex.test-file_contexts", + key: "com.android.apex.apkrollback.test.key", + apps: ["TestAppAv1"], + installable: false, +} + +apex { + name: "com.android.apex.apkrollback.test_v2", + manifest: "testdata/manifest_v2.json", + androidManifest: "testdata/AndroidManifest.xml", + file_contexts: ":apex.test-file_contexts", + key: "com.android.apex.apkrollback.test.key", + apps: ["TestAppAv2"], + installable: false, +}
\ No newline at end of file diff --git a/tests/RollbackTest/RollbackTest/src/com/android/tests/rollback/StagedRollbackTest.java b/tests/RollbackTest/RollbackTest/src/com/android/tests/rollback/StagedRollbackTest.java index 9e490f765eab..3877cc139a3e 100644 --- a/tests/RollbackTest/RollbackTest/src/com/android/tests/rollback/StagedRollbackTest.java +++ b/tests/RollbackTest/RollbackTest/src/com/android/tests/rollback/StagedRollbackTest.java @@ -435,11 +435,64 @@ public class StagedRollbackTest { // testNativeWatchdogTriggersRollback will fail if multiple staged sessions are // committed on a device which doesn't support checkpoint. Let's clean up all rollbacks // so there is only one rollback to commit when testing native crashes. - RollbackManager rm = RollbackUtils.getRollbackManager(); + RollbackManager rm = RollbackUtils.getRollbackManager(); rm.getAvailableRollbacks().stream().flatMap(info -> info.getPackages().stream()) .map(info -> info.getPackageName()).forEach(rm::expireRollbackForPackage); } + private static final String APK_IN_APEX_TESTAPEX_NAME = "com.android.apex.apkrollback.test"; + private static final TestApp TEST_APEX_WITH_APK_V1 = new TestApp("TestApexWithApkV1", + APK_IN_APEX_TESTAPEX_NAME, 1, /*isApex*/true, APK_IN_APEX_TESTAPEX_NAME + "_v1.apex"); + private static final TestApp TEST_APEX_WITH_APK_V2 = new TestApp("TestApexWithApkV2", + APK_IN_APEX_TESTAPEX_NAME, 2, /*isApex*/true, APK_IN_APEX_TESTAPEX_NAME + "_v2.apex"); + private static final TestApp TEST_APP_A_V2_UNKNOWN = new TestApp("Av2Unknown", TestApp.A, 0, + /*isApex*/false, "TestAppAv2.apk"); + + @Test + public void testRollbackApexWithApk_Phase1() throws Exception { + assertThat(InstallUtils.getInstalledVersion(TestApp.A)).isEqualTo(1); + InstallUtils.processUserData(TestApp.A); + + int sessionId = Install.single(TEST_APEX_WITH_APK_V2).setStaged().setEnableRollback() + .commit(); + InstallUtils.waitForSessionReady(sessionId); + } + + @Test + public void testRollbackApexWithApk_Phase2() throws Exception { + assertThat(InstallUtils.getInstalledVersion(APK_IN_APEX_TESTAPEX_NAME)).isEqualTo(2); + assertThat(InstallUtils.getInstalledVersion(TestApp.A)).isEqualTo(2); + InstallUtils.processUserData(TestApp.A); + + RollbackInfo available = RollbackUtils.getAvailableRollback(APK_IN_APEX_TESTAPEX_NAME); + assertThat(available).isStaged(); + assertThat(available).packagesContainsExactly( + Rollback.from(TEST_APEX_WITH_APK_V2).to(TEST_APEX_WITH_APK_V1), + Rollback.from(TEST_APP_A_V2_UNKNOWN).to(TestApp.A1)); + + RollbackUtils.rollback(available.getRollbackId(), TEST_APEX_WITH_APK_V2); + RollbackInfo committed = RollbackUtils.getCommittedRollbackById(available.getRollbackId()); + assertThat(committed).isNotNull(); + assertThat(committed).isStaged(); + assertThat(committed).packagesContainsExactly( + Rollback.from(TEST_APEX_WITH_APK_V2).to(TEST_APEX_WITH_APK_V1), + Rollback.from(TEST_APP_A_V2_UNKNOWN).to(TestApp.A1)); + assertThat(committed).causePackagesContainsExactly(TEST_APEX_WITH_APK_V2); + assertThat(committed.getCommittedSessionId()).isNotEqualTo(-1); + + // Note: The app is not rolled back until after the rollback is staged + // and the device has been rebooted. + InstallUtils.waitForSessionReady(committed.getCommittedSessionId()); + assertThat(InstallUtils.getInstalledVersion(APK_IN_APEX_TESTAPEX_NAME)).isEqualTo(2); + } + + @Test + public void testRollbackApexWithApk_Phase3() throws Exception { + assertThat(InstallUtils.getInstalledVersion(APK_IN_APEX_TESTAPEX_NAME)).isEqualTo(1); + assertThat(InstallUtils.getInstalledVersion(TestApp.A)).isEqualTo(1); + InstallUtils.processUserData(TestApp.A); + } + private static void runShellCommand(String cmd) { ParcelFileDescriptor pfd = InstrumentationRegistry.getInstrumentation().getUiAutomation() .executeShellCommand(cmd); diff --git a/tests/RollbackTest/StagedRollbackTest/src/com/android/tests/rollback/host/StagedRollbackTest.java b/tests/RollbackTest/StagedRollbackTest/src/com/android/tests/rollback/host/StagedRollbackTest.java index 91577c202df9..6daa6bc723c4 100644 --- a/tests/RollbackTest/StagedRollbackTest/src/com/android/tests/rollback/host/StagedRollbackTest.java +++ b/tests/RollbackTest/StagedRollbackTest/src/com/android/tests/rollback/host/StagedRollbackTest.java @@ -19,6 +19,7 @@ package com.android.tests.rollback.host; import static org.junit.Assert.assertTrue; import static org.testng.Assert.assertThrows; +import com.android.compatibility.common.tradefed.build.CompatibilityBuildHelper; import com.android.tradefed.testtype.DeviceJUnit4ClassRunner; import com.android.tradefed.testtype.junit4.BaseHostJUnit4Test; @@ -27,6 +28,7 @@ import org.junit.Before; import org.junit.Test; import org.junit.runner.RunWith; +import java.io.File; import java.util.concurrent.TimeUnit; /** @@ -48,14 +50,32 @@ public class StagedRollbackTest extends BaseHostJUnit4Test { phase)); } + private static final String APK_IN_APEX_TESTAPEX_NAME = "com.android.apex.apkrollback.test"; + @Before public void setUp() throws Exception { + if (!getDevice().isAdbRoot()) { + getDevice().enableAdbRoot(); + } + getDevice().remountSystemWritable(); + getDevice().executeShellCommand( + "rm -f /system/apex/" + APK_IN_APEX_TESTAPEX_NAME + "*.apex " + + "/data/apex/active/" + APK_IN_APEX_TESTAPEX_NAME + "*.apex"); getDevice().reboot(); } @After public void tearDown() throws Exception { runPhase("testCleanUp"); + + if (!getDevice().isAdbRoot()) { + getDevice().enableAdbRoot(); + } + getDevice().remountSystemWritable(); + getDevice().executeShellCommand( + "rm -f /system/apex/" + APK_IN_APEX_TESTAPEX_NAME + "*.apex " + + "/data/apex/active/" + APK_IN_APEX_TESTAPEX_NAME + "*.apex"); + getDevice().reboot(); } /** @@ -184,6 +204,28 @@ public class StagedRollbackTest extends BaseHostJUnit4Test { runPhase("testRollbackDataPolicy_Phase3"); } + /** + * Tests that userdata of apk-in-apex is restored when apex is rolled back. + */ + @Test + public void testRollbackApexWithApk() throws Exception { + getDevice().uninstallPackage("com.android.cts.install.lib.testapp.A"); + CompatibilityBuildHelper buildHelper = new CompatibilityBuildHelper(getBuild()); + final String fileName = APK_IN_APEX_TESTAPEX_NAME + "_v1.apex"; + final File apex = buildHelper.getTestFile(fileName); + if (!getDevice().isAdbRoot()) { + getDevice().enableAdbRoot(); + } + getDevice().remountSystemWritable(); + assertTrue(getDevice().pushFile(apex, "/system/apex/" + fileName)); + getDevice().reboot(); + runPhase("testRollbackApexWithApk_Phase1"); + getDevice().reboot(); + runPhase("testRollbackApexWithApk_Phase2"); + getDevice().reboot(); + runPhase("testRollbackApexWithApk_Phase3"); + } + private void crashProcess(String processName, int numberOfCrashes) throws Exception { String pid = ""; String lastPid = "invalid"; diff --git a/tests/RollbackTest/testdata/AndroidManifest.xml b/tests/RollbackTest/testdata/AndroidManifest.xml new file mode 100644 index 000000000000..f21ec899eb69 --- /dev/null +++ b/tests/RollbackTest/testdata/AndroidManifest.xml @@ -0,0 +1,8 @@ +<?xml version="1.0" encoding="utf-8"?> +<manifest xmlns:android="http://schemas.android.com/apk/res/android" + package="com.android.apex.apkrollback.test"> + <!-- APEX does not have classes.dex --> + <application android:hasCode="false" /> + <uses-sdk android:minSdkVersion="28" android:targetSdkVersion="29"/> +</manifest> + diff --git a/tests/RollbackTest/testdata/manifest_v1.json b/tests/RollbackTest/testdata/manifest_v1.json new file mode 100644 index 000000000000..1762fc6764cf --- /dev/null +++ b/tests/RollbackTest/testdata/manifest_v1.json @@ -0,0 +1,4 @@ +{ + "name": "com.android.apex.apkrollback.test", + "version": 1 +} diff --git a/tests/RollbackTest/testdata/manifest_v2.json b/tests/RollbackTest/testdata/manifest_v2.json new file mode 100644 index 000000000000..c5127b9c3023 --- /dev/null +++ b/tests/RollbackTest/testdata/manifest_v2.json @@ -0,0 +1,4 @@ +{ + "name": "com.android.apex.apkrollback.test", + "version": 2 +} diff --git a/tests/TaskOrganizerTest/Android.bp b/tests/TaskOrganizerTest/Android.bp new file mode 100644 index 000000000000..8a13dbc52c66 --- /dev/null +++ b/tests/TaskOrganizerTest/Android.bp @@ -0,0 +1,22 @@ +// +// Copyright (C) 2019 The Android Open Source Project +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// + +android_test { + name: "TaskOrganizerTest", + srcs: ["**/*.java"], + platform_apis: true, + certificate: "platform", +} diff --git a/tests/TaskOrganizerTest/AndroidManifest.xml b/tests/TaskOrganizerTest/AndroidManifest.xml new file mode 100644 index 000000000000..0cb6c10a7ff5 --- /dev/null +++ b/tests/TaskOrganizerTest/AndroidManifest.xml @@ -0,0 +1,23 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- Copyright (C) 2019 The Android Open Source Project + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + http://www.apache.org/licenses/LICENSE-2.0 + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +--> +<manifest xmlns:android="http://schemas.android.com/apk/res/android" + package="com.android.test.taskembed"> + <uses-permission android:name="android.permission.CHANGE_CONFIGURATION" /> + <uses-permission android:name="android.permission.MANAGE_ACTIVITY_STACKS" /> + <uses-permission android:name="android.permission.INTERACT_ACROSS_USERS_FULL" /> + <application> + <service android:name=".TaskOrganizerPipTest" + android:exported="true"> + </service> + </application> +</manifest> diff --git a/tests/TaskOrganizerTest/src/com/android/test/taskembed/TaskOrganizerPipTest.java b/tests/TaskOrganizerTest/src/com/android/test/taskembed/TaskOrganizerPipTest.java new file mode 100644 index 000000000000..6ffa19d4ec98 --- /dev/null +++ b/tests/TaskOrganizerTest/src/com/android/test/taskembed/TaskOrganizerPipTest.java @@ -0,0 +1,127 @@ +/* + * Copyright (C) 2019 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.test.taskembed; + +import android.app.ActivityManager; +import android.app.ActivityTaskManager; +import android.app.Service; +import android.app.WindowConfiguration; +import android.content.Context; +import android.content.Intent; +import android.graphics.Rect; +import android.os.IBinder; +import android.view.ITaskOrganizer; +import android.view.IWindowContainer; +import android.view.WindowContainerTransaction; +import android.view.SurfaceControl; +import android.view.SurfaceHolder; +import android.view.SurfaceView; +import android.view.ViewGroup; +import android.view.WindowManager; +import android.widget.FrameLayout; + +public class TaskOrganizerPipTest extends Service { + static final int PIP_WIDTH = 640; + static final int PIP_HEIGHT = 360; + + class PipOrgView extends SurfaceView implements SurfaceHolder.Callback { + PipOrgView(Context c) { + super(c); + getHolder().addCallback(this); + setZOrderOnTop(true); + } + @Override + public void surfaceCreated(SurfaceHolder holder) { + try { + ActivityTaskManager.getService().registerTaskOrganizer(mOrganizer, + WindowConfiguration.WINDOWING_MODE_PINNED); + } catch (Exception e) { + } + } + + @Override + public void surfaceChanged(SurfaceHolder holder, int format, int width, int height) { + } + + @Override + public void surfaceDestroyed(SurfaceHolder holder) { + } + + void reparentTask(IWindowContainer wc) { + SurfaceControl.Transaction t = new SurfaceControl.Transaction(); + SurfaceControl leash = null; + try { + leash = wc.getLeash(); + } catch (Exception e) { + // System server died.. oh well + } + t.reparent(leash, getSurfaceControl()) + .setPosition(leash, 0, 0) + .apply(); + } + } + + PipOrgView mPipView; + + class Organizer extends ITaskOrganizer.Stub { + public void taskAppeared(IWindowContainer wc, ActivityManager.RunningTaskInfo ti) { + mPipView.reparentTask(wc); + + final WindowContainerTransaction wct = new WindowContainerTransaction(); + wct.scheduleFinishEnterPip(wc, new Rect(0, 0, PIP_WIDTH, PIP_HEIGHT)); + try { + ActivityTaskManager.getService().applyContainerTransaction(wct); + } catch (Exception e) { + } + } + public void taskVanished(IWindowContainer wc) { + } + public void transactionReady(int id, SurfaceControl.Transaction t) { + } + } + + Organizer mOrganizer = new Organizer(); + + @Override + public IBinder onBind(Intent intent) { + return null; + } + + @Override + public void onCreate() { + super.onCreate(); + + final WindowManager.LayoutParams wlp = new WindowManager.LayoutParams(); + wlp.setTitle("TaskOrganizerPipTest"); + wlp.type = WindowManager.LayoutParams.TYPE_APPLICATION_OVERLAY; + wlp.width = wlp.height = ViewGroup.LayoutParams.WRAP_CONTENT; + + FrameLayout layout = new FrameLayout(this); + ViewGroup.LayoutParams lp = + new ViewGroup.LayoutParams(PIP_WIDTH, PIP_HEIGHT); + mPipView = new PipOrgView(this); + layout.addView(mPipView, lp); + + WindowManager wm = getSystemService(WindowManager.class); + wm.addView(layout, wlp); + } + + @Override + public void onDestroy() { + super.onDestroy(); + } +} diff --git a/tools/hiddenapi/generate_hiddenapi_lists.py b/tools/hiddenapi/generate_hiddenapi_lists.py index 46105f4d66b0..0b2077d9bba0 100755 --- a/tools/hiddenapi/generate_hiddenapi_lists.py +++ b/tools/hiddenapi/generate_hiddenapi_lists.py @@ -149,7 +149,12 @@ def extract_package(signature): The package name of the class containing the field/method. """ full_class_name = signature.split(";->")[0] - package_name = full_class_name[1:full_class_name.rindex("/")] + # Example: Landroid/hardware/radio/V1_2/IRadio$Proxy + if (full_class_name[0] != "L"): + raise ValueError("Expected to start with 'L': %s" % full_class_name) + full_class_name = full_class_name[1:] + # If full_class_name doesn't contain '/', then package_name will be ''. + package_name = full_class_name.rpartition("/")[0] return package_name.replace('/', '.') class FlagsDict: diff --git a/tools/lock_agent/Android.bp b/tools/lock_agent/Android.bp index 79dce4a8ce09..7b2ca9a65242 100644 --- a/tools/lock_agent/Android.bp +++ b/tools/lock_agent/Android.bp @@ -25,6 +25,7 @@ cc_binary_host { srcs: ["agent.cpp"], static_libs: [ "libbase", + "liblog", "libz", "slicer", ], diff --git a/wifi/java/android/net/wifi/WifiNetworkSuggestion.java b/wifi/java/android/net/wifi/WifiNetworkSuggestion.java index 9c1475ffc8cd..c0e089090dc9 100644 --- a/wifi/java/android/net/wifi/WifiNetworkSuggestion.java +++ b/wifi/java/android/net/wifi/WifiNetworkSuggestion.java @@ -116,12 +116,12 @@ public final class WifiNetworkSuggestion implements Parcelable { /** * Whether this network is shared credential with user to allow user manually connect. */ - private boolean mIsUserAllowed; + private boolean mIsSharedWithUser; /** - * Whether the setIsUserAllowedToManuallyConnect have been called. + * Whether the setCredentialSharedWithUser have been called. */ - private boolean mIsUserAllowedBeenSet; + private boolean mIsSharedWithUserSet; /** * Pre-shared key for use with WAPI-PSK networks. */ @@ -146,8 +146,8 @@ public final class WifiNetworkSuggestion implements Parcelable { mIsAppInteractionRequired = false; mIsUserInteractionRequired = false; mIsMetered = false; - mIsUserAllowed = true; - mIsUserAllowedBeenSet = false; + mIsSharedWithUser = true; + mIsSharedWithUserSet = false; mPriority = UNASSIGNED_PRIORITY; mCarrierId = TelephonyManager.UNKNOWN_CARRIER_ID; mWapiPskPassphrase = null; @@ -430,13 +430,13 @@ public final class WifiNetworkSuggestion implements Parcelable { * <li>If not set, defaults to true (i.e. allow user to manually connect) for secure * networks and false for open networks.</li> * - * @param isAllowed {@code true} to indicate that the credentials may be used by the user to + * @param isShared {@code true} to indicate that the credentials may be used by the user to * manually connect to the network, {@code false} otherwise. * @return Instance of {@link Builder} to enable chaining of the builder method. */ - public @NonNull Builder setIsUserAllowedToManuallyConnect(boolean isAllowed) { - mIsUserAllowed = isAllowed; - mIsUserAllowedBeenSet = true; + public @NonNull Builder setCredentialSharedWithUser(boolean isShared) { + mIsSharedWithUser = isShared; + mIsSharedWithUserSet = true; return this; } @@ -602,11 +602,11 @@ public final class WifiNetworkSuggestion implements Parcelable { } wifiConfiguration = buildWifiConfiguration(); if (wifiConfiguration.isOpenNetwork()) { - if (mIsUserAllowedBeenSet && mIsUserAllowed) { + if (mIsSharedWithUserSet && mIsSharedWithUser) { throw new IllegalStateException("Open network should not be " - + "setIsUserAllowedToManuallyConnect to true"); + + "setCredentialSharedWithUser to true"); } - mIsUserAllowed = false; + mIsSharedWithUser = false; } } @@ -615,7 +615,7 @@ public final class WifiNetworkSuggestion implements Parcelable { mPasspointConfiguration, mIsAppInteractionRequired, mIsUserInteractionRequired, - mIsUserAllowed); + mIsSharedWithUser); } } diff --git a/wifi/tests/src/android/net/wifi/WifiNetworkSuggestionTest.java b/wifi/tests/src/android/net/wifi/WifiNetworkSuggestionTest.java index 4cdc4bc2ad48..ac915447f3c4 100644 --- a/wifi/tests/src/android/net/wifi/WifiNetworkSuggestionTest.java +++ b/wifi/tests/src/android/net/wifi/WifiNetworkSuggestionTest.java @@ -76,7 +76,7 @@ public class WifiNetworkSuggestionTest { .setSsid(TEST_SSID) .setWpa2Passphrase(TEST_PRESHARED_KEY) .setIsAppInteractionRequired(true) - .setIsUserAllowedToManuallyConnect(false) + .setCredentialSharedWithUser(false) .setPriority(0) .build(); @@ -151,7 +151,7 @@ public class WifiNetworkSuggestionTest { WifiNetworkSuggestion suggestion = new WifiNetworkSuggestion.Builder() .setSsid(TEST_SSID) .setWpa3Passphrase(TEST_PRESHARED_KEY) - .setIsUserAllowedToManuallyConnect(true) + .setCredentialSharedWithUser(true) .build(); assertEquals("\"" + TEST_SSID + "\"", suggestion.wifiConfiguration.SSID); @@ -709,14 +709,14 @@ public class WifiNetworkSuggestionTest { /** * Ensure {@link WifiNetworkSuggestion.Builder#build()} throws an exception - * when {@link WifiNetworkSuggestion.Builder#setIsUserAllowedToManuallyConnect(boolean)} to + * when {@link WifiNetworkSuggestion.Builder#setCredentialSharedWithUser(boolean)} to * true on a open network suggestion. */ @Test(expected = IllegalStateException.class) public void testSetIsUserAllowedToManuallyConnectToWithOpenNetwork() { WifiNetworkSuggestion suggestion = new WifiNetworkSuggestion.Builder() .setSsid(TEST_SSID) - .setIsUserAllowedToManuallyConnect(true) + .setCredentialSharedWithUser(true) .build(); } } |