diff options
143 files changed, 3761 insertions, 3219 deletions
diff --git a/Android.bp b/Android.bp index c3025e8561e2..b4110fa906cf 100644 --- a/Android.bp +++ b/Android.bp @@ -920,6 +920,7 @@ filegroup { "core/java/com/android/internal/util/RingBufferIndices.java", "core/java/com/android/internal/util/State.java", "core/java/com/android/internal/util/StateMachine.java", + "core/java/com/android/internal/util/TrafficStatsConstants.java", "core/java/com/android/internal/util/WakeupMessage.java", "core/java/android/net/shared/*.java", ] diff --git a/api/current.txt b/api/current.txt index 8ec7594773af..f602c9ad113d 100644 --- a/api/current.txt +++ b/api/current.txt @@ -51509,6 +51509,7 @@ package android.view { method public android.transition.Transition getSharedElementReturnTransition(); method public boolean getSharedElementsUseOverlay(); method @ColorInt public abstract int getStatusBarColor(); + method @NonNull public java.util.List<android.graphics.Rect> getSystemGestureExclusionRects(); method public long getTransitionBackgroundFadeDuration(); method public android.transition.TransitionManager getTransitionManager(); method public abstract int getVolumeControlStream(); @@ -51587,6 +51588,7 @@ package android.view { method public void setSoftInputMode(int); method public abstract void setStatusBarColor(@ColorInt int); method public void setSustainedPerformanceMode(boolean); + method public void setSystemGestureExclusionRects(@NonNull java.util.List<android.graphics.Rect>); method public abstract void setTitle(CharSequence); method @Deprecated public abstract void setTitleColor(@ColorInt int); method public void setTransitionBackgroundFadeDuration(long); @@ -53614,29 +53616,23 @@ package android.view.textclassifier { method @NonNull public android.view.textclassifier.TextClassifier.EntityConfig.Builder setIncludedTypes(@Nullable java.util.Collection<java.lang.String>); } - public final class TextClassifierEvent implements android.os.Parcelable { + public abstract class TextClassifierEvent implements android.os.Parcelable { method public int describeContents(); method @NonNull public int[] getActionIndices(); method @NonNull public String[] getEntityTypes(); method public int getEventCategory(); method @Nullable public android.view.textclassifier.TextClassificationContext getEventContext(); method public int getEventIndex(); - method public long getEventTime(); method public int getEventType(); method @NonNull public android.os.Bundle getExtras(); - method @Nullable public String getLanguage(); - method public int getRelativeSuggestedWordEndIndex(); - method public int getRelativeSuggestedWordStartIndex(); - method public int getRelativeWordEndIndex(); - method public int getRelativeWordStartIndex(); + method @Nullable public String getModelName(); method @Nullable public String getResultId(); - method public float getScore(); + method @NonNull public float[] getScores(); method public void writeToParcel(android.os.Parcel, int); field public static final int CATEGORY_CONVERSATION_ACTIONS = 3; // 0x3 field public static final int CATEGORY_LANGUAGE_DETECTION = 4; // 0x4 field public static final int CATEGORY_LINKIFY = 2; // 0x2 field public static final int CATEGORY_SELECTION = 1; // 0x1 - field public static final int CATEGORY_UNDEFINED = 0; // 0x0 field @NonNull public static final android.os.Parcelable.Creator<android.view.textclassifier.TextClassifierEvent> CREATOR; field public static final int TYPE_ACTIONS_GENERATED = 20; // 0x14 field public static final int TYPE_ACTIONS_SHOWN = 6; // 0x6 @@ -53658,25 +53654,63 @@ package android.view.textclassifier { field public static final int TYPE_SMART_ACTION = 13; // 0xd field public static final int TYPE_SMART_SELECTION_MULTI = 4; // 0x4 field public static final int TYPE_SMART_SELECTION_SINGLE = 3; // 0x3 - field public static final int TYPE_UNDEFINED = 0; // 0x0 - } - - public static final class TextClassifierEvent.Builder { - ctor public TextClassifierEvent.Builder(int, int); - method @NonNull public android.view.textclassifier.TextClassifierEvent build(); - method @NonNull public android.view.textclassifier.TextClassifierEvent.Builder setActionIndices(@NonNull int...); - method @NonNull public android.view.textclassifier.TextClassifierEvent.Builder setEntityTypes(@NonNull java.lang.String...); - method @NonNull public android.view.textclassifier.TextClassifierEvent.Builder setEventContext(@Nullable android.view.textclassifier.TextClassificationContext); - method @NonNull public android.view.textclassifier.TextClassifierEvent.Builder setEventIndex(int); - method @NonNull public android.view.textclassifier.TextClassifierEvent.Builder setEventTime(long); - method @NonNull public android.view.textclassifier.TextClassifierEvent.Builder setExtras(@NonNull android.os.Bundle); - method @NonNull public android.view.textclassifier.TextClassifierEvent.Builder setLanguage(@Nullable String); - method @NonNull public android.view.textclassifier.TextClassifierEvent.Builder setRelativeSuggestedWordEndIndex(int); - method @NonNull public android.view.textclassifier.TextClassifierEvent.Builder setRelativeSuggestedWordStartIndex(int); - method @NonNull public android.view.textclassifier.TextClassifierEvent.Builder setRelativeWordEndIndex(int); - method @NonNull public android.view.textclassifier.TextClassifierEvent.Builder setRelativeWordStartIndex(int); - method @NonNull public android.view.textclassifier.TextClassifierEvent.Builder setResultId(@Nullable String); - method @NonNull public android.view.textclassifier.TextClassifierEvent.Builder setScore(float); + } + + public abstract static class TextClassifierEvent.Builder<T extends android.view.textclassifier.TextClassifierEvent.Builder<T>> { + method @NonNull public T setActionIndices(@NonNull int...); + method @NonNull public T setEntityTypes(@NonNull java.lang.String...); + method @NonNull public T setEventContext(@Nullable android.view.textclassifier.TextClassificationContext); + method @NonNull public T setEventIndex(int); + method @NonNull public T setExtras(@NonNull android.os.Bundle); + method @NonNull public T setModelName(@Nullable String); + method @NonNull public T setResultId(@Nullable String); + method @NonNull public T setScores(@NonNull float...); + } + + public static final class TextClassifierEvent.ConversationActionsEvent extends android.view.textclassifier.TextClassifierEvent implements android.os.Parcelable { + field @NonNull public static final android.os.Parcelable.Creator<android.view.textclassifier.TextClassifierEvent.ConversationActionsEvent> CREATOR; + } + + public static final class TextClassifierEvent.ConversationActionsEvent.Builder extends android.view.textclassifier.TextClassifierEvent.Builder<android.view.textclassifier.TextClassifierEvent.ConversationActionsEvent.Builder> { + ctor public TextClassifierEvent.ConversationActionsEvent.Builder(int); + method @NonNull public android.view.textclassifier.TextClassifierEvent.ConversationActionsEvent build(); + } + + public static final class TextClassifierEvent.LanguageDetectionEvent extends android.view.textclassifier.TextClassifierEvent implements android.os.Parcelable { + method @Nullable public android.icu.util.ULocale getLocale(); + field @NonNull public static final android.os.Parcelable.Creator<android.view.textclassifier.TextClassifierEvent.LanguageDetectionEvent> CREATOR; + } + + public static final class TextClassifierEvent.LanguageDetectionEvent.Builder extends android.view.textclassifier.TextClassifierEvent.Builder<android.view.textclassifier.TextClassifierEvent.LanguageDetectionEvent.Builder> { + ctor public TextClassifierEvent.LanguageDetectionEvent.Builder(int); + method @NonNull public android.view.textclassifier.TextClassifierEvent.LanguageDetectionEvent build(); + method @NonNull public android.view.textclassifier.TextClassifierEvent.LanguageDetectionEvent.Builder setLocale(@Nullable android.icu.util.ULocale); + } + + public static final class TextClassifierEvent.TextLinkifyEvent extends android.view.textclassifier.TextClassifierEvent implements android.os.Parcelable { + field @NonNull public static final android.os.Parcelable.Creator<android.view.textclassifier.TextClassifierEvent.TextLinkifyEvent> CREATOR; + } + + public static final class TextClassifierEvent.TextLinkifyEvent.Builder extends android.view.textclassifier.TextClassifierEvent.Builder<android.view.textclassifier.TextClassifierEvent.TextLinkifyEvent.Builder> { + ctor public TextClassifierEvent.TextLinkifyEvent.Builder(int); + method @NonNull public android.view.textclassifier.TextClassifierEvent.TextLinkifyEvent build(); + } + + public static final class TextClassifierEvent.TextSelectionEvent extends android.view.textclassifier.TextClassifierEvent implements android.os.Parcelable { + method public int getRelativeSuggestedWordEndIndex(); + method public int getRelativeSuggestedWordStartIndex(); + method public int getRelativeWordEndIndex(); + method public int getRelativeWordStartIndex(); + field @NonNull public static final android.os.Parcelable.Creator<android.view.textclassifier.TextClassifierEvent.TextSelectionEvent> CREATOR; + } + + public static final class TextClassifierEvent.TextSelectionEvent.Builder extends android.view.textclassifier.TextClassifierEvent.Builder<android.view.textclassifier.TextClassifierEvent.TextSelectionEvent.Builder> { + ctor public TextClassifierEvent.TextSelectionEvent.Builder(int); + method @NonNull public android.view.textclassifier.TextClassifierEvent.TextSelectionEvent build(); + method @NonNull public android.view.textclassifier.TextClassifierEvent.TextSelectionEvent.Builder setRelativeSuggestedWordEndIndex(int); + method @NonNull public android.view.textclassifier.TextClassifierEvent.TextSelectionEvent.Builder setRelativeSuggestedWordStartIndex(int); + method @NonNull public android.view.textclassifier.TextClassifierEvent.TextSelectionEvent.Builder setRelativeWordEndIndex(int); + method @NonNull public android.view.textclassifier.TextClassifierEvent.TextSelectionEvent.Builder setRelativeWordStartIndex(int); } public final class TextLanguage implements android.os.Parcelable { diff --git a/api/system-current.txt b/api/system-current.txt index c031429b6d8a..b60e850249ef 100644 --- a/api/system-current.txt +++ b/api/system-current.txt @@ -1092,11 +1092,14 @@ package android.app.prediction { } public static final class AppTarget.Builder { + ctor @Deprecated public AppTarget.Builder(@NonNull android.app.prediction.AppTargetId); ctor public AppTarget.Builder(@NonNull android.app.prediction.AppTargetId, @NonNull String, @NonNull android.os.UserHandle); ctor public AppTarget.Builder(@NonNull android.app.prediction.AppTargetId, @NonNull android.content.pm.ShortcutInfo); method @NonNull public android.app.prediction.AppTarget build(); method @NonNull public android.app.prediction.AppTarget.Builder setClassName(@NonNull String); method @NonNull public android.app.prediction.AppTarget.Builder setRank(@IntRange(from=0) int); + method @Deprecated @NonNull public android.app.prediction.AppTarget.Builder setTarget(@NonNull String, @NonNull android.os.UserHandle); + method @Deprecated @NonNull public android.app.prediction.AppTarget.Builder setTarget(@NonNull android.content.pm.ShortcutInfo); } public final class AppTargetEvent implements android.os.Parcelable { @@ -4225,9 +4228,12 @@ package android.net { method public static void setThreadStatsTagApp(); method public static void setThreadStatsTagBackup(); method public static void setThreadStatsTagRestore(); - field public static final int TAG_SYSTEM_DHCP = -192; // 0xffffff40 - field public static final int TAG_SYSTEM_DHCP_SERVER = -186; // 0xffffff46 - field public static final int TAG_SYSTEM_PROBE = -190; // 0xffffff42 + field public static final int TAG_NETWORK_STACK_IMPERSONATION_RANGE_END = -113; // 0xffffff8f + field public static final int TAG_NETWORK_STACK_IMPERSONATION_RANGE_START = -128; // 0xffffff80 + field public static final int TAG_NETWORK_STACK_RANGE_END = -257; // 0xfffffeff + field public static final int TAG_NETWORK_STACK_RANGE_START = -768; // 0xfffffd00 + field public static final int TAG_SYSTEM_IMPERSONATION_RANGE_END = -241; // 0xffffff0f + field public static final int TAG_SYSTEM_IMPERSONATION_RANGE_START = -256; // 0xffffff00 } public abstract class Uri implements java.lang.Comparable<android.net.Uri> android.os.Parcelable { diff --git a/api/test-current.txt b/api/test-current.txt index 811ad43b5a9e..f76b3830c461 100644 --- a/api/test-current.txt +++ b/api/test-current.txt @@ -503,6 +503,8 @@ package android.app.prediction { method @NonNull public android.app.prediction.AppTarget build(); method @NonNull public android.app.prediction.AppTarget.Builder setClassName(@NonNull String); method @NonNull public android.app.prediction.AppTarget.Builder setRank(@IntRange(from=0) int); + method @Deprecated @NonNull public android.app.prediction.AppTarget.Builder setTarget(@NonNull String, @NonNull android.os.UserHandle); + method @Deprecated @NonNull public android.app.prediction.AppTarget.Builder setTarget(@NonNull android.content.pm.ShortcutInfo); } public final class AppTargetEvent implements android.os.Parcelable { @@ -1104,6 +1106,7 @@ package android.media { method @RequiresPermission("android.permission.MODIFY_AUDIO_ROUTING") public void setFocusRequestResult(@NonNull android.media.AudioFocusInfo, int, @NonNull android.media.audiopolicy.AudioPolicy); method @RequiresPermission("android.permission.MODIFY_AUDIO_ROUTING") public void unregisterAudioPolicy(@NonNull android.media.audiopolicy.AudioPolicy); method @RequiresPermission("android.permission.MODIFY_AUDIO_ROUTING") public void unregisterAudioPolicyAsync(@NonNull android.media.audiopolicy.AudioPolicy); + field public static final int SUCCESS = 0; // 0x0 } public static final class AudioRecord.MetricsConstants { @@ -1129,22 +1132,6 @@ package android.media { field public static final String SAMPLE_RATE = "android.media.audiotrack.sampleRate"; } - public final class BufferingParams implements android.os.Parcelable { - method public int describeContents(); - method public int getInitialMarkMs(); - method public int getResumePlaybackMarkMs(); - method public void writeToParcel(android.os.Parcel, int); - field @NonNull public static final android.os.Parcelable.Creator<android.media.BufferingParams> CREATOR; - } - - public static class BufferingParams.Builder { - ctor public BufferingParams.Builder(); - ctor public BufferingParams.Builder(android.media.BufferingParams); - method public android.media.BufferingParams build(); - method public android.media.BufferingParams.Builder setInitialMarkMs(int); - method public android.media.BufferingParams.Builder setResumePlaybackMarkMs(int); - } - public static final class MediaCodecInfo.VideoCapabilities.PerformancePoint { ctor public MediaCodecInfo.VideoCapabilities.PerformancePoint(int, int, int, int, @NonNull android.util.Size); ctor public MediaCodecInfo.VideoCapabilities.PerformancePoint(@NonNull android.media.MediaCodecInfo.VideoCapabilities.PerformancePoint, @NonNull android.util.Size); @@ -1241,8 +1228,10 @@ package android.media.audiopolicy { method public int detachMixes(@NonNull java.util.List<android.media.audiopolicy.AudioMix>); method public int getFocusDuckingBehavior(); method public int getStatus(); + method public boolean removeUidDeviceAffinity(int); method public int setFocusDuckingBehavior(int) throws java.lang.IllegalArgumentException, java.lang.IllegalStateException; method public void setRegistration(String); + method public boolean setUidDeviceAffinity(int, @NonNull java.util.List<android.media.AudioDeviceInfo>); method public String toLogFriendlyString(); field public static final int FOCUS_POLICY_DUCKING_DEFAULT = 0; // 0x0 field public static final int FOCUS_POLICY_DUCKING_IN_APP = 0; // 0x0 @@ -1459,9 +1448,6 @@ package android.net { method public static long getLoopbackRxPackets(); method public static long getLoopbackTxBytes(); method public static long getLoopbackTxPackets(); - field public static final int TAG_SYSTEM_DHCP = -192; // 0xffffff40 - field public static final int TAG_SYSTEM_DHCP_SERVER = -186; // 0xffffff46 - field public static final int TAG_SYSTEM_PROBE = -190; // 0xffffff42 } } diff --git a/cmds/statsd/src/atoms.proto b/cmds/statsd/src/atoms.proto index 5778fa9f6903..d28108ceeb73 100644 --- a/cmds/statsd/src/atoms.proto +++ b/cmds/statsd/src/atoms.proto @@ -268,7 +268,8 @@ message Atom { TouchGestureClassified touch_gesture_classified = 177; HiddenApiUsed hidden_api_used = 178 [(allow_from_any_uid) = true]; StyleUIChanged style_ui_changed = 179; - PrivacyIndicatorsInteracted privacy_indicators_interacted = 180; + PrivacyIndicatorsInteracted privacy_indicators_interacted = + 180 [(log_from_module) = "permissioncontroller"]; AppInstallOnExternalStorageReported app_install_on_external_storage_reported = 181; NetworkStackReported network_stack_reported = 182 [(log_from_module) = "network_stack"]; AppMovedStorageReported app_moved_storage_reported = 183; diff --git a/core/java/android/app/Activity.java b/core/java/android/app/Activity.java index 5f972c9895b4..6e935e1e56c4 100644 --- a/core/java/android/app/Activity.java +++ b/core/java/android/app/Activity.java @@ -127,7 +127,6 @@ import android.view.autofill.AutofillPopupWindow; import android.view.autofill.IAutofillWindowPresenter; import android.view.contentcapture.ContentCaptureContext; import android.view.contentcapture.ContentCaptureManager; -import android.view.contentcapture.ContentCaptureManager.ContentCaptureClient; import android.widget.AdapterView; import android.widget.Toast; import android.widget.Toolbar; @@ -724,7 +723,7 @@ public class Activity extends ContextThemeWrapper Window.Callback, KeyEvent.Callback, OnCreateContextMenuListener, ComponentCallbacks2, Window.OnWindowDismissedCallback, WindowControllerCallback, - AutofillManager.AutofillClient, ContentCaptureManager.ContentCaptureClient { + AutofillManager.AutofillClient { private static final String TAG = "Activity"; private static final boolean DEBUG_LIFECYCLE = false; @@ -1124,12 +1123,6 @@ public class Activity extends ContextThemeWrapper return this; } - /** @hide */ - @Override - public final ContentCaptureClient getContentCaptureClient() { - return this; - } - /** * Register an {@link Application.ActivityLifecycleCallbacks} instance that receives * lifecycle callbacks for only this Activity. @@ -6509,12 +6502,6 @@ public class Activity extends ContextThemeWrapper return getComponentName(); } - /** @hide */ - @Override - public final ComponentName contentCaptureClientGetComponentName() { - return getComponentName(); - } - /** * Retrieve a {@link SharedPreferences} object for accessing preferences * that are private to this activity. This simply calls the underlying diff --git a/core/java/android/app/ActivityThread.java b/core/java/android/app/ActivityThread.java index 0260faae4567..5e5611bcf058 100644 --- a/core/java/android/app/ActivityThread.java +++ b/core/java/android/app/ActivityThread.java @@ -4549,7 +4549,7 @@ public final class ActivityThread extends ClientTransactionHandler { if (!show && !r.stopped) { performStopActivityInner(r, null /* stopInfo */, show, false /* saveState */, false /* finalStateRequest */, "handleWindowVisibility"); - } else if (show && r.stopped) { + } else if (show && r.getLifecycleState() == ON_STOP) { // If we are getting ready to gc after going to the background, well // we are back active so skip it. unscheduleGcIdler(); diff --git a/core/java/android/app/AppOpsManager.java b/core/java/android/app/AppOpsManager.java index 6f9224481eba..227703d7c027 100644 --- a/core/java/android/app/AppOpsManager.java +++ b/core/java/android/app/AppOpsManager.java @@ -823,9 +823,11 @@ public class AppOpsManager { public static final int OP_LEGACY_STORAGE = 87; /** @hide Accessing accessibility features */ public static final int OP_ACCESS_ACCESSIBILITY = 88; + /** @hide Read the device identifiers (IMEI / MEID, IMSI, SIM / Build serial) */ + public static final int OP_READ_DEVICE_IDENTIFIERS = 89; /** @hide */ @UnsupportedAppUsage - public static final int _NUM_OP = 89; + public static final int _NUM_OP = 90; /** Access to coarse location information. */ public static final String OPSTR_COARSE_LOCATION = "android:coarse_location"; @@ -1099,6 +1101,8 @@ public class AppOpsManager { /** @hide Interact with accessibility. */ @SystemApi public static final String OPSTR_ACCESS_ACCESSIBILITY = "android:access_accessibility"; + /** @hide Read device identifiers */ + public static final String OPSTR_READ_DEVICE_IDENTIFIERS = "android:read_device_identifiers"; // Warning: If an permission is added here it also has to be added to // com.android.packageinstaller.permission.utils.EventLogger @@ -1259,6 +1263,7 @@ public class AppOpsManager { OP_WRITE_MEDIA_IMAGES, // WRITE_MEDIA_IMAGES OP_LEGACY_STORAGE, // LEGACY_STORAGE OP_ACCESS_ACCESSIBILITY, // ACCESS_ACCESSIBILITY + OP_READ_DEVICE_IDENTIFIERS, // READ_DEVICE_IDENTIFIERS }; /** @@ -1354,6 +1359,7 @@ public class AppOpsManager { OPSTR_WRITE_MEDIA_IMAGES, OPSTR_LEGACY_STORAGE, OPSTR_ACCESS_ACCESSIBILITY, + OPSTR_READ_DEVICE_IDENTIFIERS, }; /** @@ -1450,6 +1456,7 @@ public class AppOpsManager { "WRITE_MEDIA_IMAGES", "LEGACY_STORAGE", "ACCESS_ACCESSIBILITY", + "READ_DEVICE_IDENTIFIERS", }; /** @@ -1547,6 +1554,7 @@ public class AppOpsManager { null, // no permission for OP_WRITE_MEDIA_IMAGES null, // no permission for OP_LEGACY_STORAGE null, // no permission for OP_ACCESS_ACCESSIBILITY + null, // no direct permission for OP_READ_DEVICE_IDENTIFIERS }; /** @@ -1644,6 +1652,7 @@ public class AppOpsManager { null, // WRITE_MEDIA_IMAGES null, // LEGACY_STORAGE null, // ACCESS_ACCESSIBILITY + null, // READ_DEVICE_IDENTIFIERS }; /** @@ -1740,6 +1749,7 @@ public class AppOpsManager { false, // WRITE_MEDIA_IMAGES false, // LEGACY_STORAGE false, // ACCESS_ACCESSIBILITY + false, // READ_DEVICE_IDENTIFIERS }; /** @@ -1835,6 +1845,7 @@ public class AppOpsManager { AppOpsManager.MODE_ERRORED, // WRITE_MEDIA_IMAGES AppOpsManager.MODE_DEFAULT, // LEGACY_STORAGE AppOpsManager.MODE_ALLOWED, // ACCESS_ACCESSIBILITY + AppOpsManager.MODE_ERRORED, // READ_DEVICE_IDENTIFIERS }; /** @@ -1934,6 +1945,7 @@ public class AppOpsManager { false, // WRITE_MEDIA_IMAGES false, // LEGACY_STORAGE false, // ACCESS_ACCESSIBILITY + false, // READ_DEVICE_IDENTIFIERS }; /** diff --git a/core/java/android/app/TEST_MAPPING b/core/java/android/app/TEST_MAPPING index 14c58e744dac..def1f457fb4a 100644 --- a/core/java/android/app/TEST_MAPPING +++ b/core/java/android/app/TEST_MAPPING @@ -27,5 +27,15 @@ } ] } + ], + "postsubmit": [ + { + "file_patterns": ["(/|^)ActivityThreadClientTest.java"], + "name": "FrameworksMockingCoreTests" + }, + { + "file_patterns": ["(/|^)ActivityThreadTest.java"], + "name": "FrameworksCoreTests" + } ] } diff --git a/core/java/android/app/prediction/AppTarget.java b/core/java/android/app/prediction/AppTarget.java index 4704661c2b24..61e4569c1228 100644 --- a/core/java/android/app/prediction/AppTarget.java +++ b/core/java/android/app/prediction/AppTarget.java @@ -208,6 +208,7 @@ public final class AppTarget implements Parcelable { * @hide */ @Deprecated + @SystemApi public Builder(@NonNull AppTargetId id) { mId = id; } @@ -243,7 +244,6 @@ public final class AppTarget implements Parcelable { /** * @deprecated Use the appropriate constructor. - * @hide */ @NonNull @Deprecated @@ -258,7 +258,6 @@ public final class AppTarget implements Parcelable { /** * @deprecated Use the appropriate constructor. - * @hide */ @NonNull @Deprecated diff --git a/core/java/android/app/role/RoleControllerManager.java b/core/java/android/app/role/RoleControllerManager.java index 027e152ee95f..394a0d64b3e4 100644 --- a/core/java/android/app/role/RoleControllerManager.java +++ b/core/java/android/app/role/RoleControllerManager.java @@ -52,7 +52,10 @@ public class RoleControllerManager { private static final String LOG_TAG = RoleControllerManager.class.getSimpleName(); + private static volatile ComponentName sRemoteServiceComponentName; + private static final Object sRemoteServicesLock = new Object(); + /** * Global remote services (per user) used by all {@link RoleControllerManager managers}. */ @@ -62,18 +65,36 @@ public class RoleControllerManager { @NonNull private final RemoteService mRemoteService; - public RoleControllerManager(@NonNull Context context, @NonNull Handler handler) { + /** + * Initialize the remote service component name once so that we can avoid acquiring the + * PackageManagerService lock in constructor. + * + * @see #createWithInitializedRemoteServiceComponentName(Handler, Context) + */ + public static void initializeRemoteServiceComponentName(@NonNull Context context) { + sRemoteServiceComponentName = getRemoteServiceComponentName(context); + } + + /** + * Create a {@link RoleControllerManager} instance with the initialized remote service component + * name so that we can avoid acquiring the PackageManagerService lock in constructor. + * + * @see #initializeRemoteServiceComponentName(Context) + */ + @NonNull + public static RoleControllerManager createWithInitializedRemoteServiceComponentName( + @NonNull Handler handler, @NonNull Context context) { + return new RoleControllerManager(sRemoteServiceComponentName, handler, context); + } + + private RoleControllerManager(@NonNull ComponentName remoteServiceComponentName, + @NonNull Handler handler, @NonNull Context context) { synchronized (sRemoteServicesLock) { int userId = context.getUserId(); RemoteService remoteService = sRemoteServices.get(userId); if (remoteService == null) { - Intent intent = new Intent(RoleControllerService.SERVICE_INTERFACE); - PackageManager packageManager = context.getPackageManager(); - intent.setPackage(packageManager.getPermissionControllerPackageName()); - ResolveInfo resolveInfo = packageManager.resolveService(intent, 0); - remoteService = new RemoteService(context.getApplicationContext(), - resolveInfo.getComponentInfo().getComponentName(), handler, userId); + remoteServiceComponentName, handler, userId); sRemoteServices.put(userId, remoteService); } mRemoteService = remoteService; @@ -81,7 +102,16 @@ public class RoleControllerManager { } public RoleControllerManager(@NonNull Context context) { - this(context, context.getMainThreadHandler()); + this(getRemoteServiceComponentName(context), context.getMainThreadHandler(), context); + } + + @NonNull + private static ComponentName getRemoteServiceComponentName(@NonNull Context context) { + Intent intent = new Intent(RoleControllerService.SERVICE_INTERFACE); + PackageManager packageManager = context.getPackageManager(); + intent.setPackage(packageManager.getPermissionControllerPackageName()); + ResolveInfo resolveInfo = packageManager.resolveService(intent, 0); + return resolveInfo.getComponentInfo().getComponentName(); } /** diff --git a/core/java/android/content/AutofillOptions.java b/core/java/android/content/AutofillOptions.java index f59bc9891c86..8fb9501b3319 100644 --- a/core/java/android/content/AutofillOptions.java +++ b/core/java/android/content/AutofillOptions.java @@ -24,7 +24,7 @@ import android.os.Parcelable; import android.util.ArraySet; import android.util.Log; import android.view.autofill.AutofillManager; -import android.view.contentcapture.ContentCaptureManager.ContentCaptureClient; +import android.view.autofill.AutofillManager.AutofillClient; import java.io.PrintWriter; @@ -73,10 +73,10 @@ public final class AutofillOptions implements Parcelable { public boolean isAugmentedAutofillEnabled(@NonNull Context context) { if (!augmentedAutofillEnabled) return false; - final ContentCaptureClient contentCaptureClient = context.getContentCaptureClient(); - if (contentCaptureClient == null) return false; + final AutofillClient autofillClient = context.getAutofillClient(); + if (autofillClient == null) return false; - final ComponentName component = contentCaptureClient.contentCaptureClientGetComponentName(); + final ComponentName component = autofillClient.autofillClientGetComponentName(); return whitelistedActivitiesForAugmentedAutofill == null || whitelistedActivitiesForAugmentedAutofill.contains(component); } diff --git a/core/java/android/content/ContentProviderOperation.java b/core/java/android/content/ContentProviderOperation.java index a646e49f4f61..c201e4d2a129 100644 --- a/core/java/android/content/ContentProviderOperation.java +++ b/core/java/android/content/ContentProviderOperation.java @@ -283,14 +283,6 @@ public class ContentProviderOperation implements Parcelable { return mType == TYPE_ASSERT; } - private ContentProviderResult fail(String msg) throws OperationApplicationException { - if (mFailureAllowed) { - return new ContentProviderResult(msg); - } else { - throw new OperationApplicationException(msg); - } - } - /** * Applies this operation using the given provider. The backRefs array is used to resolve any * back references that were requested using @@ -307,20 +299,35 @@ public class ContentProviderOperation implements Parcelable { */ public ContentProviderResult apply(ContentProvider provider, ContentProviderResult[] backRefs, int numBackRefs) throws OperationApplicationException { + if (mFailureAllowed) { + try { + return applyInternal(provider, backRefs, numBackRefs); + } catch (Exception e) { + return new ContentProviderResult(e.getMessage()); + } + } else { + return applyInternal(provider, backRefs, numBackRefs); + } + } + + private ContentProviderResult applyInternal(ContentProvider provider, + ContentProviderResult[] backRefs, int numBackRefs) + throws OperationApplicationException { ContentValues values = resolveValueBackReferences(backRefs, numBackRefs); String[] selectionArgs = resolveSelectionArgsBackReferences(backRefs, numBackRefs); if (mType == TYPE_INSERT) { - Uri newUri = provider.insert(mUri, values); - if (newUri == null) { - Log.e(TAG, this.toString()); - return fail("Insert into " + mUri + " returned no result"); + final Uri newUri = provider.insert(mUri, values); + if (newUri != null) { + return new ContentProviderResult(newUri); + } else { + throw new OperationApplicationException( + "Insert into " + mUri + " returned no result"); } - return new ContentProviderResult(newUri); } - int numRows; + final int numRows; if (mType == TYPE_DELETE) { numRows = provider.delete(mUri, mSelection, selectionArgs); } else if (mType == TYPE_UPDATE) { @@ -346,8 +353,7 @@ public class ContentProviderOperation implements Parcelable { final String expectedValue = values.getAsString(projection[i]); if (!TextUtils.equals(cursorValue, expectedValue)) { // Throw exception when expected values don't match - Log.e(TAG, this.toString()); - return fail("Found value " + cursorValue + throw new OperationApplicationException("Found value " + cursorValue + " when expected " + expectedValue + " for column " + projection[i]); } @@ -358,13 +364,12 @@ public class ContentProviderOperation implements Parcelable { cursor.close(); } } else { - Log.e(TAG, this.toString()); throw new IllegalStateException("bad type, " + mType); } if (mExpectedCount != null && mExpectedCount != numRows) { - Log.e(TAG, this.toString()); - return fail("Expected " + mExpectedCount + " rows but actual " + numRows); + throw new OperationApplicationException( + "Expected " + mExpectedCount + " rows but actual " + numRows); } return new ContentProviderResult(numRows); diff --git a/core/java/android/content/Context.java b/core/java/android/content/Context.java index 5a844aab02f0..0ba457e65c67 100644 --- a/core/java/android/content/Context.java +++ b/core/java/android/content/Context.java @@ -70,7 +70,6 @@ import android.view.View; import android.view.ViewDebug; import android.view.WindowManager; import android.view.autofill.AutofillManager.AutofillClient; -import android.view.contentcapture.ContentCaptureManager.ContentCaptureClient; import android.view.textclassifier.TextClassificationManager; import java.io.File; @@ -5414,14 +5413,6 @@ public abstract class Context { /** * @hide */ - @Nullable - public ContentCaptureClient getContentCaptureClient() { - return null; - } - - /** - * @hide - */ public final boolean isAutofillCompatibilityEnabled() { final AutofillOptions options = getAutofillOptions(); return options != null && options.compatModeEnabled; diff --git a/core/java/android/net/DnsResolver.java b/core/java/android/net/DnsResolver.java index b6c4fe2de4f4..68826cbeb845 100644 --- a/core/java/android/net/DnsResolver.java +++ b/core/java/android/net/DnsResolver.java @@ -197,7 +197,7 @@ public final class DnsResolver { final FileDescriptor queryfd; try { queryfd = resNetworkSend((network != null - ? network.netId : NETID_UNSET), query, query.length, flags); + ? network.getNetIdForResolv() : NETID_UNSET), query, query.length, flags); } catch (ErrnoException e) { executor.execute(() -> callback.onError(new DnsException(ERROR_SYSTEM, e))); return; @@ -238,7 +238,7 @@ public final class DnsResolver { final FileDescriptor queryfd; try { queryfd = resNetworkQuery((network != null - ? network.netId : NETID_UNSET), domain, nsClass, nsType, flags); + ? network.getNetIdForResolv() : NETID_UNSET), domain, nsClass, nsType, flags); } catch (ErrnoException e) { executor.execute(() -> callback.onError(new DnsException(ERROR_SYSTEM, e))); return; @@ -346,7 +346,8 @@ public final class DnsResolver { if (queryIpv6) { try { v6fd = resNetworkQuery((network != null - ? network.netId : NETID_UNSET), domain, CLASS_IN, TYPE_AAAA, flags); + ? network.getNetIdForResolv() : NETID_UNSET), + domain, CLASS_IN, TYPE_AAAA, flags); } catch (ErrnoException e) { executor.execute(() -> callback.onError(new DnsException(ERROR_SYSTEM, e))); return; @@ -365,7 +366,8 @@ public final class DnsResolver { if (queryIpv4) { try { v4fd = resNetworkQuery((network != null - ? network.netId : NETID_UNSET), domain, CLASS_IN, TYPE_A, flags); + ? network.getNetIdForResolv() : NETID_UNSET), + domain, CLASS_IN, TYPE_A, flags); } catch (ErrnoException e) { if (queryIpv6) resNetworkCancel(v6fd); // Closes fd, marks it invalid. executor.execute(() -> callback.onError(new DnsException(ERROR_SYSTEM, e))); @@ -423,7 +425,7 @@ public final class DnsResolver { final FileDescriptor queryfd; try { queryfd = resNetworkQuery((network != null - ? network.netId : NETID_UNSET), domain, CLASS_IN, nsType, flags); + ? network.getNetIdForResolv() : NETID_UNSET), domain, CLASS_IN, nsType, flags); } catch (ErrnoException e) { executor.execute(() -> callback.onError(new DnsException(ERROR_SYSTEM, e))); return; diff --git a/core/java/android/net/SntpClient.java b/core/java/android/net/SntpClient.java index b8d7cf167ca8..a55d9d00414f 100644 --- a/core/java/android/net/SntpClient.java +++ b/core/java/android/net/SntpClient.java @@ -20,6 +20,8 @@ import android.annotation.UnsupportedAppUsage; import android.os.SystemClock; import android.util.Log; +import com.android.internal.util.TrafficStatsConstants; + import java.net.DatagramPacket; import java.net.DatagramSocket; import java.net.InetAddress; @@ -99,7 +101,8 @@ public class SntpClient { public boolean requestTime(InetAddress address, int port, int timeout, Network network) { DatagramSocket socket = null; - final int oldTag = TrafficStats.getAndSetThreadStatsTag(TrafficStats.TAG_SYSTEM_NTP); + final int oldTag = TrafficStats.getAndSetThreadStatsTag( + TrafficStatsConstants.TAG_SYSTEM_NTP); try { socket = new DatagramSocket(); network.bindSocket(socket); diff --git a/core/java/android/net/TrafficStats.java b/core/java/android/net/TrafficStats.java index 49c6f74b1a34..4332d8abbce3 100644 --- a/core/java/android/net/TrafficStats.java +++ b/core/java/android/net/TrafficStats.java @@ -90,6 +90,42 @@ public class TrafficStats { public static final int UID_TETHERING = -5; /** + * Tag values in this range are reserved for the network stack. The network stack is + * running as UID {@link android.os.Process.NETWORK_STACK_UID} when in the mainline + * module separate process, and as the system UID otherwise. + */ + /** @hide */ + @SystemApi + public static final int TAG_NETWORK_STACK_RANGE_START = 0xFFFFFD00; + /** @hide */ + @SystemApi + public static final int TAG_NETWORK_STACK_RANGE_END = 0xFFFFFEFF; + + /** + * Tags between 0xFFFFFF00 and 0xFFFFFFFF are reserved and used internally by system services + * like DownloadManager when performing traffic on behalf of an application. + */ + // Please note there is no enforcement of these constants, so do not rely on them to + // determine that the caller is a system caller. + /** @hide */ + @SystemApi + public static final int TAG_SYSTEM_IMPERSONATION_RANGE_START = 0xFFFFFF00; + /** @hide */ + @SystemApi + public static final int TAG_SYSTEM_IMPERSONATION_RANGE_END = 0xFFFFFF0F; + + /** + * Tag values between these ranges are reserved for the network stack to do traffic + * on behalf of applications. It is a subrange of the range above. + */ + /** @hide */ + @SystemApi + public static final int TAG_NETWORK_STACK_IMPERSONATION_RANGE_START = 0xFFFFFF80; + /** @hide */ + @SystemApi + public static final int TAG_NETWORK_STACK_IMPERSONATION_RANGE_END = 0xFFFFFF8F; + + /** * Default tag value for {@link DownloadManager} traffic. * * @hide @@ -127,26 +163,9 @@ public class TrafficStats { */ public static final int TAG_SYSTEM_APP = 0xFFFFFF05; + // TODO : remove this constant when Wifi code is updated /** @hide */ - @SystemApi - @TestApi - public static final int TAG_SYSTEM_DHCP = 0xFFFFFF40; - /** @hide */ - public static final int TAG_SYSTEM_NTP = 0xFFFFFF41; - /** @hide */ - @SystemApi - @TestApi public static final int TAG_SYSTEM_PROBE = 0xFFFFFF42; - /** @hide */ - public static final int TAG_SYSTEM_NEIGHBOR = 0xFFFFFF43; - /** @hide */ - public static final int TAG_SYSTEM_GPS = 0xFFFFFF44; - /** @hide */ - public static final int TAG_SYSTEM_PAC = 0xFFFFFF45; - /** @hide */ - @SystemApi - @TestApi - public static final int TAG_SYSTEM_DHCP_SERVER = 0xFFFFFF46; private static INetworkStatsService sStatsService; diff --git a/core/java/android/service/contentcapture/ContentCaptureService.java b/core/java/android/service/contentcapture/ContentCaptureService.java index d8614a9d66af..02ce87324a4f 100644 --- a/core/java/android/service/contentcapture/ContentCaptureService.java +++ b/core/java/android/service/contentcapture/ContentCaptureService.java @@ -86,11 +86,28 @@ public abstract class ContentCaptureService extends Service { * <code><{@link * android.R.styleable#ContentCaptureService content-capture-service}></code> tag. * - * <p>This is a a sample XML file configuring a ContentCaptureService: - * <pre> <content-capture-service - * android:settingsActivity="foo.bar.SettingsActivity" - * . . . - * /></pre> + * <p>Here's an example of how to use it on {@code AndroidManifest.xml}: + * + * <pre> + * <service android:name=".MyContentCaptureService" + * android:permission="android.permission.BIND_CONTENT_CAPTURE_SERVICE"> + * <intent-filter> + * <action android:name="android.service.contentcapture.ContentCaptureService" /> + * </intent-filter> + * + * <meta-data + * android:name="android.content_capture" + * android:resource="@xml/my_content_capture_service"/> + * </service> + * </pre> + * + * <p>And then on {@code res/xml/my_content_capture_service.xml}: + * + * <pre> + * <content-capture-service xmlns:android="http://schemas.android.com/apk/res/android" + * android:settingsActivity="my.package.MySettingsActivity"> + * </content-capture-service> + * </pre> */ public static final String SERVICE_META_DATA = "android.content_capture"; diff --git a/core/java/android/util/FeatureFlagUtils.java b/core/java/android/util/FeatureFlagUtils.java index feff9db1a63e..f2aaeaddef97 100644 --- a/core/java/android/util/FeatureFlagUtils.java +++ b/core/java/android/util/FeatureFlagUtils.java @@ -63,7 +63,6 @@ public class FeatureFlagUtils { DEFAULT_FLAGS.put(FORCE_GLOBAL_ACTIONS_GRID_ENABLED, "false"); DEFAULT_FLAGS.put(GLOBAL_ACTIONS_PANEL_ENABLED, "true"); DEFAULT_FLAGS.put(PIXEL_WALLPAPER_CATEGORY_SWITCH, "false"); - DEFAULT_FLAGS.put("settings_wifi_details_saved_screen", "true"); DEFAULT_FLAGS.put("settings_wifi_details_datausage_header", "false"); } diff --git a/core/java/android/view/GestureExclusionTracker.java b/core/java/android/view/GestureExclusionTracker.java index 8eccc04fa647..6fcdd714f827 100644 --- a/core/java/android/view/GestureExclusionTracker.java +++ b/core/java/android/view/GestureExclusionTracker.java @@ -20,6 +20,8 @@ import android.annotation.NonNull; import android.annotation.Nullable; import android.graphics.Rect; +import com.android.internal.util.Preconditions; + import java.lang.ref.WeakReference; import java.util.ArrayList; import java.util.Collections; @@ -31,6 +33,8 @@ import java.util.List; */ class GestureExclusionTracker { private boolean mGestureExclusionViewsChanged = false; + private boolean mRootGestureExclusionRectsChanged = false; + private List<Rect> mRootGestureExclusionRects = Collections.emptyList(); private List<GestureExclusionViewInfo> mGestureExclusionViewInfos = new ArrayList<>(); private List<Rect> mGestureExclusionRects = Collections.emptyList(); @@ -59,9 +63,9 @@ class GestureExclusionTracker { @Nullable public List<Rect> computeChangedRects() { - boolean changed = false; + boolean changed = mRootGestureExclusionRectsChanged; final Iterator<GestureExclusionViewInfo> i = mGestureExclusionViewInfos.iterator(); - final List<Rect> rects = new ArrayList<>(); + final List<Rect> rects = new ArrayList<>(mRootGestureExclusionRects); while (i.hasNext()) { final GestureExclusionViewInfo info = i.next(); switch (info.update()) { @@ -79,6 +83,7 @@ class GestureExclusionTracker { } if (changed || mGestureExclusionViewsChanged) { mGestureExclusionViewsChanged = false; + mRootGestureExclusionRectsChanged = false; if (!mGestureExclusionRects.equals(rects)) { mGestureExclusionRects = rects; return rects; @@ -87,6 +92,17 @@ class GestureExclusionTracker { return null; } + public void setRootSystemGestureExclusionRects(@NonNull List<Rect> rects) { + Preconditions.checkNotNull(rects, "rects must not be null"); + mRootGestureExclusionRects = rects; + mRootGestureExclusionRectsChanged = true; + } + + @NonNull + public List<Rect> getRootSystemGestureExclusionRects() { + return mRootGestureExclusionRects; + } + private static class GestureExclusionViewInfo { public static final int CHANGED = 0; public static final int UNCHANGED = 1; diff --git a/core/java/android/view/ImeInsetsSourceConsumer.java b/core/java/android/view/ImeInsetsSourceConsumer.java index 2ba1e016e03d..d415387808dd 100644 --- a/core/java/android/view/ImeInsetsSourceConsumer.java +++ b/core/java/android/view/ImeInsetsSourceConsumer.java @@ -85,6 +85,7 @@ public final class ImeInsetsSourceConsumer extends InsetsSourceConsumer { @Override public void onWindowFocusLost() { mHasWindowFocus = false; + getImm().unregisterImeConsumer(this); } /** diff --git a/core/java/android/view/View.java b/core/java/android/view/View.java index c3a94655d7d5..096c988c8cf1 100644 --- a/core/java/android/view/View.java +++ b/core/java/android/view/View.java @@ -11648,14 +11648,11 @@ public class View implements Drawable.Callback, KeyEvent.Callback, } /** - * When screen readers (one type of accessibility tool) decide what should be read to the - * user, they typically look for input focusable ({@link #isFocusable()}) parents of - * non-focusable text items, and read those focusable parents and their non-focusable children - * as a unit. In some situations, this behavior is desirable for views that should not take - * input focus. Setting an item to be screen reader focusable requests that the view be - * treated as a unit by screen readers without any effect on input focusability. The default - * value of {@code false} lets screen readers use other signals, like focusable, to determine - * how to group items. + * Sets whether this View should be a focusable element for screen readers + * and include non-focusable Views from its subtree when providing feedback. + * <p> + * Note: this is similar to using <a href="#attr_android:focusable">{@code android:focusable}, + * but does not impact input focus behavior. * * @param screenReaderFocusable Whether the view should be treated as a unit by screen reader * accessibility tools. diff --git a/core/java/android/view/ViewRootImpl.java b/core/java/android/view/ViewRootImpl.java index 5ca70ba9a575..7ad118e760d8 100644 --- a/core/java/android/view/ViewRootImpl.java +++ b/core/java/android/view/ViewRootImpl.java @@ -3829,6 +3829,24 @@ public final class ViewRootImpl implements ViewParent, } /** + * Set the root-level system gesture exclusion rects. These are added to those provided by + * the root's view hierarchy. + */ + public void setRootSystemGestureExclusionRects(@NonNull List<Rect> rects) { + mGestureExclusionTracker.setRootSystemGestureExclusionRects(rects); + mHandler.sendEmptyMessage(MSG_SYSTEM_GESTURE_EXCLUSION_CHANGED); + } + + /** + * Returns the root-level system gesture exclusion rects. These do not include those provided by + * the root's view hierarchy. + */ + @NonNull + public List<Rect> getRootSystemGestureExclusionRects() { + return mGestureExclusionTracker.getRootSystemGestureExclusionRects(); + } + + /** * Requests that the root render node is invalidated next time we perform a draw, such that * {@link WindowCallbacks#onPostDraw} gets called. */ diff --git a/core/java/android/view/Window.java b/core/java/android/view/Window.java index fc9d8c269538..b0ec6210d828 100644 --- a/core/java/android/view/Window.java +++ b/core/java/android/view/Window.java @@ -48,6 +48,7 @@ import android.transition.Transition; import android.transition.TransitionManager; import android.view.accessibility.AccessibilityEvent; +import java.util.Collections; import java.util.List; /** @@ -2397,6 +2398,53 @@ public abstract class Window { return false; } + /** + * Sets a list of areas within this window's coordinate space where the system should not + * intercept touch or other pointing device gestures. + * + * <p>This method should be used by apps that make use of + * {@link #takeSurface(SurfaceHolder.Callback2)} and do not have a view hierarchy available. + * Apps that do have a view hierarchy should use + * {@link View#setSystemGestureExclusionRects(List)} instead. This method does not modify or + * replace the gesture exclusion rects populated by individual views in this window's view + * hierarchy using {@link View#setSystemGestureExclusionRects(List)}.</p> + * + * <p>Use this to tell the system which specific sub-areas of a view need to receive gesture + * input in order to function correctly in the presence of global system gestures that may + * conflict. For example, if the system wishes to capture swipe-in-from-screen-edge gestures + * to provide system-level navigation functionality, a view such as a navigation drawer + * container can mark the left (or starting) edge of itself as requiring gesture capture + * priority using this API. The system may then choose to relax its own gesture recognition + * to allow the app to consume the user's gesture. It is not necessary for an app to register + * exclusion rects for broadly spanning regions such as the entirety of a + * <code>ScrollView</code> or for simple press and release click targets such as + * <code>Button</code>. Mark an exclusion rect when interacting with a view requires + * a precision touch gesture in a small area in either the X or Y dimension, such as + * an edge swipe or dragging a <code>SeekBar</code> thumb.</p> + * + * <p>Do not modify the provided list after this method is called.</p> + * + * @param rects A list of precision gesture regions that this window needs to function correctly + */ + @SuppressWarnings("unused") + public void setSystemGestureExclusionRects(@NonNull List<Rect> rects) { + throw new UnsupportedOperationException("window does not support gesture exclusion rects"); + } + + /** + * Retrieve the list of areas within this window's coordinate space where the system should not + * intercept touch or other pointing device gestures. This is the list as set by + * {@link #setSystemGestureExclusionRects(List)} or an empty list if + * {@link #setSystemGestureExclusionRects(List)} has not been called. It does not include + * exclusion rects set by this window's view hierarchy. + * + * @return a list of system gesture exclusion rects specific to this window + */ + @NonNull + public List<Rect> getSystemGestureExclusionRects() { + return Collections.emptyList(); + } + /** @hide */ public void setTheme(int resId) { } diff --git a/core/java/android/view/autofill/AutofillManagerInternal.java b/core/java/android/view/autofill/AutofillManagerInternal.java index 3de1a03d98e5..a07d46d77258 100644 --- a/core/java/android/view/autofill/AutofillManagerInternal.java +++ b/core/java/android/view/autofill/AutofillManagerInternal.java @@ -45,4 +45,12 @@ public abstract class AutofillManagerInternal { @Nullable public abstract AutofillOptions getAutofillOptions(@NonNull String packageName, long versionCode, @UserIdInt int userId); + + /** + * Checks whether the given {@code uid} owns the + * {@link android.service.autofill.augmented.AugmentedAutofillService} implementation associated + * with the given {@code userId}. + */ + public abstract boolean isAugmentedAutofillServiceForUser(@NonNull int callingUid, + @UserIdInt int userId); } diff --git a/core/java/android/view/contentcapture/ContentCaptureCondition.java b/core/java/android/view/contentcapture/ContentCaptureCondition.java index 6f9d4d30909f..54ebf55a0063 100644 --- a/core/java/android/view/contentcapture/ContentCaptureCondition.java +++ b/core/java/android/view/contentcapture/ContentCaptureCondition.java @@ -35,7 +35,8 @@ import java.lang.annotation.RetentionPolicy; public final class ContentCaptureCondition implements Parcelable { /** - * When set, package should use the {@link LocusId#getId()} as a regular expression. + * When set, package should use the {@link LocusId#getId()} as a regular expression (using the + * {@link java.util.regex.Pattern} format). */ public static final int FLAG_IS_REGEX = 0x2; diff --git a/core/java/android/view/contentcapture/ContentCaptureManager.java b/core/java/android/view/contentcapture/ContentCaptureManager.java index 7a6e2adfcaa4..26454c055932 100644 --- a/core/java/android/view/contentcapture/ContentCaptureManager.java +++ b/core/java/android/view/contentcapture/ContentCaptureManager.java @@ -343,15 +343,6 @@ public final class ContentCaptureManager { private MainContentCaptureSession mMainSession; /** @hide */ - public interface ContentCaptureClient { - /** - * Gets the component name of the client. - */ - @NonNull - ComponentName contentCaptureClientGetComponentName(); - } - - /** @hide */ public ContentCaptureManager(@NonNull Context context, @NonNull IContentCaptureManager service, @NonNull ContentCaptureOptions options) { mContext = Preconditions.checkNotNull(context, "context cannot be null"); diff --git a/core/java/android/view/inputmethod/InputMethodManager.java b/core/java/android/view/inputmethod/InputMethodManager.java index 5e00425407ba..fd73856bf79b 100644 --- a/core/java/android/view/inputmethod/InputMethodManager.java +++ b/core/java/android/view/inputmethod/InputMethodManager.java @@ -1951,6 +1951,23 @@ public final class InputMethodManager { } /** + * Unregister for IME state callbacks and applying visibility in + * {@link android.view.ImeInsetsSourceConsumer}. + * @hide + */ + public void unregisterImeConsumer(@NonNull ImeInsetsSourceConsumer imeInsetsConsumer) { + if (imeInsetsConsumer == null) { + throw new IllegalStateException("ImeInsetsSourceConsumer cannot be null."); + } + + synchronized (mH) { + if (mImeInsetsConsumer == imeInsetsConsumer) { + mImeInsetsConsumer = null; + } + } + } + + /** * Call showSoftInput with currently focused view. * @return {@code true} if IME can be shown. * @hide diff --git a/core/java/android/view/textclassifier/ExtrasUtils.java b/core/java/android/view/textclassifier/ExtrasUtils.java index 7b236747bae6..11e0e2ca072c 100644 --- a/core/java/android/view/textclassifier/ExtrasUtils.java +++ b/core/java/android/view/textclassifier/ExtrasUtils.java @@ -36,6 +36,7 @@ import java.util.List; // TODO: Make this a TestApi for CTS testing. public final class ExtrasUtils { + // Keys for response objects. private static final String SERIALIZED_ENTITIES_DATA = "serialized-entities-data"; private static final String ENTITIES_EXTRAS = "entities-extras"; private static final String ACTION_INTENT = "action-intent"; @@ -48,6 +49,10 @@ public final class ExtrasUtils { private static final String TEXT_LANGUAGES = "text-languages"; private static final String ENTITIES = "entities"; + // Keys for request objects. + private static final String IS_SERIALIZED_ENTITY_DATA_ENABLED = + "is-serialized-entity-data-enabled"; + private ExtrasUtils() {} /** @@ -308,7 +313,23 @@ public final class ExtrasUtils { /** * Returns a list of entities contained in the {@code extra}. */ + @Nullable public static List<Bundle> getEntities(Bundle container) { return container.getParcelableArrayList(ENTITIES); } + + /** + * Whether the annotator should populate serialized entity data into the result object. + */ + public static boolean isSerializedEntityDataEnabled(TextLinks.Request request) { + return request.getExtras().getBoolean(IS_SERIALIZED_ENTITY_DATA_ENABLED); + } + + /** + * To indicate whether the annotator should populate serialized entity data in the result + * object. + */ + public static void putIsSerializedEntityDataEnabled(Bundle bundle, boolean isEnabled) { + bundle.putBoolean(IS_SERIALIZED_ENTITY_DATA_ENABLED, isEnabled); + } } diff --git a/core/java/android/view/textclassifier/TextClassifierEvent.java b/core/java/android/view/textclassifier/TextClassifierEvent.java index 236f89bf921a..d3d61a7460c7 100644 --- a/core/java/android/view/textclassifier/TextClassifierEvent.java +++ b/core/java/android/view/textclassifier/TextClassifierEvent.java @@ -19,6 +19,7 @@ package android.view.textclassifier; import android.annotation.IntDef; import android.annotation.NonNull; import android.annotation.Nullable; +import android.icu.util.ULocale; import android.os.Bundle; import android.os.Parcel; import android.os.Parcelable; @@ -30,54 +31,65 @@ import java.lang.annotation.RetentionPolicy; import java.util.Arrays; /** - * A text classifier event. + * This class represents events that are sent by components to the {@link TextClassifier} to report + * something of note that relates to a feature powered by the TextClassifier. The TextClassifier may + * log these events or use them to improve future responses to queries. + * <p> + * Each categories of the events have their own subclass. Events of each types has an associated + * set of related properties. You can find the specification of them in the subclasses. */ -// TODO: Comprehensive javadoc. -public final class TextClassifierEvent implements Parcelable { +public abstract class TextClassifierEvent implements Parcelable { - public static final @android.annotation.NonNull Creator<TextClassifierEvent> CREATOR = new Creator<TextClassifierEvent>() { - @Override - public TextClassifierEvent createFromParcel(Parcel in) { - return readFromParcel(in); - } - - @Override - public TextClassifierEvent[] newArray(int size) { - return new TextClassifierEvent[size]; - } - }; + private static final int PARCEL_TOKEN_TEXT_SELECTION_EVENT = 1; + private static final int PARCEL_TOKEN_TEXT_LINKIFY_EVENT = 2; + private static final int PARCEL_TOKEN_CONVERSATION_ACTION_EVENT = 3; + private static final int PARCEL_TOKEN_LANGUAGE_DETECTION_EVENT = 4; /** @hide **/ @Retention(RetentionPolicy.SOURCE) - @IntDef({CATEGORY_UNDEFINED, CATEGORY_SELECTION, CATEGORY_LINKIFY, + @IntDef({CATEGORY_SELECTION, CATEGORY_LINKIFY, CATEGORY_CONVERSATION_ACTIONS, CATEGORY_LANGUAGE_DETECTION}) public @interface Category { // For custom event categories, use range 1000+. } - /** Undefined category */ - public static final int CATEGORY_UNDEFINED = 0; - /** Smart selection */ + + /** + * Smart selection + * + * @see TextSelectionEvent + */ public static final int CATEGORY_SELECTION = 1; - /** Linkify */ + /** + * Linkify + * + * @see TextLinkifyEvent + */ public static final int CATEGORY_LINKIFY = 2; - /** Conversation actions */ + /** + * Conversation actions + * + * @see ConversationActionsEvent + */ public static final int CATEGORY_CONVERSATION_ACTIONS = 3; - /** Language detection */ + /** + * Language detection + * + * @see LanguageDetectionEvent + */ public static final int CATEGORY_LANGUAGE_DETECTION = 4; /** @hide */ @Retention(RetentionPolicy.SOURCE) - @IntDef({TYPE_UNDEFINED, TYPE_SELECTION_STARTED, TYPE_SELECTION_MODIFIED, - TYPE_SMART_SELECTION_SINGLE, TYPE_SMART_SELECTION_MULTI, TYPE_AUTO_SELECTION, - TYPE_ACTIONS_SHOWN, TYPE_LINK_CLICKED, TYPE_OVERTYPE, TYPE_COPY_ACTION, - TYPE_PASTE_ACTION, TYPE_CUT_ACTION, TYPE_SHARE_ACTION, TYPE_SMART_ACTION, - TYPE_SELECTION_DRAG, TYPE_SELECTION_DESTROYED, TYPE_OTHER_ACTION, TYPE_SELECT_ALL, - TYPE_SELECTION_RESET, TYPE_MANUAL_REPLY, TYPE_ACTIONS_GENERATED}) + @IntDef({TYPE_SELECTION_STARTED, TYPE_SELECTION_MODIFIED, + TYPE_SMART_SELECTION_SINGLE, TYPE_SMART_SELECTION_MULTI, TYPE_AUTO_SELECTION, + TYPE_ACTIONS_SHOWN, TYPE_LINK_CLICKED, TYPE_OVERTYPE, TYPE_COPY_ACTION, + TYPE_PASTE_ACTION, TYPE_CUT_ACTION, TYPE_SHARE_ACTION, TYPE_SMART_ACTION, + TYPE_SELECTION_DRAG, TYPE_SELECTION_DESTROYED, TYPE_OTHER_ACTION, TYPE_SELECT_ALL, + TYPE_SELECTION_RESET, TYPE_MANUAL_REPLY, TYPE_ACTIONS_GENERATED}) public @interface Type { // For custom event types, use range 1,000,000+. } - /** User started a new selection. */ - public static final int TYPE_UNDEFINED = 0; + /** User started a new selection. */ public static final int TYPE_SELECTION_STARTED = 1; /** User modified an existing selection. */ @@ -119,63 +131,49 @@ public final class TextClassifierEvent implements Parcelable { /** TextClassifier generated some actions */ public static final int TYPE_ACTIONS_GENERATED = 20; - @Category private final int mEventCategory; - @Type private final int mEventType; - @Nullable private final String[] mEntityTypes; - @Nullable private final TextClassificationContext mEventContext; - @Nullable private final String mResultId; + @Category + private final int mEventCategory; + @Type + private final int mEventType; + @Nullable + private final String[] mEntityTypes; + @Nullable + private final TextClassificationContext mEventContext; + @Nullable + private final String mResultId; private final int mEventIndex; - private final long mEventTime; + private final float[] mScores; + @Nullable + private final String mModelName; + private final int[] mActionIndices; private final Bundle mExtras; - // Smart selection. - private final int mRelativeWordStartIndex; - private final int mRelativeWordEndIndex; - private final int mRelativeSuggestedWordStartIndex; - private final int mRelativeSuggestedWordEndIndex; - - // Smart action. - private final int[] mActionIndices; + private TextClassifierEvent(Builder builder) { + mEventCategory = builder.mEventCategory; + mEventType = builder.mEventType; + mEntityTypes = builder.mEntityTypes; + mEventContext = builder.mEventContext; + mResultId = builder.mResultId; + mEventIndex = builder.mEventIndex; + mScores = builder.mScores; + mModelName = builder.mModelName; + mActionIndices = builder.mActionIndices; + mExtras = builder.mExtras == null ? Bundle.EMPTY : builder.mExtras; + } - // Language detection. - @Nullable private final String mLanguage; - private final float mScore; - - @Nullable private final String mModelName; - - private TextClassifierEvent( - int eventCategory, - int eventType, - String[] entityTypes, - TextClassificationContext eventContext, - String resultId, - int eventIndex, - long eventTime, - Bundle extras, - int relativeWordStartIndex, - int relativeWordEndIndex, - int relativeSuggestedWordStartIndex, - int relativeSuggestedWordEndIndex, - int[] actionIndex, - String language, - float score, - String modelVersion) { - mEventCategory = eventCategory; - mEventType = eventType; - mEntityTypes = entityTypes; - mEventContext = eventContext; - mResultId = resultId; - mEventIndex = eventIndex; - mEventTime = eventTime; - mExtras = extras; - mRelativeWordStartIndex = relativeWordStartIndex; - mRelativeWordEndIndex = relativeWordEndIndex; - mRelativeSuggestedWordStartIndex = relativeSuggestedWordStartIndex; - mRelativeSuggestedWordEndIndex = relativeSuggestedWordEndIndex; - mActionIndices = actionIndex; - mLanguage = language; - mScore = score; - mModelName = modelVersion; + private TextClassifierEvent(Parcel in) { + mEventCategory = in.readInt(); + mEventType = in.readInt(); + mEntityTypes = in.readStringArray(); + mEventContext = in.readParcelable(null); + mResultId = in.readString(); + mEventIndex = in.readInt(); + int scoresLength = in.readInt(); + mScores = new float[scoresLength]; + in.readFloatArray(mScores); + mModelName = in.readString(); + mActionIndices = in.createIntArray(); + mExtras = in.readBundle(); } @Override @@ -183,44 +181,62 @@ public final class TextClassifierEvent implements Parcelable { return 0; } + @NonNull + public static final Creator<TextClassifierEvent> CREATOR = new Creator<TextClassifierEvent>() { + @Override + public TextClassifierEvent createFromParcel(Parcel in) { + int token = in.readInt(); + if (token == PARCEL_TOKEN_TEXT_SELECTION_EVENT) { + return new TextSelectionEvent(in); + } + if (token == PARCEL_TOKEN_TEXT_LINKIFY_EVENT) { + return new TextLinkifyEvent(in); + } + if (token == PARCEL_TOKEN_LANGUAGE_DETECTION_EVENT) { + return new LanguageDetectionEvent(in); + } + if (token == PARCEL_TOKEN_CONVERSATION_ACTION_EVENT) { + return new ConversationActionsEvent(in); + } + throw new IllegalStateException("Unexpected input event type token in parcel."); + } + + @Override + public TextClassifierEvent[] newArray(int size) { + return new TextClassifierEvent[size]; + } + }; + @Override public void writeToParcel(Parcel dest, int flags) { + dest.writeInt(getParcelToken()); dest.writeInt(mEventCategory); dest.writeInt(mEventType); dest.writeStringArray(mEntityTypes); dest.writeParcelable(mEventContext, flags); dest.writeString(mResultId); dest.writeInt(mEventIndex); - dest.writeLong(mEventTime); - dest.writeBundle(mExtras); - dest.writeInt(mRelativeWordStartIndex); - dest.writeInt(mRelativeWordEndIndex); - dest.writeInt(mRelativeSuggestedWordStartIndex); - dest.writeInt(mRelativeSuggestedWordEndIndex); - dest.writeIntArray(mActionIndices); - dest.writeString(mLanguage); - dest.writeFloat(mScore); + dest.writeInt(mScores.length); + dest.writeFloatArray(mScores); dest.writeString(mModelName); + dest.writeIntArray(mActionIndices); + dest.writeBundle(mExtras); } - private static TextClassifierEvent readFromParcel(Parcel in) { - return new TextClassifierEvent( - /* eventCategory= */ in.readInt(), - /* eventType= */ in.readInt(), - /* entityTypes=*/ in.readStringArray(), - /* eventContext= */ in.readParcelable(null), - /* resultId= */ in.readString(), - /* eventIndex= */ in.readInt(), - /* eventTime= */ in.readLong(), - /* extras= */ in.readBundle(), - /* relativeWordStartIndex= */ in.readInt(), - /* relativeWordEndIndex= */ in.readInt(), - /* relativeSuggestedWordStartIndex= */ in.readInt(), - /* relativeSuggestedWordEndIndex= */ in.readInt(), - /* actionIndices= */ in.createIntArray(), - /* language= */ in.readString(), - /* score= */ in.readFloat(), - /* modelVersion= */ in.readString()); + private int getParcelToken() { + if (this instanceof TextSelectionEvent) { + return PARCEL_TOKEN_TEXT_SELECTION_EVENT; + } + if (this instanceof TextLinkifyEvent) { + return PARCEL_TOKEN_TEXT_LINKIFY_EVENT; + } + if (this instanceof LanguageDetectionEvent) { + return PARCEL_TOKEN_LANGUAGE_DETECTION_EVENT; + } + if (this instanceof ConversationActionsEvent) { + return PARCEL_TOKEN_CONVERSATION_ACTION_EVENT; + } + throw new IllegalArgumentException("Unexpected type: " + this.getClass().getSimpleName()); } /** @@ -241,6 +257,8 @@ public final class TextClassifierEvent implements Parcelable { /** * Returns an array of entity types. e.g. {@link TextClassifier#TYPE_ADDRESS}. + * + * @see Builder#setEntityTypes(String...) for supported types. */ @NonNull public String[] getEntityTypes() { @@ -270,52 +288,20 @@ public final class TextClassifierEvent implements Parcelable { return mEventIndex; } - // TODO: Remove this API. /** - * Returns the time this event occurred. This is the number of milliseconds since - * January 1, 1970, 00:00:00 GMT. 0 indicates not set. - */ - public long getEventTime() { - return mEventTime; - } - - /** - * Returns a bundle containing non-structured extra information about this event. - * - * <p><b>NOTE: </b>Do not modify this bundle. + * Returns the scores of the suggestions. */ @NonNull - public Bundle getExtras() { - return mExtras; - } - - /** - * For smart selection. Returns the relative word index of the start of the selection. - */ - public int getRelativeWordStartIndex() { - return mRelativeWordStartIndex; - } - - /** - * For smart selection. Returns the relative word (exclusive) index of the end of the selection. - */ - public int getRelativeWordEndIndex() { - return mRelativeWordEndIndex; - } - - /** - * For smart selection. Returns the relative word index of the start of the smart selection. - */ - public int getRelativeSuggestedWordStartIndex() { - return mRelativeSuggestedWordStartIndex; + public float[] getScores() { + return mScores; } /** - * For smart selection. Returns the relative word (exclusive) index of the end of the - * smart selection. + * Returns the model name. */ - public int getRelativeSuggestedWordEndIndex() { - return mRelativeSuggestedWordEndIndex; + @Nullable + public String getModelName() { + return mModelName; } /** @@ -323,6 +309,8 @@ public final class TextClassifierEvent implements Parcelable { * Actions are usually returned by the text classifier in priority order with the most * preferred action at index 0. This list gives an indication of the position of the actions * that are being reported. + * + * @see Builder#setActionIndices(int...) */ @NonNull public int[] getActionIndices() { @@ -330,110 +318,158 @@ public final class TextClassifierEvent implements Parcelable { } /** - * For language detection. Returns the language tag for the detected locale. - * @see java.util.Locale#forLanguageTag(String). - */ - @Nullable - public String getLanguage() { - return mLanguage; - } - - /** - * Returns the score of the suggestion. + * Returns a bundle containing non-structured extra information about this event. + * + * <p><b>NOTE: </b>Do not modify this bundle. */ - public float getScore() { - return mScore; + @NonNull + public Bundle getExtras() { + return mExtras; } - /** - * Returns the model name. - * @hide - */ - @Nullable - public String getModelName() { - return mModelName; + @Override + public String toString() { + StringBuilder out = new StringBuilder(128); + out.append(this.getClass().getSimpleName()); + out.append("{"); + out.append("mEventCategory=").append(mEventCategory); + out.append(", mEventTypes=").append(Arrays.toString(mEntityTypes)); + out.append(", mEventContext=").append(mEventContext); + out.append(", mResultId=").append(mResultId); + out.append(", mEventIndex=").append(mEventIndex); + out.append(", mExtras=").append(mExtras); + out.append(", mScores=").append(Arrays.toString(mScores)); + out.append(", mModelName=").append(mModelName); + out.append(", mActionIndices=").append(Arrays.toString(mActionIndices)); + out.append("}"); + return out.toString(); } /** * Builder to build a text classifier event. + * + * @param <T> The subclass to be built. */ - public static final class Builder { + public abstract static class Builder<T extends Builder<T>> { private final int mEventCategory; private final int mEventType; private String[] mEntityTypes = new String[0]; - @Nullable private TextClassificationContext mEventContext; - @Nullable private String mResultId; + @Nullable + private TextClassificationContext mEventContext; + @Nullable + private String mResultId; private int mEventIndex; - private long mEventTime; - @Nullable private Bundle mExtras; - private int mRelativeWordStartIndex; - private int mRelativeWordEndIndex; - private int mRelativeSuggestedWordStartIndex; - private int mRelativeSuggestedWordEndIndex; - private int[] mActionIndices = new int[0]; - @Nullable private String mLanguage; - private float mScore; - + private float[] mScores = new float[0]; + @Nullable private String mModelName; + private int[] mActionIndices = new int[0]; + @Nullable + private Bundle mExtras; /** * Creates a builder for building {@link TextClassifierEvent}s. * * @param eventCategory The event category. e.g. {@link #CATEGORY_SELECTION} - * @param eventType The event type. e.g. {@link #TYPE_SELECTION_STARTED} + * @param eventType The event type. e.g. {@link #TYPE_SELECTION_STARTED} */ - public Builder(@Category int eventCategory, @Type int eventType) { + private Builder(@Category int eventCategory, @Type int eventType) { mEventCategory = eventCategory; mEventType = eventType; } /** * Sets the entity types. e.g. {@link TextClassifier#TYPE_ADDRESS}. + * <p> + * Supported types: + * <p>See {@link TextClassifier.EntityType} + * <p>See {@link ConversationAction.ActionType} + * <p>See {@link ULocale#toLanguageTag()} */ @NonNull - public Builder setEntityTypes(@NonNull String... entityTypes) { + public T setEntityTypes(@NonNull String... entityTypes) { + Preconditions.checkNotNull(entityTypes); mEntityTypes = new String[entityTypes.length]; System.arraycopy(entityTypes, 0, mEntityTypes, 0, entityTypes.length); - return this; + return self(); } /** * Sets the event context. */ @NonNull - public Builder setEventContext(@Nullable TextClassificationContext eventContext) { + public T setEventContext(@Nullable TextClassificationContext eventContext) { mEventContext = eventContext; - return this; + return self(); } /** * Sets the id of the text classifier result related to this event. */ @NonNull - public Builder setResultId(@Nullable String resultId) { + public T setResultId(@Nullable String resultId) { mResultId = resultId; - return this; + return self(); } /** - * Sets the index of this events in the series of events it belongs to. + * Sets the index of this event in the series of events it belongs to. */ @NonNull - public Builder setEventIndex(int eventIndex) { + public T setEventIndex(int eventIndex) { mEventIndex = eventIndex; - return this; + return self(); + } + + /** + * Sets the scores of the suggestions. + */ + @NonNull + public T setScores(@NonNull float... scores) { + Preconditions.checkNotNull(scores); + mScores = new float[scores.length]; + System.arraycopy(scores, 0, mScores, 0, scores.length); + return self(); } - // TODO: Remove this API. /** - * Sets the time this event occurred. This is the number of milliseconds since - * January 1, 1970, 00:00:00 GMT. 0 indicates not set. + * Sets the model name string. */ @NonNull - public Builder setEventTime(long eventTime) { - mEventTime = eventTime; - return this; + public T setModelName(@Nullable String modelVersion) { + mModelName = modelVersion; + return self(); + } + + /** + * Sets the indices of the actions involved in this event. Actions are usually returned by + * the text classifier in priority order with the most preferred action at index 0. + * These indices give an indication of the position of the actions that are being reported. + * <p> + * E.g. + * <pre> + * // 3 smart actions are shown at index 0, 1, 2 respectively in response to a link click. + * new TextClassifierEvent.Builder(CATEGORY_LINKIFY, TYPE_ACTIONS_SHOWN) + * .setEventIndex(0, 1, 2) + * ... + * .build(); + * + * ... + * + * // Smart action at index 1 is activated. + * new TextClassifierEvent.Builder(CATEGORY_LINKIFY, TYPE_SMART_ACTION) + * .setEventIndex(1) + * ... + * .build(); + * </pre> + * + * @see TextClassification#getActions() + */ + @NonNull + public T setActionIndices(@NonNull int... actionIndices) { + mActionIndices = new int[actionIndices.length]; + System.arraycopy(actionIndices, 0, mActionIndices, 0, actionIndices.length); + return self(); } /** @@ -445,136 +481,545 @@ public final class TextClassifierEvent implements Parcelable { * objects in this bundle. */ @NonNull - public Builder setExtras(@NonNull Bundle extras) { + public T setExtras(@NonNull Bundle extras) { mExtras = Preconditions.checkNotNull(extras); - return this; + return self(); + } + + abstract T self(); + } + + /** + * This class represents events that are related to the smart text selection feature. + * <p> + * <pre> + * // User started a selection. e.g. "York" in text "New York City, NY". + * new TextSelectionEvent.Builder(TYPE_SELECTION_STARTED) + * .setEventContext(classificationContext) + * .setEventIndex(0) + * .build(); + * + * // System smart-selects a recognized entity. e.g. "New York City". + * new TextSelectionEvent.Builder(TYPE_SMART_SELECTION_MULTI) + * .setEventContext(classificationContext) + * .setResultId(textSelection.getId()) + * .setRelativeWordStartIndex(-1) // Goes back one word to "New" from "York". + * .setRelativeWordEndIndex(2) // Goes forward 2 words from "York" to start of ",". + * .setEntityTypes(textClassification.getEntity(0)) + * .setScore(textClassification.getConfidenceScore(entityType)) + * .setEventIndex(1) + * .build(); + * + * // User resets the selection to the original selection. i.e. "York". + * new TextSelectionEvent.Builder(TYPE_SELECTION_RESET) + * .setEventContext(classificationContext) + * .setResultId(textSelection.getId()) + * .setRelativeSuggestedWordStartIndex(-1) // Repeated from above. + * .setRelativeSuggestedWordEndIndex(2) // Repeated from above. + * .setRelativeWordStartIndex(0) // Original selection is always at (0, 1]. + * .setRelativeWordEndIndex(1) + * .setEntityTypes(textClassification.getEntity(0)) + * .setScore(textClassification.getConfidenceScore(entityType)) + * .setEventIndex(2) + * .build(); + * + * // User modified the selection. e.g. "New". + * new TextSelectionEvent.Builder(TYPE_SELECTION_MODIFIED) + * .setEventContext(classificationContext) + * .setResultId(textSelection.getId()) + * .setRelativeSuggestedWordStartIndex(-1) // Repeated from above. + * .setRelativeSuggestedWordEndIndex(2) // Repeated from above. + * .setRelativeWordStartIndex(-1) // Goes backward one word from "York" to + * "New". + * .setRelativeWordEndIndex(0) // Goes backward one word to exclude "York". + * .setEntityTypes(textClassification.getEntity(0)) + * .setScore(textClassification.getConfidenceScore(entityType)) + * .setEventIndex(3) + * .build(); + * + * // Smart (contextual) actions (at indices, 0, 1, 2) presented to the user. + * // e.g. "Map", "Ride share", "Explore". + * new TextSelectionEvent.Builder(TYPE_ACTIONS_SHOWN) + * .setEventContext(classificationContext) + * .setResultId(textClassification.getId()) + * .setEntityTypes(textClassification.getEntity(0)) + * .setScore(textClassification.getConfidenceScore(entityType)) + * .setActionIndices(0, 1, 2) + * .setEventIndex(4) + * .build(); + * + * // User chooses the "Copy" action. + * new TextSelectionEvent.Builder(TYPE_COPY_ACTION) + * .setEventContext(classificationContext) + * .setResultId(textClassification.getId()) + * .setEntityTypes(textClassification.getEntity(0)) + * .setScore(textClassification.getConfidenceScore(entityType)) + * .setEventIndex(5) + * .build(); + * + * // User chooses smart action at index 1. i.e. "Ride share". + * new TextSelectionEvent.Builder(TYPE_SMART_ACTION) + * .setEventContext(classificationContext) + * .setResultId(textClassification.getId()) + * .setEntityTypes(textClassification.getEntity(0)) + * .setScore(textClassification.getConfidenceScore(entityType)) + * .setActionIndices(1) + * .setEventIndex(5) + * .build(); + * + * // Selection dismissed. + * new TextSelectionEvent.Builder(TYPE_SELECTION_DESTROYED) + * .setEventContext(classificationContext) + * .setResultId(textClassification.getId()) + * .setEntityTypes(textClassification.getEntity(0)) + * .setScore(textClassification.getConfidenceScore(entityType)) + * .setEventIndex(6) + * .build(); + * </pre> + * <p> + */ + public static final class TextSelectionEvent extends TextClassifierEvent implements Parcelable { + + @NonNull + public static final Creator<TextSelectionEvent> CREATOR = + new Creator<TextSelectionEvent>() { + @Override + public TextSelectionEvent createFromParcel(Parcel in) { + in.readInt(); // skip token, we already know this is a TextSelectionEvent + return new TextSelectionEvent(in); + } + + @Override + public TextSelectionEvent[] newArray(int size) { + return new TextSelectionEvent[size]; + } + }; + + final int mRelativeWordStartIndex; + final int mRelativeWordEndIndex; + final int mRelativeSuggestedWordStartIndex; + final int mRelativeSuggestedWordEndIndex; + + private TextSelectionEvent(TextSelectionEvent.Builder builder) { + super(builder); + mRelativeWordStartIndex = builder.mRelativeWordStartIndex; + mRelativeWordEndIndex = builder.mRelativeWordEndIndex; + mRelativeSuggestedWordStartIndex = builder.mRelativeSuggestedWordStartIndex; + mRelativeSuggestedWordEndIndex = builder.mRelativeSuggestedWordEndIndex; + } + + private TextSelectionEvent(Parcel in) { + super(in); + mRelativeWordStartIndex = in.readInt(); + mRelativeWordEndIndex = in.readInt(); + mRelativeSuggestedWordStartIndex = in.readInt(); + mRelativeSuggestedWordEndIndex = in.readInt(); + } + + @Override + public void writeToParcel(Parcel dest, int flags) { + super.writeToParcel(dest, flags); + dest.writeInt(mRelativeWordStartIndex); + dest.writeInt(mRelativeWordEndIndex); + dest.writeInt(mRelativeSuggestedWordStartIndex); + dest.writeInt(mRelativeSuggestedWordEndIndex); } /** - * For smart selection. Sets the relative word index of the start of the selection. + * Returns the relative word index of the start of the selection. */ - @NonNull - public Builder setRelativeWordStartIndex(int relativeWordStartIndex) { - mRelativeWordStartIndex = relativeWordStartIndex; - return this; + public int getRelativeWordStartIndex() { + return mRelativeWordStartIndex; } /** - * For smart selection. Sets the relative word (exclusive) index of the end of the - * selection. + * Returns the relative word (exclusive) index of the end of the selection. */ - @NonNull - public Builder setRelativeWordEndIndex(int relativeWordEndIndex) { - mRelativeWordEndIndex = relativeWordEndIndex; - return this; + public int getRelativeWordEndIndex() { + return mRelativeWordEndIndex; } /** - * For smart selection. Sets the relative word index of the start of the smart selection. + * Returns the relative word index of the start of the smart selection. */ - @NonNull - public Builder setRelativeSuggestedWordStartIndex(int relativeSuggestedWordStartIndex) { - mRelativeSuggestedWordStartIndex = relativeSuggestedWordStartIndex; - return this; + public int getRelativeSuggestedWordStartIndex() { + return mRelativeSuggestedWordStartIndex; } /** - * For smart selection. Sets the relative word (exclusive) index of the end of the + * Returns the relative word (exclusive) index of the end of the * smart selection. */ - @NonNull - public Builder setRelativeSuggestedWordEndIndex(int relativeSuggestedWordEndIndex) { - mRelativeSuggestedWordEndIndex = relativeSuggestedWordEndIndex; - return this; + public int getRelativeSuggestedWordEndIndex() { + return mRelativeSuggestedWordEndIndex; } /** - * Sets the indices of the actions involved in this event. Actions are usually returned by - * the text classifier in priority order with the most preferred action at index 0. - * This index gives an indication of the position of the action that is being reported. + * Builder class for {@link TextSelectionEvent}. */ + public static final class Builder extends + TextClassifierEvent.Builder<TextSelectionEvent.Builder> { + int mRelativeWordStartIndex; + int mRelativeWordEndIndex; + int mRelativeSuggestedWordStartIndex; + int mRelativeSuggestedWordEndIndex; + + /** + * Creates a builder for building {@link TextSelectionEvent}s. + * + * @param eventType The event type. e.g. {@link #TYPE_SELECTION_STARTED} + */ + public Builder(@Type int eventType) { + super(CATEGORY_SELECTION, eventType); + } + + /** + * Sets the relative word index of the start of the selection. + */ + @NonNull + public Builder setRelativeWordStartIndex(int relativeWordStartIndex) { + mRelativeWordStartIndex = relativeWordStartIndex; + return this; + } + + /** + * Sets the relative word (exclusive) index of the end of the + * selection. + */ + @NonNull + public Builder setRelativeWordEndIndex(int relativeWordEndIndex) { + mRelativeWordEndIndex = relativeWordEndIndex; + return this; + } + + /** + * Sets the relative word index of the start of the smart + * selection. + */ + @NonNull + public Builder setRelativeSuggestedWordStartIndex(int relativeSuggestedWordStartIndex) { + mRelativeSuggestedWordStartIndex = relativeSuggestedWordStartIndex; + return this; + } + + /** + * Sets the relative word (exclusive) index of the end of the + * smart selection. + */ + @NonNull + public Builder setRelativeSuggestedWordEndIndex(int relativeSuggestedWordEndIndex) { + mRelativeSuggestedWordEndIndex = relativeSuggestedWordEndIndex; + return this; + } + + @Override + TextSelectionEvent.Builder self() { + return this; + } + + /** + * Builds and returns a {@link TextSelectionEvent}. + */ + @NonNull + public TextSelectionEvent build() { + return new TextSelectionEvent(this); + } + } + } + + /** + * This class represents events that are related to the smart linkify feature. + * <p> + * <pre> + * // User clicked on a link. + * new TextLinkifyEvent.Builder(TYPE_LINK_CLICKED) + * .setEventContext(classificationContext) + * .setResultId(textClassification.getId()) + * .setEntityTypes(textClassification.getEntity(0)) + * .setScore(textClassification.getConfidenceScore(entityType)) + * .setEventIndex(0) + * .build(); + * + * // Smart (contextual) actions presented to the user in response to a link click. + * new TextLinkifyEvent.Builder(TYPE_ACTIONS_SHOWN) + * .setEventContext(classificationContext) + * .setResultId(textClassification.getId()) + * .setEntityTypes(textClassification.getEntity(0)) + * .setScore(textClassification.getConfidenceScore(entityType)) + * .setActionIndices(range(textClassification.getActions().size())) + * .setEventIndex(1) + * .build(); + * + * // User chooses smart action at index 0. + * new TextLinkifyEvent.Builder(TYPE_SMART_ACTION) + * .setEventContext(classificationContext) + * .setResultId(textClassification.getId()) + * .setEntityTypes(textClassification.getEntity(0)) + * .setScore(textClassification.getConfidenceScore(entityType)) + * .setActionIndices(0) + * .setEventIndex(2) + * .build(); + * </pre> + */ + public static final class TextLinkifyEvent extends TextClassifierEvent implements Parcelable { + @NonNull - public Builder setActionIndices(@NonNull int... actionIndices) { - mActionIndices = new int[actionIndices.length]; - System.arraycopy(actionIndices, 0, mActionIndices, 0, actionIndices.length); - return this; + public static final Creator<TextLinkifyEvent> CREATOR = + new Creator<TextLinkifyEvent>() { + @Override + public TextLinkifyEvent createFromParcel(Parcel in) { + in.readInt(); // skip token, we already know this is a TextLinkifyEvent + return new TextLinkifyEvent(in); + } + + @Override + public TextLinkifyEvent[] newArray(int size) { + return new TextLinkifyEvent[size]; + } + }; + + private TextLinkifyEvent(Parcel in) { + super(in); + } + + private TextLinkifyEvent(TextLinkifyEvent.Builder builder) { + super(builder); } /** - * For language detection. Sets the language tag for the detected locale. - * @see java.util.Locale#forLanguageTag(String). + * Builder class for {@link TextLinkifyEvent}. */ + public static final class Builder + extends TextClassifierEvent.Builder<TextLinkifyEvent.Builder> { + /** + * Creates a builder for building {@link TextLinkifyEvent}s. + * + * @param eventType The event type. e.g. {@link #TYPE_SMART_ACTION} + */ + public Builder(@Type int eventType) { + super(TextClassifierEvent.CATEGORY_LINKIFY, eventType); + } + + @Override + Builder self() { + return this; + } + + /** + * Builds and returns a {@link TextLinkifyEvent}. + */ + @NonNull + public TextLinkifyEvent build() { + return new TextLinkifyEvent(this); + } + } + } + + /** + * This class represents events that are related to the language detection feature. + * <p> + * <pre> + * // Translate action shown for foreign text. + * new LanguageDetectionEvent.Builder(TYPE_ACTIONS_SHOWN) + * .setEventContext(classificationContext) + * .setResultId(textClassification.getId()) + * .setEntityTypes(language) + * .setScore(score) + * .setActionIndices(textClassification.getActions().indexOf(translateAction)) + * .setEventIndex(0) + * .build(); + * + * // Translate action selected. + * new LanguageDetectionEvent.Builder(TYPE_SMART_ACTION) + * .setEventContext(classificationContext) + * .setResultId(textClassification.getId()) + * .setEntityTypes(language) + * .setScore(score) + * .setActionIndices(textClassification.getActions().indexOf(translateAction)) + * .setEventIndex(1) + * .build(); + */ + public static final class LanguageDetectionEvent extends TextClassifierEvent + implements Parcelable { + @NonNull - public Builder setLanguage(@Nullable String language) { - mLanguage = language; - return this; + public static final Creator<LanguageDetectionEvent> CREATOR = + new Creator<LanguageDetectionEvent>() { + @Override + public LanguageDetectionEvent createFromParcel(Parcel in) { + // skip token, we already know this is a LanguageDetectionEvent. + in.readInt(); + return new LanguageDetectionEvent(in); + } + + @Override + public LanguageDetectionEvent[] newArray(int size) { + return new LanguageDetectionEvent[size]; + } + }; + + @Nullable + private final ULocale mLocale; + + private LanguageDetectionEvent(Parcel in) { + super(in); + final String languageTag = in.readString(); + mLocale = languageTag == null ? null : ULocale.forLanguageTag(languageTag); + } + + private LanguageDetectionEvent(LanguageDetectionEvent.Builder builder) { + super(builder); + mLocale = builder.mLocale; } /** - * Sets the score of the suggestion. + * Returns the detected locale. */ - @NonNull - public Builder setScore(float score) { - mScore = score; - return this; + @Nullable + public ULocale getLocale() { + return mLocale; } /** - * Sets the model name string. - * @hide + * Builder class for {@link LanguageDetectionEvent}. */ - public Builder setModelName(@Nullable String modelVersion) { - mModelName = modelVersion; - return this; + public static final class Builder + extends TextClassifierEvent.Builder<LanguageDetectionEvent.Builder> { + @Nullable + private ULocale mLocale; + + /** + * Creates a builder for building {@link TextSelectionEvent}s. + * + * @param eventType The event type. e.g. {@link #TYPE_SMART_ACTION} + */ + public Builder(@Type int eventType) { + super(TextClassifierEvent.CATEGORY_LANGUAGE_DETECTION, eventType); + } + + /** + * Sets the detected locale. + */ + @NonNull + public Builder setLocale(@Nullable ULocale locale) { + mLocale = locale; + return this; + } + + @Override + Builder self() { + return this; + } + + /** + * Builds and returns a {@link LanguageDetectionEvent}. + */ + @NonNull + public LanguageDetectionEvent build() { + return new LanguageDetectionEvent(this); + } + } + + @Override + public void writeToParcel(Parcel dest, int flags) { + super.writeToParcel(dest, flags); + dest.writeString(mLocale == null ? null : mLocale.toLanguageTag()); + } + } + + /** + * This class represents events that are related to the conversation actions feature. + * <p> + * <pre> + * // Conversation (contextual) actions/replies generated. + * new ConversationActionsEvent.Builder(TYPE_ACTIONS_GENERATED) + * .setEventContext(classificationContext) + * .setResultId(conversationActions.getId()) + * .setEntityTypes(getTypes(conversationActions)) + * .setActionIndices(range(conversationActions.getActions().size())) + * .setEventIndex(0) + * .build(); + * + * // Conversation actions/replies presented to user. + * new ConversationActionsEvent.Builder(TYPE_ACTIONS_SHOWN) + * .setEventContext(classificationContext) + * .setResultId(conversationActions.getId()) + * .setEntityTypes(getTypes(conversationActions)) + * .setActionIndices(range(conversationActions.getActions().size())) + * .setEventIndex(1) + * .build(); + * + * // User clicked the "Reply" button to compose their custom reply. + * new ConversationActionsEvent.Builder(TYPE_MANUAL_REPLY) + * .setEventContext(classificationContext) + * .setResultId(conversationActions.getId()) + * .setEventIndex(2) + * .build(); + * + * // User selected a smart (contextual) action/reply. + * new ConversationActionsEvent.Builder(TYPE_SMART_ACTION) + * .setEventContext(classificationContext) + * .setResultId(conversationActions.getId()) + * .setEntityTypes(conversationActions.get(1).getType()) + * .setScore(conversationAction.get(1).getConfidenceScore()) + * .setActionIndices(1) + * .setEventIndex(2) + * .build(); + * </pre> + */ + public static final class ConversationActionsEvent extends TextClassifierEvent + implements Parcelable { + + @NonNull + public static final Creator<ConversationActionsEvent> CREATOR = + new Creator<ConversationActionsEvent>() { + @Override + public ConversationActionsEvent createFromParcel(Parcel in) { + // skip token, we already know this is a ConversationActionsEvent. + in.readInt(); + return new ConversationActionsEvent(in); + } + + @Override + public ConversationActionsEvent[] newArray(int size) { + return new ConversationActionsEvent[size]; + } + }; + + private ConversationActionsEvent(Parcel in) { + super(in); + } + + private ConversationActionsEvent(ConversationActionsEvent.Builder builder) { + super(builder); } /** - * Builds and returns a text classifier event. + * Builder class for {@link ConversationActionsEvent}. */ - @NonNull - public TextClassifierEvent build() { - mExtras = mExtras == null ? Bundle.EMPTY : mExtras; - return new TextClassifierEvent( - mEventCategory, - mEventType, - mEntityTypes, - mEventContext, - mResultId, - mEventIndex, - mEventTime, - mExtras, - mRelativeWordStartIndex, - mRelativeWordEndIndex, - mRelativeSuggestedWordStartIndex, - mRelativeSuggestedWordEndIndex, - mActionIndices, - mLanguage, - mScore, - mModelName); - } - // TODO: Add build(boolean validate). - } + public static final class Builder + extends TextClassifierEvent.Builder<ConversationActionsEvent.Builder> { + /** + * Creates a builder for building {@link TextSelectionEvent}s. + * + * @param eventType The event type. e.g. {@link #TYPE_SMART_ACTION} + */ + public Builder(@Type int eventType) { + super(TextClassifierEvent.CATEGORY_CONVERSATION_ACTIONS, eventType); + } - @Override - public String toString() { - StringBuilder out = new StringBuilder(128); - out.append("TextClassifierEvent{"); - out.append("mEventCategory=").append(mEventCategory); - out.append(", mEventTypes=").append(Arrays.toString(mEntityTypes)); - out.append(", mEventContext=").append(mEventContext); - out.append(", mResultId=").append(mResultId); - out.append(", mEventIndex=").append(mEventIndex); - out.append(", mEventTime=").append(mEventTime); - out.append(", mExtras=").append(mExtras); - out.append(", mRelativeWordStartIndex=").append(mRelativeWordStartIndex); - out.append(", mRelativeWordEndIndex=").append(mRelativeWordEndIndex); - out.append(", mRelativeSuggestedWordStartIndex=").append(mRelativeSuggestedWordStartIndex); - out.append(", mRelativeSuggestedWordEndIndex=").append(mRelativeSuggestedWordEndIndex); - out.append(", mActionIndices=").append(Arrays.toString(mActionIndices)); - out.append(", mLanguage=").append(mLanguage); - out.append(", mScore=").append(mScore); - out.append(", mModelName=").append(mModelName); - out.append("}"); - return out.toString(); + @Override + Builder self() { + return this; + } + + /** + * Builds and returns a {@link ConversationActionsEvent}. + */ + @NonNull + public ConversationActionsEvent build() { + return new ConversationActionsEvent(this); + } + } } } diff --git a/core/java/android/view/textclassifier/TextClassifierEventTronLogger.java b/core/java/android/view/textclassifier/TextClassifierEventTronLogger.java index 6a122506d0ac..3e088b8565f2 100644 --- a/core/java/android/view/textclassifier/TextClassifierEventTronLogger.java +++ b/core/java/android/view/textclassifier/TextClassifierEventTronLogger.java @@ -65,9 +65,10 @@ public final class TextClassifierEventTronLogger { final LogMaker log = new LogMaker(category) .setSubtype(getLogType(event)) .addTaggedData(FIELD_TEXT_CLASSIFIER_SESSION_ID, event.getResultId()) - .addTaggedData(FIELD_TEXTCLASSIFIER_MODEL, getModelName(event)) - .addTaggedData(FIELD_TEXT_CLASSIFIER_SCORE, event.getScore()); - + .addTaggedData(FIELD_TEXTCLASSIFIER_MODEL, getModelName(event)); + if (event.getScores().length >= 1) { + log.addTaggedData(FIELD_TEXT_CLASSIFIER_SCORE, event.getScores()[0]); + } String[] entityTypes = event.getEntityTypes(); // The old logger does not support a field of list type, and thus workaround by store them // in three separate fields. This is not an issue with the new logger. diff --git a/core/java/android/view/textclassifier/TextClassifierImpl.java b/core/java/android/view/textclassifier/TextClassifierImpl.java index 323bf597ab55..3297523b0da9 100644 --- a/core/java/android/view/textclassifier/TextClassifierImpl.java +++ b/core/java/android/view/textclassifier/TextClassifierImpl.java @@ -307,6 +307,8 @@ public final class TextClassifierImpl implements TextClassifier { final String detectLanguageTags = detectLanguageTagsFromText(request.getText()); final AnnotatorModel annotatorImpl = getAnnotatorImpl(request.getDefaultLocales()); + final boolean isSerializedEntityDataEnabled = + ExtrasUtils.isSerializedEntityDataEnabled(request); final AnnotatorModel.AnnotatedSpan[] annotations = annotatorImpl.annotate( textString, @@ -314,7 +316,10 @@ public final class TextClassifierImpl implements TextClassifier { refTime.toInstant().toEpochMilli(), refTime.getZone().getId(), localesString, - detectLanguageTags)); + detectLanguageTags, + entitiesToIdentify, + AnnotatorModel.AnnotationUsecase.SMART.getValue(), + isSerializedEntityDataEnabled)); for (AnnotatorModel.AnnotatedSpan span : annotations) { final AnnotatorModel.ClassificationResult[] results = span.getClassification(); @@ -326,7 +331,11 @@ public final class TextClassifierImpl implements TextClassifier { for (int i = 0; i < results.length; i++) { entityScores.put(results[i].getCollection(), results[i].getScore()); } - builder.addLink(span.getStartIndex(), span.getEndIndex(), entityScores); + Bundle extras = new Bundle(); + if (isSerializedEntityDataEnabled) { + ExtrasUtils.putEntities(extras, results); + } + builder.addLink(span.getStartIndex(), span.getEndIndex(), entityScores, extras); } final TextLinks links = builder.build(); final long endTimeMs = System.currentTimeMillis(); @@ -451,10 +460,6 @@ public final class TextClassifierImpl implements TextClassifier { Collection<String> expectedTypes = resolveActionTypesFromRequest(request); List<ConversationAction> conversationActions = new ArrayList<>(); for (ActionsSuggestionsModel.ActionSuggestion nativeSuggestion : nativeSuggestions) { - if (request.getMaxSuggestions() >= 0 - && conversationActions.size() == request.getMaxSuggestions()) { - break; - } String actionType = nativeSuggestion.getActionType(); if (!expectedTypes.contains(actionType)) { continue; @@ -484,6 +489,10 @@ public final class TextClassifierImpl implements TextClassifier { } conversationActions = ActionsSuggestionsHelper.removeActionsWithDuplicates(conversationActions); + if (request.getMaxSuggestions() >= 0 + && conversationActions.size() > request.getMaxSuggestions()) { + conversationActions = conversationActions.subList(0, request.getMaxSuggestions()); + } String resultId = ActionsSuggestionsHelper.createResultId( mContext, request.getConversation(), diff --git a/core/java/android/widget/SelectionActionModeHelper.java b/core/java/android/widget/SelectionActionModeHelper.java index 564cfdd20d76..51ca80524090 100644 --- a/core/java/android/widget/SelectionActionModeHelper.java +++ b/core/java/android/widget/SelectionActionModeHelper.java @@ -933,12 +933,11 @@ public final class SelectionActionModeHelper { final String language = ExtrasUtils.getEntityType(foreignLanguageExtra); final float score = ExtrasUtils.getScore(foreignLanguageExtra); final String model = ExtrasUtils.getModelName(foreignLanguageExtra); - return new TextClassifierEvent.Builder( - TextClassifierEvent.CATEGORY_LANGUAGE_DETECTION, eventType) + return new TextClassifierEvent.LanguageDetectionEvent.Builder(eventType) .setEventContext(classificationContext) .setResultId(classification.getId()) .setEntityTypes(language) - .setScore(score) + .setScores(score) .setActionIndices(classification.getActions().indexOf(translateAction)) .setModelName(model) .build(); diff --git a/core/java/com/android/internal/app/ChooserActivity.java b/core/java/com/android/internal/app/ChooserActivity.java index ed851f86889d..54338bf6a176 100644 --- a/core/java/com/android/internal/app/ChooserActivity.java +++ b/core/java/com/android/internal/app/ChooserActivity.java @@ -2658,12 +2658,24 @@ public class ChooserActivity extends ResolverActivity { } } + /** + * Need to merge CALLER + ranked STANDARD into a single row. All other types + * are placed into their own row as determined by their target type, and dividers + * are added in the list to separate each type. + */ + int getRowType(int rowPosition) { + int positionType = mChooserListAdapter.getPositionTargetType(rowPosition); + if (positionType == ChooserListAdapter.TARGET_CALLER) { + return ChooserListAdapter.TARGET_STANDARD; + } + + return positionType; + } + void bindViewHolder(int rowPosition, RowViewHolder holder) { final int start = getFirstRowPosition(rowPosition); - final int startType = mChooserListAdapter.getPositionTargetType(start); - - final int lastStartType = mChooserListAdapter.getPositionTargetType( - getFirstRowPosition(rowPosition - 1)); + final int startType = getRowType(start); + final int lastStartType = getRowType(getFirstRowPosition(rowPosition - 1)); final ViewGroup row = holder.getViewGroup(); @@ -2675,7 +2687,7 @@ public class ChooserActivity extends ResolverActivity { int columnCount = holder.getColumnCount(); int end = start + columnCount - 1; - while (mChooserListAdapter.getPositionTargetType(end) != startType && end >= start) { + while (getRowType(end) != startType && end >= start) { end--; } @@ -2727,14 +2739,15 @@ public class ChooserActivity extends ResolverActivity { return row * getMaxTargetsPerRow(); } - final int callerCount = mChooserListAdapter.getCallerTargetCount(); - final int callerRows = (int) Math.ceil((float) callerCount / getMaxTargetsPerRow()); - if (row < callerRows + serviceRows) { + final int callerAndRankedCount = mChooserListAdapter.getCallerTargetCount() + + mChooserListAdapter.getRankedTargetCount(); + final int callerAndRankedRows = getCallerAndRankedTargetRowCount(); + if (row < callerAndRankedRows + serviceRows) { return serviceCount + (row - serviceRows) * getMaxTargetsPerRow(); } - return callerCount + serviceCount - + (row - callerRows - serviceRows) * getMaxTargetsPerRow(); + return callerAndRankedCount + serviceCount + + (row - callerAndRankedRows - serviceRows) * getMaxTargetsPerRow(); } public void handleScroll(View v, int y, int oldy) { diff --git a/core/java/com/android/internal/policy/PhoneWindow.java b/core/java/com/android/internal/policy/PhoneWindow.java index fd75f4fa4567..21f8d87e7e8c 100644 --- a/core/java/com/android/internal/policy/PhoneWindow.java +++ b/core/java/com/android/internal/policy/PhoneWindow.java @@ -45,6 +45,7 @@ import android.content.res.Configuration; import android.content.res.Resources.Theme; import android.content.res.TypedArray; import android.graphics.Color; +import android.graphics.Rect; import android.graphics.drawable.Drawable; import android.media.AudioManager; import android.media.session.MediaController; @@ -115,6 +116,7 @@ import com.android.internal.widget.SwipeDismissLayout; import java.lang.ref.WeakReference; import java.util.ArrayList; +import java.util.List; /** * Android-specific Window. @@ -3926,4 +3928,15 @@ public class PhoneWindow extends Window implements MenuBuilder.Callback { public WindowInsetsController getInsetsController() { return mDecor.getWindowInsetsController(); } + + @Override + public void setSystemGestureExclusionRects(@NonNull List<Rect> rects) { + getViewRootImpl().setRootSystemGestureExclusionRects(rects); + } + + @Override + @NonNull + public List<Rect> getSystemGestureExclusionRects() { + return getViewRootImpl().getRootSystemGestureExclusionRects(); + } } diff --git a/core/java/com/android/internal/util/TrafficStatsConstants.java b/core/java/com/android/internal/util/TrafficStatsConstants.java new file mode 100644 index 000000000000..2806ae2f9298 --- /dev/null +++ b/core/java/com/android/internal/util/TrafficStatsConstants.java @@ -0,0 +1,43 @@ +/* + * 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.internal.util; + +/** + * Constants for traffic stats. + * @hide + */ +public class TrafficStatsConstants { + // These tags are used by the network stack to do traffic for its own purposes. Traffic + // tagged with these will be counted toward the network stack and must stay inside the + // range defined by + // {@link android.net.TrafficStats#TAG_NETWORK_STACK_RANGE_START} and + // {@link android.net.TrafficStats#TAG_NETWORK_STACK_RANGE_END}. + public static final int TAG_SYSTEM_DHCP = 0xFFFFFE01; + public static final int TAG_SYSTEM_NEIGHBOR = 0xFFFFFE02; + public static final int TAG_SYSTEM_DHCP_SERVER = 0xFFFFFE03; + + public static final int TAG_SYSTEM_NTP = 0xFFFFFF41; + public static final int TAG_SYSTEM_GPS = 0xFFFFFF44; + public static final int TAG_SYSTEM_PAC = 0xFFFFFF45; + + // These tags are used by the network stack to do traffic on behalf of apps. Traffic + // tagged with these will be counted toward the app on behalf of which the network + // stack is doing this traffic. These values must stay inside the range defined by + // {@link android.net.TrafficStats#TAG_NETWORK_STACK_IMPERSONATION_RANGE_START} and + // {@link android.net.TrafficStats#TAG_NETWORK_STACK_IMPERSONATION_RANGE_END}. + public static final int TAG_SYSTEM_PROBE = 0xFFFFFF81; +} diff --git a/core/jni/android_nio_utils.cpp b/core/jni/android_nio_utils.cpp index a62dd7c4048f..1e6d49e49b72 100644 --- a/core/jni/android_nio_utils.cpp +++ b/core/jni/android_nio_utils.cpp @@ -18,51 +18,29 @@ #include "core_jni_helpers.h" -namespace { +namespace android { -void* getPointer(JNIEnv *_env, jobject buffer, jarray *array, void** elements) { - assert(array); - jint position; - jint limit; - jint elementSizeShift; - jlong pointer = jniGetNioBufferFields(_env, buffer, &position, &limit, &elementSizeShift); +AutoBufferPointer::AutoBufferPointer(JNIEnv* env, jobject nioBuffer, jboolean commit) + : fEnv(env), fCommit(commit) { + jlong pointer = jniGetNioBufferPointer(fEnv, nioBuffer); if (pointer != 0L) { - *array = nullptr; - *elements = nullptr; - pointer += position << elementSizeShift; - return reinterpret_cast<void*>(pointer); + // Buffer is backed by a direct buffer. + fArray = nullptr; + fElements = nullptr; + fPointer = reinterpret_cast<void*>(pointer); + } else { + // Buffer is backed by a managed array. + jint byteOffset = jniGetNioBufferBaseArrayOffset(fEnv, nioBuffer); + fArray = jniGetNioBufferBaseArray(fEnv, nioBuffer); + fElements = fEnv->GetPrimitiveArrayCritical(fArray, /* isCopy= */ nullptr); + fPointer = reinterpret_cast<void*>(reinterpret_cast<char*>(fElements) + byteOffset); } - jint offset = jniGetNioBufferBaseArrayOffset(_env, buffer); - *array = jniGetNioBufferBaseArray(_env, buffer); - *elements = _env->GetPrimitiveArrayCritical(*array, (jboolean *) 0); - return reinterpret_cast<void*>(reinterpret_cast<char*>(*elements) + offset); } -void releasePointer(JNIEnv *_env, jarray array, void *elements, jboolean commit) { - _env->ReleasePrimitiveArrayCritical(array, elements, commit ? 0 : JNI_ABORT); -} - -} // namespace - -void* android::nio_getPointer(JNIEnv *_env, jobject buffer, jarray *array) { - void* elements; - return getPointer(_env, buffer, array, &elements); -} - -void android::nio_releasePointer(JNIEnv *_env, jarray array, void *data, jboolean commit) { - releasePointer(_env, array, data, commit); -} - -/////////////////////////////////////////////////////////////////////////////// - -android::AutoBufferPointer::AutoBufferPointer(JNIEnv* env, jobject nioBuffer, jboolean commit) { - fEnv = env; - fCommit = commit; - fPointer = getPointer(env, nioBuffer, &fArray, &fElements); -} - -android::AutoBufferPointer::~AutoBufferPointer() { +AutoBufferPointer::~AutoBufferPointer() { if (nullptr != fArray) { - releasePointer(fEnv, fArray, fElements, fCommit); + fEnv->ReleasePrimitiveArrayCritical(fArray, fElements, fCommit ? 0 : JNI_ABORT); } } + +} // namespace android diff --git a/core/jni/android_nio_utils.h b/core/jni/android_nio_utils.h index 7c9acd2638da..aa75dd0c7e32 100644 --- a/core/jni/android_nio_utils.h +++ b/core/jni/android_nio_utils.h @@ -22,51 +22,58 @@ namespace android { /** - * Given an nio.Buffer, return a pointer to it, beginning at its current - * position. The returned pointer is only valid for the current JNI stack-frame. - * For performance, it does not create any global references, so the getPointer - * (and releasePointer if array is returned non-null) must be done in the - * same JNI stack-frame. + * Class providing scoped access to the memory backing a java.nio.Buffer instance. * - * @param env The current JNI env - * @param buffer The nio.Buffer object - * @param array REQUIRED. Output. If on return it is set to non-null, then - * nio_releasePointer must be called with the array - * and the returned pointer when the caller is through with it. - * If on return it is set to null, do not call - * nio_releasePointer. - * @return The pointer to the memory in the buffer object - */ -void* nio_getPointer(JNIEnv *env, jobject buffer, jarray *array); - -/** - * Call this if android_nio_getPointer returned non-null in its array parameter. - * Pass that array and the returned pointer when you are done accessing the - * pointer. If called (i.e. array is non-null), it must be called in the same - * JNI stack-frame as getPointer + * Instances of this class should only be allocated on the stack as heap allocation is not + * supported. * - * @param env The current JNI env - * @param buffer The array returned from android_nio_getPointer (!= null) - * @param pointer The pointer returned by android_nio_getPointer - * @param commit JNI_FALSE if the pointer was just read, and JNI_TRUE if - * the pointer was written to. + * Instances of this class do not create any global references for performance reasons. */ -void nio_releasePointer(JNIEnv *env, jarray array, void *pointer, - jboolean commit); - -class AutoBufferPointer { +class AutoBufferPointer final { public: + /** Constructor for an AutoBufferPointer instance. + * + * @param env The current JNI env + * @param nioBuffer Instance of a java.nio.Buffer whose memory will be accessed. + * @param commit JNI_TRUE if the underlying memory will be updated and should be + * copied back to the managed heap. JNI_FALSE if the data will + * not be modified or the modifications may be discarded. + * + * The commit parameter is only applicable if the buffer is backed by a managed heap + * array and the runtime had to provide a copy of the data rather than the original data. + */ AutoBufferPointer(JNIEnv* env, jobject nioBuffer, jboolean commit); + + /** Destructor for an AutoBufferPointer instance. + * + * Releases critical managed heap array pointer if acquired. + */ ~AutoBufferPointer(); + /** + * Returns a pointer to the current position of the buffer provided to the constructor. This + * pointer is only valid whilst the AutoBufferPointer instance remains in scope. + */ void* pointer() const { return fPointer; } private: - JNIEnv* fEnv; - void* fPointer; // pointer to current buffer position. - void* fElements; // pointer to array element 0 (may be directly in fArray or a copy). - jarray fArray; // pointer to array on managed heap. - jboolean fCommit; // commit data to source if required (when fElements is a copy of fArray). + JNIEnv* const fEnv; + void* fPointer; // Pointer to current buffer position when constructed. + void* fElements; // Pointer to array element 0 (null if buffer is direct, may be + // within fArray or point to a copy of the array). + jarray fArray; // Pointer to array on managed heap. + const jboolean fCommit; // Flag to commit data to source (when fElements is a copy of fArray). + + // Unsupported constructors and operators. + AutoBufferPointer() = delete; + AutoBufferPointer(AutoBufferPointer&) = delete; + AutoBufferPointer& operator=(AutoBufferPointer&) = delete; + static void* operator new(std::size_t); + static void* operator new[](std::size_t); + static void* operator new(std::size_t, void*); + static void* operator new[](std::size_t, void*); + static void operator delete(void*, std::size_t); + static void operator delete[](void*, std::size_t); }; } /* namespace android */ diff --git a/core/jni/com_android_internal_os_Zygote.cpp b/core/jni/com_android_internal_os_Zygote.cpp index f599913ce497..e2214d160fdc 100644 --- a/core/jni/com_android_internal_os_Zygote.cpp +++ b/core/jni/com_android_internal_os_Zygote.cpp @@ -73,6 +73,7 @@ #include <android-base/stringprintf.h> #include <android-base/strings.h> #include <android-base/unique_fd.h> +#include <bionic_malloc.h> #include <cutils/ashmem.h> #include <cutils/fs.h> #include <cutils/multiuser.h> @@ -499,12 +500,9 @@ static void EnableDebugger() { } } -// The debug malloc library needs to know whether it's the zygote or a child. -extern "C" int gMallocLeakZygoteChild; - static void PreApplicationInit() { // The child process sets this to indicate it's not the zygote. - gMallocLeakZygoteChild = 1; + android_mallopt(M_SET_ZYGOTE_CHILD, nullptr, 0); // Set the jemalloc decay time to 1. mallopt(M_DECAY_TIME, 1); diff --git a/core/res/res/values/config.xml b/core/res/res/values/config.xml index 526818949b3d..4d6fda980755 100644 --- a/core/res/res/values/config.xml +++ b/core/res/res/values/config.xml @@ -3483,6 +3483,12 @@ <item>com.android.messaging</item> </string-array> + <!-- An array of packages that can make sound on the ringer stream in priority-only DND + mode --> + <string-array translatable="false" name="config_priorityOnlyDndExemptPackages"> + <item>com.android.dialer</item> + </string-array> + <!-- An array of packages which can listen for notifications on low ram devices. --> <string-array translatable="false" name="config_allowedManagedServicesOnLowRamDevices" /> diff --git a/core/res/res/values/symbols.xml b/core/res/res/values/symbols.xml index 94b5da6baa07..924b036813a6 100644 --- a/core/res/res/values/symbols.xml +++ b/core/res/res/values/symbols.xml @@ -3188,6 +3188,7 @@ <java-symbol type="array" name="config_convert_to_emergency_number_map" /> <java-symbol type="array" name="config_nonBlockableNotificationPackages" /> + <java-symbol type="array" name="config_priorityOnlyDndExemptPackages" /> <java-symbol type="array" name="config_allowedManagedServicesOnLowRamDevices" /> diff --git a/core/tests/coretests/src/android/graphics/BitmapTest.java b/core/tests/coretests/src/android/graphics/BitmapTest.java index 4bee24385cc5..2280cf1cccfa 100644 --- a/core/tests/coretests/src/android/graphics/BitmapTest.java +++ b/core/tests/coretests/src/android/graphics/BitmapTest.java @@ -23,6 +23,8 @@ import androidx.test.filters.SmallTest; import junit.framework.TestCase; import java.nio.ByteBuffer; +import java.nio.IntBuffer; +import java.nio.ShortBuffer; public class BitmapTest extends TestCase { @@ -266,10 +268,11 @@ public class BitmapTest extends TestCase { } @SmallTest - public void testCopyWithDirectBuffer() { + public void testCopyWithDirectByteBuffer() { // Initialize Bitmap final int width = 2; final int height = 2; + final int bytesPerPixel = 2; Bitmap bm1 = Bitmap.createBitmap(width, height, Bitmap.Config.RGB_565); bm1.setPixels(new int[] { 0xff, 0xeeee, 0xdddddd, 0xcccccccc }, 0, 2, 0, 0, 2, 2); @@ -277,7 +280,8 @@ public class BitmapTest extends TestCase { // of bitmap. final int pad = 1; final byte padValue = 0x5a; - final int bufferSize = pad + width * height * 2 + pad; + final int bytesPerElement = 1; + final int bufferSize = pad + width * height * bytesPerPixel / bytesPerElement + pad; ByteBuffer directBuffer = ByteBuffer.allocateDirect(bufferSize); // Write padding @@ -287,7 +291,8 @@ public class BitmapTest extends TestCase { // Copy bitmap directBuffer.position(pad); bm1.copyPixelsToBuffer(directBuffer); - assertEquals(directBuffer.position(), pad + width * height * 2); + assertEquals(directBuffer.position(), + pad + width * height * bytesPerPixel / bytesPerElement); // Check padding assertEquals(directBuffer.get(0), padValue); @@ -301,10 +306,89 @@ public class BitmapTest extends TestCase { } @SmallTest - public void testCopyWithHeapBuffer() { + public void testCopyWithDirectShortBuffer() { // Initialize Bitmap final int width = 2; final int height = 2; + final int bytesPerPixel = 2; + Bitmap bm1 = Bitmap.createBitmap(width, height, Bitmap.Config.RGB_565); + bm1.setPixels(new int[] { 0xff, 0xeeee, 0xdddddd, 0xcccccccc }, 0, 2, 0, 0, 2, 2); + + // Copy bytes to heap buffer, buffer is padded by fixed amount (pad bytes) either side + // of bitmap. + final int pad = 1; + final short padValue = 0x55aa; + final int bytesPerElement = 2; + final int bufferSize = pad + width * height * bytesPerPixel / bytesPerElement + pad; + ShortBuffer directBuffer = + ByteBuffer.allocateDirect(bufferSize * bytesPerElement).asShortBuffer(); + + // Write padding + directBuffer.put(0, padValue); + directBuffer.put(directBuffer.limit() - 1, padValue); + + // Copy bitmap + directBuffer.position(pad); + bm1.copyPixelsToBuffer(directBuffer); + assertEquals(directBuffer.position(), + pad + width * height * bytesPerPixel / bytesPerElement); + + // Check padding + assertEquals(directBuffer.get(0), padValue); + assertEquals(directBuffer.get(directBuffer.limit() - 1), padValue); + + // Create bitmap from heap buffer and check match. + directBuffer.position(pad); + Bitmap bm2 = Bitmap.createBitmap(width, height, Bitmap.Config.RGB_565); + bm2.copyPixelsFromBuffer(directBuffer); + assertTrue(bm2.sameAs(bm1)); + } + + @SmallTest + public void testCopyWithDirectIntBuffer() { + // Initialize Bitmap + final int width = 2; + final int height = 2; + final int bytesPerPixel = 2; + Bitmap bm1 = Bitmap.createBitmap(width, height, Bitmap.Config.RGB_565); + bm1.setPixels(new int[] { 0xff, 0xeeee, 0xdddddd, 0xcccccccc }, 0, 2, 0, 0, 2, 2); + + // Copy bytes to heap buffer, buffer is padded by fixed amount (pad bytes) either side + // of bitmap. + final int pad = 1; + final int padValue = 0x55aa5a5a; + final int bytesPerElement = 4; + final int bufferSize = pad + width * height * bytesPerPixel / bytesPerElement + pad; + IntBuffer directBuffer = + ByteBuffer.allocateDirect(bufferSize * bytesPerElement).asIntBuffer(); + + // Write padding + directBuffer.put(0, padValue); + directBuffer.put(directBuffer.limit() - 1, padValue); + + // Copy bitmap + directBuffer.position(pad); + bm1.copyPixelsToBuffer(directBuffer); + assertEquals(directBuffer.position(), + pad + width * height * bytesPerPixel / bytesPerElement); + + // Check padding + assertEquals(directBuffer.get(0), padValue); + assertEquals(directBuffer.get(directBuffer.limit() - 1), padValue); + + // Create bitmap from heap buffer and check match. + directBuffer.position(pad); + Bitmap bm2 = Bitmap.createBitmap(width, height, Bitmap.Config.RGB_565); + bm2.copyPixelsFromBuffer(directBuffer); + assertTrue(bm2.sameAs(bm1)); + } + + @SmallTest + public void testCopyWithHeapByteBuffer() { + // Initialize Bitmap + final int width = 2; + final int height = 2; + final int bytesPerPixel = 2; Bitmap bm1 = Bitmap.createBitmap(width, height, Bitmap.Config.RGB_565); bm1.setPixels(new int[] { 0xff, 0xeeee, 0xdddddd, 0xcccccccc }, 0, 2, 0, 0, 2, 2); @@ -312,7 +396,8 @@ public class BitmapTest extends TestCase { // of bitmap. final int pad = 1; final byte padValue = 0x5a; - final int bufferSize = pad + width * height * 2 + pad; + final int bytesPerElement = 1; + final int bufferSize = pad + width * height * bytesPerPixel / bytesPerElement + pad; ByteBuffer heapBuffer = ByteBuffer.allocate(bufferSize); // Write padding @@ -322,7 +407,81 @@ public class BitmapTest extends TestCase { // Copy bitmap heapBuffer.position(pad); bm1.copyPixelsToBuffer(heapBuffer); - assertEquals(heapBuffer.position(), pad + width * height * 2); + assertEquals(heapBuffer.position(), pad + width * height * bytesPerPixel / bytesPerElement); + + // Check padding + assertEquals(heapBuffer.get(0), padValue); + assertEquals(heapBuffer.get(heapBuffer.limit() - 1), padValue); + + // Create bitmap from heap buffer and check match. + heapBuffer.position(pad); + Bitmap bm2 = Bitmap.createBitmap(width, height, Bitmap.Config.RGB_565); + bm2.copyPixelsFromBuffer(heapBuffer); + assertTrue(bm2.sameAs(bm1)); + } + + @SmallTest + public void testCopyWithHeapShortBuffer() { + // Initialize Bitmap + final int width = 2; + final int height = 2; + final int bytesPerPixel = 2; + Bitmap bm1 = Bitmap.createBitmap(width, height, Bitmap.Config.RGB_565); + bm1.setPixels(new int[] { 0xff, 0xeeee, 0xdddddd, 0xcccccccc }, 0, 2, 0, 0, 2, 2); + + // Copy bytes to heap buffer, buffer is padded by fixed amount (pad bytes) either side + // of bitmap. + final int pad = 1; + final short padValue = 0x55aa; + final int bytesPerElement = 2; + final int bufferSize = pad + width * height * bytesPerPixel / bytesPerElement + pad; + ShortBuffer heapBuffer = ShortBuffer.allocate(bufferSize); + + // Write padding + heapBuffer.put(0, padValue); + heapBuffer.put(heapBuffer.limit() - 1, padValue); + + // Copy bitmap + heapBuffer.position(pad); + bm1.copyPixelsToBuffer(heapBuffer); + assertEquals(heapBuffer.position(), pad + width * height * bytesPerPixel / bytesPerElement); + + // Check padding + assertEquals(heapBuffer.get(0), padValue); + assertEquals(heapBuffer.get(heapBuffer.limit() - 1), padValue); + + // Create bitmap from heap buffer and check match. + heapBuffer.position(pad); + Bitmap bm2 = Bitmap.createBitmap(width, height, Bitmap.Config.RGB_565); + bm2.copyPixelsFromBuffer(heapBuffer); + assertTrue(bm2.sameAs(bm1)); + } + + @SmallTest + public void testCopyWithHeapIntBuffer() { + // Initialize Bitmap + final int width = 2; + final int height = 2; + final int bytesPerPixel = 2; + Bitmap bm1 = Bitmap.createBitmap(width, height, Bitmap.Config.RGB_565); + bm1.setPixels(new int[] { 0xff, 0xeeee, 0xdddddd, 0xcccccccc }, 0, 2, 0, 0, 2, 2); + + // Copy bytes to heap buffer, buffer is padded by fixed amount (pad bytes) either side + // of bitmap. + final int pad = 1; + final int padValue = 0x55aa5a5a; + final int bytesPerElement = 4; + final int bufferSize = pad + width * height * bytesPerPixel / bytesPerElement + pad; + IntBuffer heapBuffer = IntBuffer.allocate(bufferSize); + + // Write padding + heapBuffer.put(0, padValue); + heapBuffer.put(heapBuffer.limit() - 1, padValue); + + // Copy bitmap + heapBuffer.position(pad); + bm1.copyPixelsToBuffer(heapBuffer); + assertEquals(heapBuffer.position(), pad + width * height * bytesPerPixel / bytesPerElement); // Check padding assertEquals(heapBuffer.get(0), padValue); diff --git a/core/tests/coretests/src/android/view/textclassifier/TextClassifierTest.java b/core/tests/coretests/src/android/view/textclassifier/TextClassifierTest.java index 433991e86212..aeb8949c6976 100644 --- a/core/tests/coretests/src/android/view/textclassifier/TextClassifierTest.java +++ b/core/tests/coretests/src/android/view/textclassifier/TextClassifierTest.java @@ -362,6 +362,38 @@ public class TextClassifierTest { } @Test + public void testGenerateLinks_entityData() { + if (isTextClassifierDisabled()) return; + String text = "The number is +12122537077."; + Bundle extras = new Bundle(); + ExtrasUtils.putIsSerializedEntityDataEnabled(extras, true); + TextLinks.Request request = new TextLinks.Request.Builder(text).setExtras(extras).build(); + + TextLinks textLinks = mClassifier.generateLinks(request); + + Truth.assertThat(textLinks.getLinks()).hasSize(1); + TextLinks.TextLink textLink = textLinks.getLinks().iterator().next(); + List<Bundle> entities = ExtrasUtils.getEntities(textLink.getExtras()); + Truth.assertThat(entities).hasSize(1); + Bundle entity = entities.get(0); + Truth.assertThat(ExtrasUtils.getEntityType(entity)).isEqualTo(TextClassifier.TYPE_PHONE); + } + + @Test + public void testGenerateLinks_entityData_disabled() { + if (isTextClassifierDisabled()) return; + String text = "The number is +12122537077."; + TextLinks.Request request = new TextLinks.Request.Builder(text).build(); + + TextLinks textLinks = mClassifier.generateLinks(request); + + Truth.assertThat(textLinks.getLinks()).hasSize(1); + TextLinks.TextLink textLink = textLinks.getLinks().iterator().next(); + List<Bundle> entities = ExtrasUtils.getEntities(textLink.getExtras()); + Truth.assertThat(entities).isNull(); + } + + @Test public void testDetectLanguage() { if (isTextClassifierDisabled()) return; String text = "This is English text"; @@ -380,7 +412,7 @@ public class TextClassifierTest { } @Test - public void testSuggestConversationActions_textReplyOnly_maxThree() { + public void testSuggestConversationActions_textReplyOnly_maxOne() { if (isTextClassifierDisabled()) return; ConversationActions.Message message = new ConversationActions.Message.Builder( @@ -399,12 +431,11 @@ public class TextClassifierTest { .build(); ConversationActions conversationActions = mClassifier.suggestConversationActions(request); - assertTrue(conversationActions.getConversationActions().size() > 0); - for (ConversationAction conversationAction : - conversationActions.getConversationActions()) { - assertThat(conversationAction, - isConversationAction(ConversationAction.TYPE_TEXT_REPLY)); - } + Truth.assertThat(conversationActions.getConversationActions()).hasSize(1); + ConversationAction conversationAction = conversationActions.getConversationActions().get(0); + Truth.assertThat(conversationAction.getType()).isEqualTo( + ConversationAction.TYPE_TEXT_REPLY); + Truth.assertThat(conversationAction.getTextReply()).isNotNull(); } @Test @@ -493,6 +524,24 @@ public class TextClassifierTest { ExtrasUtils.getSerializedEntityData(conversationAction.getExtras())).isNotEmpty(); } + @Test + public void testSuggetsConversationActions_deduplicate() { + if (isTextClassifierDisabled()) return; + ConversationActions.Message message = + new ConversationActions.Message.Builder( + ConversationActions.Message.PERSON_USER_OTHERS) + .setText("a@android.com b@android.com") + .build(); + ConversationActions.Request request = + new ConversationActions.Request.Builder(Collections.singletonList(message)) + .setMaxSuggestions(3) + .build(); + + ConversationActions conversationActions = mClassifier.suggestConversationActions(request); + + Truth.assertThat(conversationActions.getConversationActions()).isEmpty(); + } + private boolean isTextClassifierDisabled() { return mClassifier == null || mClassifier == TextClassifier.NO_OP; } diff --git a/core/tests/coretests/src/android/view/textclassifier/logging/TextClassifierEventTronLoggerTest.java b/core/tests/coretests/src/android/view/textclassifier/logging/TextClassifierEventTronLoggerTest.java index 1980a604fdd2..2c540e560f6b 100644 --- a/core/tests/coretests/src/android/view/textclassifier/logging/TextClassifierEventTronLoggerTest.java +++ b/core/tests/coretests/src/android/view/textclassifier/logging/TextClassifierEventTronLoggerTest.java @@ -49,8 +49,6 @@ import org.mockito.MockitoAnnotations; public class TextClassifierEventTronLoggerTest { private static final String WIDGET_TYPE = "notification"; private static final String PACKAGE_NAME = "pkg"; - private static final long EVENT_TIME = System.currentTimeMillis(); - @Mock private MetricsLogger mMetricsLogger; @@ -68,13 +66,11 @@ public class TextClassifierEventTronLoggerTest { TextClassificationContext textClassificationContext = new TextClassificationContext.Builder(PACKAGE_NAME, WIDGET_TYPE) .build(); - TextClassifierEvent textClassifierEvent = - new TextClassifierEvent.Builder( - TextClassifierEvent.CATEGORY_CONVERSATION_ACTIONS, + TextClassifierEvent.ConversationActionsEvent textClassifierEvent = + new TextClassifierEvent.ConversationActionsEvent.Builder( TextClassifierEvent.TYPE_SMART_ACTION) .setEntityTypes(ConversationAction.TYPE_CALL_PHONE) - .setScore(0.5f) - .setEventTime(EVENT_TIME) + .setScores(0.5f) .setEventContext(textClassificationContext) .build(); @@ -83,10 +79,8 @@ public class TextClassifierEventTronLoggerTest { ArgumentCaptor<LogMaker> captor = ArgumentCaptor.forClass(LogMaker.class); Mockito.verify(mMetricsLogger).write(captor.capture()); LogMaker logMaker = captor.getValue(); - assertThat(logMaker.getCategory()).isEqualTo( - CONVERSATION_ACTIONS); - assertThat(logMaker.getSubtype()).isEqualTo( - ACTION_TEXT_SELECTION_SMART_SHARE); + assertThat(logMaker.getCategory()).isEqualTo(CONVERSATION_ACTIONS); + assertThat(logMaker.getSubtype()).isEqualTo(ACTION_TEXT_SELECTION_SMART_SHARE); assertThat(logMaker.getTaggedData(FIELD_TEXT_CLASSIFIER_FIRST_ENTITY_TYPE)) .isEqualTo(ConversationAction.TYPE_CALL_PHONE); assertThat((float) logMaker.getTaggedData(FIELD_TEXT_CLASSIFIER_SCORE)) @@ -101,9 +95,8 @@ public class TextClassifierEventTronLoggerTest { @Test public void testWriteEvent_unsupportedCategory() { - TextClassifierEvent textClassifierEvent = - new TextClassifierEvent.Builder( - TextClassifierEvent.CATEGORY_SELECTION, + TextClassifierEvent.TextSelectionEvent textClassifierEvent = + new TextClassifierEvent.TextSelectionEvent.Builder( TextClassifierEvent.TYPE_SMART_ACTION) .build(); diff --git a/core/tests/mockingcoretests/Android.bp b/core/tests/mockingcoretests/Android.bp new file mode 100644 index 000000000000..ae3ff8612eee --- /dev/null +++ b/core/tests/mockingcoretests/Android.bp @@ -0,0 +1,53 @@ +// +// 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: "FrameworksMockingCoreTests", + + srcs: [ + "src/**/*.java", + ], + + static_libs: [ + "frameworks-base-testutils", + "services.core", + "androidx.test.runner", + "androidx.test.rules", + "androidx.test.ext.junit", + "mockito-target-extended-minus-junit4", + "platform-test-annotations", + "truth-prebuilt", + "testables", + "ub-uiautomator", + ], + + libs: [ + "android.test.base", + "android.test.mock", + "android.test.runner", + ], + + // These are not normally accessible from apps so they must be explicitly included. + jni_libs: [ + "libdexmakerjvmtiagent", + "libstaticjvmtiagent", + ], + + platform_apis: true, + test_suites: ["device-tests"], + + certificate: "platform", +} diff --git a/core/tests/mockingcoretests/AndroidManifest.xml b/core/tests/mockingcoretests/AndroidManifest.xml new file mode 100644 index 000000000000..b9ee0852366d --- /dev/null +++ b/core/tests/mockingcoretests/AndroidManifest.xml @@ -0,0 +1,35 @@ +<?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" + android:installLocation="internalOnly" + package="com.android.frameworks.mockingcoretests" + android:sharedUserId="com.android.uid.test"> + + <application android:supportsRtl="true" android:debuggable="true"> + <uses-library android:name="android.test.runner" /> + + <activity android:name="android.app.activity.ActivityThreadClientTest$TestActivity" + android:exported="true"> + </activity> + + </application> + + <instrumentation android:name="androidx.test.runner.AndroidJUnitRunner" + android:targetPackage="com.android.frameworks.mockingcoretests" + android:label="Frameworks Mocking Core Tests" /> +</manifest> diff --git a/core/tests/mockingcoretests/AndroidTest.xml b/core/tests/mockingcoretests/AndroidTest.xml new file mode 100644 index 000000000000..47aa41003336 --- /dev/null +++ b/core/tests/mockingcoretests/AndroidTest.xml @@ -0,0 +1,31 @@ +<?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 + --> +<configuration description="Runs Frameworks Mocking Core Tests."> + <option name="test-suite-tag" value="apct" /> + <option name="test-suite-tag" value="apct-instrumentation" /> + + <target_preparer class="com.android.tradefed.targetprep.suite.SuiteApkInstaller"> + <option name="cleanup-apks" value="true" /> + <option name="test-file-name" value="FrameworksMockingCoreTests.apk" /> + </target_preparer> + <option name="test-tag" value="FrameworksMockingCoreTests" /> + <test class="com.android.tradefed.testtype.AndroidJUnitTest" > + <option name="package" value="com.android.frameworks.mockingcoretests" /> + <option name="runner" value="androidx.test.runner.AndroidJUnitRunner" /> + <option name="hidden-api-checks" value="false"/> + </test> +</configuration> diff --git a/core/tests/mockingcoretests/README b/core/tests/mockingcoretests/README new file mode 100644 index 000000000000..c0f65c29a8a4 --- /dev/null +++ b/core/tests/mockingcoretests/README @@ -0,0 +1,33 @@ +* 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. + + +INTRODUCTION + +The Android platform core tests that require additional mocking capabilities and use Extended +Mockito, such as the ability to stub static methods. ExtendedMockito is not fully compatible with +regular Mockito library, so tests that use it have to be verified and adapted. For that reason a +separate module is used instead of adding to FrameworksCoreTests. + +For more information about ExtendedMockito see documentation of +com.android.dx.mockito.inline.extended.ExtendedMockito class. + +See ../coretests/README for more information on FrameworksCoreTests. + + +INSTRUCTIONS + +To build, install and run: + + atest FrameworksMockingCoreTests diff --git a/core/tests/mockingcoretests/src/android/app/activity/ActivityThreadClientTest.java b/core/tests/mockingcoretests/src/android/app/activity/ActivityThreadClientTest.java new file mode 100644 index 000000000000..86d55ea4f5c2 --- /dev/null +++ b/core/tests/mockingcoretests/src/android/app/activity/ActivityThreadClientTest.java @@ -0,0 +1,362 @@ +/* + * Copyright (C) 2019 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package android.app.activity; + +import static android.app.servertransaction.ActivityLifecycleItem.ON_CREATE; +import static android.app.servertransaction.ActivityLifecycleItem.ON_DESTROY; +import static android.app.servertransaction.ActivityLifecycleItem.ON_PAUSE; +import static android.app.servertransaction.ActivityLifecycleItem.ON_RESUME; +import static android.app.servertransaction.ActivityLifecycleItem.ON_START; +import static android.app.servertransaction.ActivityLifecycleItem.ON_STOP; + +import static com.android.dx.mockito.inline.extended.ExtendedMockito.doReturn; +import static com.android.dx.mockito.inline.extended.ExtendedMockito.mockitoSession; + +import static org.junit.Assert.assertEquals; +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.ArgumentMatchers.anyInt; +import static org.mockito.Mockito.when; + +import android.app.Activity; +import android.app.ActivityTaskManager; +import android.app.ActivityThread; +import android.app.ActivityThread.ActivityClientRecord; +import android.app.IActivityTaskManager; +import android.app.servertransaction.PendingTransactionActions; +import android.content.ComponentName; +import android.content.Intent; +import android.content.pm.ActivityInfo; +import android.content.pm.ApplicationInfo; +import android.content.res.CompatibilityInfo; +import android.content.res.Configuration; +import android.os.Binder; +import android.os.RemoteException; +import android.os.UserHandle; +import android.view.WindowManagerGlobal; + +import androidx.test.annotation.UiThreadTest; +import androidx.test.ext.junit.runners.AndroidJUnit4; +import androidx.test.filters.MediumTest; +import androidx.test.platform.app.InstrumentationRegistry; + +import org.junit.Test; +import org.junit.runner.RunWith; +import org.mockito.Mockito; +import org.mockito.MockitoSession; +import org.mockito.quality.Strictness; + +/** + * Test for verifying {@link android.app.ActivityThread} class. + * + * <p>Build/Install/Run: + * atest FrameworksMockingCoreTests:android.app.activity.ActivityThreadClientTest + * + * <p>This test class is a part of Window Manager Service tests and specified in + * {@link com.android.server.wm.test.filters.FrameworksTestsFilter}. + */ +@RunWith(AndroidJUnit4.class) +@MediumTest +public class ActivityThreadClientTest { + + @Test + @UiThreadTest + public void testWindowVisibilityChange_OnCreate() throws Exception { + try (ClientMockSession clientSession = new ClientMockSession()) { + ActivityClientRecord r = clientSession.stubActivityRecord(); + + clientSession.launchActivity(r); + assertEquals(ON_CREATE, r.getLifecycleState()); + + clientSession.changeVisibility(r, true); + assertEquals(ON_CREATE, r.getLifecycleState()); + + clientSession.changeVisibility(r, false); + assertEquals(ON_CREATE, r.getLifecycleState()); + } + } + + @Test + @UiThreadTest + public void testWindowVisibilityChange_OnCreate_Finished() throws Exception { + try (ClientMockSession clientSession = new ClientMockSession()) { + ActivityClientRecord r = clientSession.stubActivityRecord(); + + Activity activity = clientSession.launchActivity(r); + activity.finish(); + assertEquals(ON_CREATE, r.getLifecycleState()); + + clientSession.changeVisibility(r, true); + assertEquals(ON_CREATE, r.getLifecycleState()); + + clientSession.changeVisibility(r, false); + assertEquals(ON_CREATE, r.getLifecycleState()); + } + } + + @Test + @UiThreadTest + public void testWindowVisibilityChange_OnStart() throws Exception { + try (ClientMockSession clientSession = new ClientMockSession()) { + ActivityClientRecord r = clientSession.stubActivityRecord(); + + clientSession.launchActivity(r); + clientSession.startActivity(r); + assertEquals(ON_START, r.getLifecycleState()); + + clientSession.changeVisibility(r, false); + assertEquals(ON_STOP, r.getLifecycleState()); + + clientSession.changeVisibility(r, true); + assertEquals(ON_START, r.getLifecycleState()); + } + } + + @Test + @UiThreadTest + public void testWindowVisibilityChange_OnStart_Finished() throws Exception { + try (ClientMockSession clientSession = new ClientMockSession()) { + ActivityClientRecord r = clientSession.stubActivityRecord(); + + Activity activity = clientSession.launchActivity(r); + clientSession.startActivity(r); + activity.finish(); + assertEquals(ON_START, r.getLifecycleState()); + + clientSession.changeVisibility(r, false); + assertEquals(ON_STOP, r.getLifecycleState()); + + clientSession.changeVisibility(r, true); + assertEquals(ON_START, r.getLifecycleState()); + } + } + + @Test + @UiThreadTest + public void testWindowVisibilityChange_OnResume() throws Exception { + try (ClientMockSession clientSession = new ClientMockSession()) { + ActivityClientRecord r = clientSession.stubActivityRecord(); + + clientSession.launchActivity(r); + clientSession.startActivity(r); + clientSession.resumeActivity(r); + assertEquals(ON_RESUME, r.getLifecycleState()); + + clientSession.changeVisibility(r, false); + assertEquals(ON_STOP, r.getLifecycleState()); + + clientSession.changeVisibility(r, true); + assertEquals(ON_START, r.getLifecycleState()); + } + } + + @Test + @UiThreadTest + public void testWindowVisibilityChange_OnPause() throws Exception { + try (ClientMockSession clientSession = new ClientMockSession()) { + ActivityClientRecord r = clientSession.stubActivityRecord(); + + clientSession.launchActivity(r); + clientSession.startActivity(r); + clientSession.resumeActivity(r); + clientSession.pauseActivity(r); + assertEquals(ON_PAUSE, r.getLifecycleState()); + + clientSession.changeVisibility(r, false); + assertEquals(ON_STOP, r.getLifecycleState()); + + clientSession.changeVisibility(r, true); + assertEquals(ON_START, r.getLifecycleState()); + } + } + + @Test + @UiThreadTest + public void testWindowVisibilityChange_OnStop() throws Exception { + try (ClientMockSession clientSession = new ClientMockSession()) { + ActivityClientRecord r = clientSession.stubActivityRecord(); + + clientSession.launchActivity(r); + clientSession.startActivity(r); + clientSession.resumeActivity(r); + clientSession.pauseActivity(r); + clientSession.stopActivity(r); + assertEquals(ON_STOP, r.getLifecycleState()); + + clientSession.changeVisibility(r, true); + assertEquals(ON_START, r.getLifecycleState()); + + clientSession.changeVisibility(r, false); + assertEquals(ON_STOP, r.getLifecycleState()); + } + } + + @Test + @UiThreadTest + public void testLifecycleAfterFinished_OnCreate() throws Exception { + try (ClientMockSession clientSession = new ClientMockSession()) { + ActivityClientRecord r = clientSession.stubActivityRecord(); + + Activity activity = clientSession.launchActivity(r); + activity.finish(); + assertEquals(ON_CREATE, r.getLifecycleState()); + + clientSession.startActivity(r); + assertEquals(ON_CREATE, r.getLifecycleState()); + + clientSession.resumeActivity(r); + assertEquals(ON_CREATE, r.getLifecycleState()); + + clientSession.pauseActivity(r); + assertEquals(ON_CREATE, r.getLifecycleState()); + + clientSession.stopActivity(r); + assertEquals(ON_CREATE, r.getLifecycleState()); + + clientSession.destroyActivity(r); + assertEquals(ON_DESTROY, r.getLifecycleState()); + } + } + + @Test + @UiThreadTest + public void testLifecycleAfterFinished_OnStart() throws Exception { + try (ClientMockSession clientSession = new ClientMockSession()) { + ActivityClientRecord r = clientSession.stubActivityRecord(); + + Activity activity = clientSession.launchActivity(r); + clientSession.startActivity(r); + activity.finish(); + assertEquals(ON_START, r.getLifecycleState()); + + clientSession.resumeActivity(r); + assertEquals(ON_START, r.getLifecycleState()); + + clientSession.pauseActivity(r); + assertEquals(ON_START, r.getLifecycleState()); + + clientSession.stopActivity(r); + assertEquals(ON_STOP, r.getLifecycleState()); + + clientSession.destroyActivity(r); + assertEquals(ON_DESTROY, r.getLifecycleState()); + } + } + + @Test + @UiThreadTest + public void testLifecycleAfterFinished_OnResume() throws Exception { + try (ClientMockSession clientSession = new ClientMockSession()) { + ActivityClientRecord r = clientSession.stubActivityRecord(); + + Activity activity = clientSession.launchActivity(r); + clientSession.startActivity(r); + clientSession.resumeActivity(r); + activity.finish(); + assertEquals(ON_RESUME, r.getLifecycleState()); + + clientSession.pauseActivity(r); + assertEquals(ON_PAUSE, r.getLifecycleState()); + + clientSession.stopActivity(r); + assertEquals(ON_STOP, r.getLifecycleState()); + + clientSession.destroyActivity(r); + assertEquals(ON_DESTROY, r.getLifecycleState()); + } + } + + private class ClientMockSession implements AutoCloseable { + private MockitoSession mMockSession; + private ActivityThread mThread; + + private ClientMockSession() throws RemoteException { + mThread = ActivityThread.currentActivityThread(); + mMockSession = mockitoSession() + .strictness(Strictness.LENIENT) + .spyStatic(ActivityTaskManager.class) + .spyStatic(WindowManagerGlobal.class) + .startMocking(); + doReturn(Mockito.mock(WindowManagerGlobal.class)) + .when(WindowManagerGlobal::getInstance); + IActivityTaskManager mockAtm = Mockito.mock(IActivityTaskManager.class); + doReturn(mockAtm).when(ActivityTaskManager::getService); + when(mockAtm.finishActivity(any(), anyInt(), any(), anyInt())).thenReturn(true); + } + + private Activity launchActivity(ActivityClientRecord r) { + return mThread.handleLaunchActivity(r, null /* pendingActions */, + null /* customIntent */); + } + + private void startActivity(ActivityClientRecord r) { + mThread.handleStartActivity(r, null /* pendingActions */); + } + + private void resumeActivity(ActivityClientRecord r) { + mThread.handleResumeActivity(r.token, true /* finalStateRequest */, + true /* isForward */, "test"); + } + + private void pauseActivity(ActivityClientRecord r) { + mThread.handlePauseActivity(r.token, false /* finished */, + false /* userLeaving */, 0 /* configChanges */, null /* pendingActions */, + "test"); + } + + private void stopActivity(ActivityClientRecord r) { + mThread.handleStopActivity(r.token, false /* show */, 0 /* configChanges */, + new PendingTransactionActions(), false /* finalStateRequest */, "test"); + } + + private void destroyActivity(ActivityClientRecord r) { + mThread.handleDestroyActivity(r.token, true /* finishing */, 0 /* configChanges */, + false /* getNonConfigInstance */, "test"); + } + + private void changeVisibility(ActivityClientRecord r, boolean show) { + mThread.handleWindowVisibility(r.token, show); + } + + private ActivityClientRecord stubActivityRecord() { + ComponentName component = new ComponentName( + InstrumentationRegistry.getInstrumentation().getContext(), TestActivity.class); + ActivityInfo info = new ActivityInfo(); + info.packageName = component.getPackageName(); + info.name = component.getClassName(); + info.exported = true; + info.applicationInfo = new ApplicationInfo(); + info.applicationInfo.packageName = info.packageName; + info.applicationInfo.uid = UserHandle.myUserId(); + + return new ActivityClientRecord(new Binder(), Intent.makeMainActivity(component), + 0 /* ident */, info, new Configuration(), + CompatibilityInfo.DEFAULT_COMPATIBILITY_INFO, null /* referrer */, + null /* voiceInteractor */, null /* state */, null /* persistentState */, + null /* pendingResults */, null /* pendingNewIntents */, true /* isForward */, + null /* profilerInfo */, mThread /* client */); + } + + @Override + public void close() { + mMockSession.finishMocking(); + } + } + + // Test activity + public static class TestActivity extends Activity { + } +} diff --git a/data/etc/platform.xml b/data/etc/platform.xml index 0e957df4dc14..c54208b96031 100644 --- a/data/etc/platform.xml +++ b/data/etc/platform.xml @@ -180,6 +180,9 @@ <assign-permission name="android.permission.STATSCOMPANION" uid="statsd" /> <assign-permission name="android.permission.UPDATE_APP_OPS_STATS" uid="statsd" /> + <split-permission name="android.permission.ACCESS_FINE_LOCATION"> + <new-permission name="android.permission.ACCESS_COARSE_LOCATION" /> + </split-permission> <split-permission name="android.permission.WRITE_EXTERNAL_STORAGE"> <new-permission name="android.permission.READ_EXTERNAL_STORAGE" /> </split-permission> diff --git a/media/Android.bp b/media/Android.bp index 5b7b26cef27a..70dacb20cde7 100644 --- a/media/Android.bp +++ b/media/Android.bp @@ -20,10 +20,6 @@ java_library { ], }, - static_libs: [ - "mediaplayer2-protos", - ], - permitted_packages: [ "android.media", ], @@ -43,7 +39,6 @@ filegroup { name: "updatable-media-srcs", srcs: [ ":mediasession2-srcs", - ":mediaplayer2-srcs", ], } @@ -51,7 +46,6 @@ filegroup { name: "updatable-media-srcs-without-aidls", srcs : [ ":mediasession2-srcs-without-aidls", - ":mediaplayer2-srcs", ], } @@ -109,8 +103,7 @@ metalava_updatable_media_args = " --error UnhiddenSystemApi " + "--hide MissingPermission --hide BroadcastBehavior " + "--hide HiddenSuperclass --hide DeprecationMismatch --hide UnavailableSymbol " + "--hide SdkConstant --hide HiddenTypeParameter --hide Todo --hide Typo " + - "--hide HiddenTypedefConstant --show-annotation android.annotation.SystemApi " + - " --show-annotation android.annotation.TestApi " + "--hide HiddenTypedefConstant --show-annotation android.annotation.SystemApi " droidstubs { name: "updatable-media-stubs", diff --git a/media/apex/java/android/media/BufferingParams.java b/media/apex/java/android/media/BufferingParams.java index 83594d474b37..943f14277fc3 100644 --- a/media/apex/java/android/media/BufferingParams.java +++ b/media/apex/java/android/media/BufferingParams.java @@ -17,7 +17,6 @@ package android.media; import android.annotation.IntDef; -import android.annotation.TestApi; import android.os.Parcel; import android.os.Parcelable; @@ -64,7 +63,6 @@ import java.lang.annotation.RetentionPolicy; * <p>Users should use {@link Builder} to change {@link BufferingParams}. * @hide */ -@TestApi public final class BufferingParams implements Parcelable { private static final int BUFFERING_NO_MARK = -1; diff --git a/media/java/android/media/AudioManager.java b/media/java/android/media/AudioManager.java index d3471378b9d9..f8e43437d244 100644 --- a/media/java/android/media/AudioManager.java +++ b/media/java/android/media/AudioManager.java @@ -4523,6 +4523,7 @@ public class AudioManager { */ /** @hide */ + @TestApi @SystemApi public static final int SUCCESS = AudioSystem.SUCCESS; /** diff --git a/media/java/android/media/MiniThumbFile.java b/media/java/android/media/MiniThumbFile.java deleted file mode 100644 index f704acde7cf7..000000000000 --- a/media/java/android/media/MiniThumbFile.java +++ /dev/null @@ -1,334 +0,0 @@ -/* - * Copyright (C) 2009 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package android.media; - -import android.annotation.UnsupportedAppUsage; -import android.net.Uri; -import android.os.Build; -import android.os.Environment; -import android.util.Log; - -import dalvik.system.VMRuntime; - -import java.io.File; -import java.io.IOException; -import java.io.RandomAccessFile; -import java.nio.ByteBuffer; -import java.nio.channels.FileChannel; -import java.nio.channels.FileLock; -import java.util.Hashtable; - -/** - * This class handles the mini-thumb file. A mini-thumb file consists - * of blocks, indexed by id. Each block has BYTES_PER_MINTHUMB bytes in the - * following format: - * - * 1 byte status (0 = empty, 1 = mini-thumb available) - * 8 bytes magic (a magic number to match what's in the database) - * 4 bytes data length (LEN) - * LEN bytes jpeg data - * (the remaining bytes are unused) - * - * @hide This file is shared between MediaStore and MediaProvider and should remained internal use - * only. - * @deprecated thumbnails are now maintained in separate files, and this file - * format is no longer used. - */ -@Deprecated -public class MiniThumbFile { - private static final String TAG = "MiniThumbFile"; - private static final int MINI_THUMB_DATA_FILE_VERSION = 4; - public static final int BYTES_PER_MINTHUMB = 10000; - private static final int HEADER_SIZE = 1 + 8 + 4; - private Uri mUri; - private RandomAccessFile mMiniThumbFile; - private FileChannel mChannel; - private ByteBuffer mBuffer; - private ByteBuffer mEmptyBuffer; - private static final Hashtable<String, MiniThumbFile> sThumbFiles = - new Hashtable<String, MiniThumbFile>(); - - /** - * We store different types of thumbnails in different files. To remain backward compatibility, - * we should hashcode of content://media/external/images/media remains the same. - */ - @UnsupportedAppUsage - public static synchronized void reset() { - for (MiniThumbFile file : sThumbFiles.values()) { - file.deactivate(); - } - sThumbFiles.clear(); - } - - public static synchronized MiniThumbFile instance(Uri uri) { - if (VMRuntime.getRuntime().getTargetSdkVersion() >= Build.VERSION_CODES.Q) { - throw new UnsupportedOperationException(); - } - String type = uri.getPathSegments().get(1); - MiniThumbFile file = sThumbFiles.get(type); - // Log.v(TAG, "get minithumbfile for type: "+type); - if (file == null) { - file = new MiniThumbFile( - Uri.parse("content://media/external/" + type + "/media")); - sThumbFiles.put(type, file); - } - - return file; - } - - private String randomAccessFilePath(int version) { - String directoryName = - Environment.getExternalStorageDirectory().toString() - + "/DCIM/.thumbnails"; - return directoryName + "/.thumbdata" + version + "-" + mUri.hashCode(); - } - - private void removeOldFile() { - String oldPath = randomAccessFilePath(MINI_THUMB_DATA_FILE_VERSION - 1); - File oldFile = new File(oldPath); - if (oldFile.exists()) { - try { - oldFile.delete(); - } catch (SecurityException ex) { - // ignore - } - } - } - - private RandomAccessFile miniThumbDataFile() { - if (mMiniThumbFile == null) { - removeOldFile(); - String path = randomAccessFilePath(MINI_THUMB_DATA_FILE_VERSION); - File directory = new File(path).getParentFile(); - if (!directory.isDirectory()) { - if (!directory.mkdirs()) { - Log.e(TAG, "Unable to create .thumbnails directory " - + directory.toString()); - } - } - File f = new File(path); - try { - mMiniThumbFile = new RandomAccessFile(f, "rw"); - } catch (IOException ex) { - // Open as read-only so we can at least read the existing - // thumbnails. - try { - mMiniThumbFile = new RandomAccessFile(f, "r"); - } catch (IOException ex2) { - // ignore exception - } - } - if (mMiniThumbFile != null) { - mChannel = mMiniThumbFile.getChannel(); - } - } - return mMiniThumbFile; - } - - private MiniThumbFile(Uri uri) { - mUri = uri; - mBuffer = ByteBuffer.allocateDirect(BYTES_PER_MINTHUMB); - mEmptyBuffer = ByteBuffer.allocateDirect(BYTES_PER_MINTHUMB); - } - - public synchronized void deactivate() { - if (mMiniThumbFile != null) { - try { - mMiniThumbFile.close(); - mMiniThumbFile = null; - } catch (IOException ex) { - // ignore exception - } - } - } - - // Get the magic number for the specified id in the mini-thumb file. - // Returns 0 if the magic is not available. - public synchronized long getMagic(long id) { - // check the mini thumb file for the right data. Right is - // defined as having the right magic number at the offset - // reserved for this "id". - RandomAccessFile r = miniThumbDataFile(); - if (r != null) { - long pos = id * BYTES_PER_MINTHUMB; - FileLock lock = null; - try { - mBuffer.clear(); - mBuffer.limit(1 + 8); - - lock = mChannel.lock(pos, 1 + 8, true); - // check that we can read the following 9 bytes - // (1 for the "status" and 8 for the long) - if (mChannel.read(mBuffer, pos) == 9) { - mBuffer.position(0); - if (mBuffer.get() == 1) { - return mBuffer.getLong(); - } - } - } catch (IOException ex) { - Log.v(TAG, "Got exception checking file magic: ", ex); - } catch (RuntimeException ex) { - // Other NIO related exception like disk full, read only channel..etc - Log.e(TAG, "Got exception when reading magic, id = " + id + - ", disk full or mount read-only? " + ex.getClass()); - } finally { - try { - if (lock != null) lock.release(); - } - catch (IOException ex) { - // ignore it. - } - } - } - return 0; - } - - public synchronized void eraseMiniThumb(long id) { - RandomAccessFile r = miniThumbDataFile(); - if (r != null) { - long pos = id * BYTES_PER_MINTHUMB; - FileLock lock = null; - try { - mBuffer.clear(); - mBuffer.limit(1 + 8); - - lock = mChannel.lock(pos, BYTES_PER_MINTHUMB, false); - // check that we can read the following 9 bytes - // (1 for the "status" and 8 for the long) - if (mChannel.read(mBuffer, pos) == 9) { - mBuffer.position(0); - if (mBuffer.get() == 1) { - long currentMagic = mBuffer.getLong(); - if (currentMagic == 0) { - // there is no thumbnail stored here - Log.i(TAG, "no thumbnail for id " + id); - return; - } - // zero out the thumbnail slot - // Log.v(TAG, "clearing slot " + id + ", magic " + currentMagic - // + " at offset " + pos); - mChannel.write(mEmptyBuffer, pos); - } - } else { - // Log.v(TAG, "No slot"); - } - } catch (IOException ex) { - Log.v(TAG, "Got exception checking file magic: ", ex); - } catch (RuntimeException ex) { - // Other NIO related exception like disk full, read only channel..etc - Log.e(TAG, "Got exception when reading magic, id = " + id + - ", disk full or mount read-only? " + ex.getClass()); - } finally { - try { - if (lock != null) lock.release(); - } - catch (IOException ex) { - // ignore it. - } - } - } else { - // Log.v(TAG, "No data file"); - } - } - - public synchronized void saveMiniThumbToFile(byte[] data, long id, long magic) - throws IOException { - RandomAccessFile r = miniThumbDataFile(); - if (r == null) return; - - long pos = id * BYTES_PER_MINTHUMB; - FileLock lock = null; - try { - if (data != null) { - if (data.length > BYTES_PER_MINTHUMB - HEADER_SIZE) { - // not enough space to store it. - return; - } - mBuffer.clear(); - mBuffer.put((byte) 1); - mBuffer.putLong(magic); - mBuffer.putInt(data.length); - mBuffer.put(data); - mBuffer.flip(); - - lock = mChannel.lock(pos, BYTES_PER_MINTHUMB, false); - mChannel.write(mBuffer, pos); - } - } catch (IOException ex) { - Log.e(TAG, "couldn't save mini thumbnail data for " - + id + "; ", ex); - throw ex; - } catch (RuntimeException ex) { - // Other NIO related exception like disk full, read only channel..etc - Log.e(TAG, "couldn't save mini thumbnail data for " - + id + "; disk full or mount read-only? " + ex.getClass()); - } finally { - try { - if (lock != null) lock.release(); - } - catch (IOException ex) { - // ignore it. - } - } - } - - /** - * Gallery app can use this method to retrieve mini-thumbnail. Full size - * images share the same IDs with their corresponding thumbnails. - * - * @param id the ID of the image (same of full size image). - * @param data the buffer to store mini-thumbnail. - */ - public synchronized byte [] getMiniThumbFromFile(long id, byte [] data) { - RandomAccessFile r = miniThumbDataFile(); - if (r == null) return null; - - long pos = id * BYTES_PER_MINTHUMB; - FileLock lock = null; - try { - mBuffer.clear(); - lock = mChannel.lock(pos, BYTES_PER_MINTHUMB, true); - int size = mChannel.read(mBuffer, pos); - if (size > 1 + 8 + 4) { // flag, magic, length - mBuffer.position(0); - byte flag = mBuffer.get(); - long magic = mBuffer.getLong(); - int length = mBuffer.getInt(); - - if (size >= 1 + 8 + 4 + length && length != 0 && magic != 0 && flag == 1 && - data.length >= length) { - mBuffer.get(data, 0, length); - return data; - } - } - } catch (IOException ex) { - Log.w(TAG, "got exception when reading thumbnail id=" + id + ", exception: " + ex); - } catch (RuntimeException ex) { - // Other NIO related exception like disk full, read only channel..etc - Log.e(TAG, "Got exception when reading thumbnail, id = " + id + - ", disk full or mount read-only? " + ex.getClass()); - } finally { - try { - if (lock != null) lock.release(); - } - catch (IOException ex) { - // ignore it. - } - } - return null; - } -} diff --git a/media/java/android/media/RingtoneManager.java b/media/java/android/media/RingtoneManager.java index fefb0d7a55bf..e207721f1918 100644 --- a/media/java/android/media/RingtoneManager.java +++ b/media/java/android/media/RingtoneManager.java @@ -495,15 +495,17 @@ public class RingtoneManager { if (mCursor == null || !mCursor.moveToPosition(position)) { return null; } - - return getUriFromCursor(mCursor); + + return getUriFromCursor(mContext, mCursor); } - private static Uri getUriFromCursor(Cursor cursor) { - return ContentUris.withAppendedId(Uri.parse(cursor.getString(URI_COLUMN_INDEX)), cursor - .getLong(ID_COLUMN_INDEX)); + private static Uri getUriFromCursor(Context context, Cursor cursor) { + final Uri uri = ContentUris.withAppendedId(Uri.parse(cursor.getString(URI_COLUMN_INDEX)), + cursor.getLong(ID_COLUMN_INDEX)); + final Uri canonicalized = context.getContentResolver().canonicalize(uri); + return (canonicalized != null) ? canonicalized : uri; } - + /** * Gets the position of a {@link Uri} within this {@link RingtoneManager}. * @@ -569,7 +571,7 @@ public class RingtoneManager { Uri uri = null; if (cursor.moveToFirst()) { - uri = getUriFromCursor(cursor); + uri = getUriFromCursor(context, cursor); } cursor.close(); diff --git a/media/java/android/media/audiopolicy/AudioPolicy.java b/media/java/android/media/audiopolicy/AudioPolicy.java index 1cd60f78886e..39474e13a2d0 100644 --- a/media/java/android/media/audiopolicy/AudioPolicy.java +++ b/media/java/android/media/audiopolicy/AudioPolicy.java @@ -416,6 +416,7 @@ public class AudioPolicy { * @param devices list of devices to which the audio stream of the application may be routed. * @return true if the change was successful, false otherwise. */ + @TestApi @SystemApi public boolean setUidDeviceAffinity(int uid, @NonNull List<AudioDeviceInfo> devices) { if (devices == null) { @@ -457,6 +458,7 @@ public class AudioPolicy { * @param uid UID of the application affected. * @return true if the change was successful, false otherwise. */ + @TestApi @SystemApi public boolean removeUidDeviceAffinity(int uid) { synchronized (mLock) { diff --git a/packages/CarrierDefaultApp/src/com/android/carrierdefaultapp/CaptivePortalLoginActivity.java b/packages/CarrierDefaultApp/src/com/android/carrierdefaultapp/CaptivePortalLoginActivity.java index 55c9361ce6c4..81c5bcd47981 100644 --- a/packages/CarrierDefaultApp/src/com/android/carrierdefaultapp/CaptivePortalLoginActivity.java +++ b/packages/CarrierDefaultApp/src/com/android/carrierdefaultapp/CaptivePortalLoginActivity.java @@ -52,6 +52,7 @@ import android.widget.TextView; import com.android.internal.telephony.PhoneConstants; import com.android.internal.telephony.TelephonyIntents; import com.android.internal.util.ArrayUtils; +import com.android.internal.util.TrafficStatsConstants; import java.io.IOException; import java.lang.reflect.Field; @@ -238,7 +239,8 @@ public class CaptivePortalLoginActivity extends Activity { if (isFinishing() || isDestroyed()) return; HttpURLConnection urlConnection = null; int httpResponseCode = 500; - int oldTag = TrafficStats.getAndSetThreadStatsTag(TrafficStats.TAG_SYSTEM_PROBE); + int oldTag = TrafficStats.getAndSetThreadStatsTag( + TrafficStatsConstants.TAG_SYSTEM_PROBE); try { urlConnection = (HttpURLConnection) mNetwork.openConnection( new URL(mCm.getCaptivePortalServerUrl())); diff --git a/packages/ExtServices/src/android/ext/services/notification/Assistant.java b/packages/ExtServices/src/android/ext/services/notification/Assistant.java index b2baff5db75b..7860f36e7d95 100644 --- a/packages/ExtServices/src/android/ext/services/notification/Assistant.java +++ b/packages/ExtServices/src/android/ext/services/notification/Assistant.java @@ -238,7 +238,7 @@ public class Assistant extends NotificationAssistantService { } mSingleThreadExecutor.submit(() -> { NotificationEntry entry = - new NotificationEntry(mPackageManager, sbn, channel, mSmsHelper); + new NotificationEntry(mPackageManager, sbn.cloneLight(), channel, mSmsHelper); SmartActionsHelper.SmartSuggestions suggestions = mSmartActionsHelper.suggest(entry); if (DEBUG) { Log.d(TAG, String.format( @@ -296,7 +296,7 @@ public class Assistant extends NotificationAssistantService { Ranking ranking = getRanking(sbn.getKey(), rankingMap); if (ranking != null && ranking.getChannel() != null) { NotificationEntry entry = new NotificationEntry(mPackageManager, - sbn, ranking.getChannel(), mSmsHelper); + sbn.cloneLight(), ranking.getChannel(), mSmsHelper); String key = getKey( sbn.getPackageName(), sbn.getUserId(), ranking.getChannel().getId()); ChannelImpressions ci = mkeyToImpressions.getOrDefault(key, diff --git a/packages/ExtServices/src/android/ext/services/notification/SmartActionsHelper.java b/packages/ExtServices/src/android/ext/services/notification/SmartActionsHelper.java index 0d687d41260d..10360a34546c 100644 --- a/packages/ExtServices/src/android/ext/services/notification/SmartActionsHelper.java +++ b/packages/ExtServices/src/android/ext/services/notification/SmartActionsHelper.java @@ -338,7 +338,7 @@ public class SmartActionsHelper { createTextClassifierEventBuilder( TextClassifierEvent.TYPE_SMART_ACTION, session.resultId) .setEntityTypes(ConversationAction.TYPE_TEXT_REPLY) - .setScore(session.repliesScores.getOrDefault(reply, 0f)) + .setScores(session.repliesScores.getOrDefault(reply, 0f)) .build(); mTextClassifier.onTextClassifierEvent(textClassifierEvent); } @@ -381,11 +381,9 @@ public class SmartActionsHelper { .build(); } - private TextClassifierEvent.Builder createTextClassifierEventBuilder( + private TextClassifierEvent.ConversationActionsEvent.Builder createTextClassifierEventBuilder( int eventType, String resultId) { - return new TextClassifierEvent.Builder( - TextClassifierEvent.CATEGORY_CONVERSATION_ACTIONS, eventType) - .setEventTime(System.currentTimeMillis()) + return new TextClassifierEvent.ConversationActionsEvent.Builder(eventType) .setEventContext( new TextClassificationContext.Builder( mContext.getPackageName(), TextClassifier.WIDGET_TYPE_NOTIFICATION) diff --git a/packages/ExtServices/tests/src/android/ext/services/notification/SmartActionsHelperTest.java b/packages/ExtServices/tests/src/android/ext/services/notification/SmartActionsHelperTest.java index ebcaee87d6d8..dfa1ea0aac90 100644 --- a/packages/ExtServices/tests/src/android/ext/services/notification/SmartActionsHelperTest.java +++ b/packages/ExtServices/tests/src/android/ext/services/notification/SmartActionsHelperTest.java @@ -330,7 +330,9 @@ public class SmartActionsHelperTest { List<TextClassifierEvent> events = argumentCaptor.getAllValues(); assertTextClassifierEvent(events.get(0), TextClassifierEvent.TYPE_ACTIONS_GENERATED); assertTextClassifierEvent(events.get(1), TextClassifierEvent.TYPE_SMART_ACTION); - assertThat(events.get(1).getScore()).isEqualTo(SCORE); + float[] scores = events.get(1).getScores(); + assertThat(scores).hasLength(1); + assertThat(scores[0]).isEqualTo(SCORE); } @Test diff --git a/packages/NetworkStack/src/android/net/dhcp/DhcpClient.java b/packages/NetworkStack/src/android/net/dhcp/DhcpClient.java index 64adc0d48309..af0e3bb8bfc2 100644 --- a/packages/NetworkStack/src/android/net/dhcp/DhcpClient.java +++ b/packages/NetworkStack/src/android/net/dhcp/DhcpClient.java @@ -65,6 +65,7 @@ import com.android.internal.util.HexDump; import com.android.internal.util.MessageUtils; import com.android.internal.util.State; import com.android.internal.util.StateMachine; +import com.android.internal.util.TrafficStatsConstants; import com.android.internal.util.WakeupMessage; import java.io.FileDescriptor; @@ -329,7 +330,8 @@ public class DhcpClient extends StateMachine { } private boolean initUdpSocket() { - final int oldTag = TrafficStats.getAndSetThreadStatsTag(TrafficStats.TAG_SYSTEM_DHCP); + final int oldTag = TrafficStats.getAndSetThreadStatsTag( + TrafficStatsConstants.TAG_SYSTEM_DHCP); try { mUdpSock = Os.socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP); SocketUtils.bindSocketToInterface(mUdpSock, mIfaceName); diff --git a/packages/NetworkStack/src/android/net/dhcp/DhcpServer.java b/packages/NetworkStack/src/android/net/dhcp/DhcpServer.java index 8832eaadf6cb..d21b5f7853bb 100644 --- a/packages/NetworkStack/src/android/net/dhcp/DhcpServer.java +++ b/packages/NetworkStack/src/android/net/dhcp/DhcpServer.java @@ -16,7 +16,6 @@ package android.net.dhcp; -import static android.net.TrafficStats.TAG_SYSTEM_DHCP_SERVER; import static android.net.dhcp.DhcpPacket.DHCP_CLIENT; import static android.net.dhcp.DhcpPacket.DHCP_HOST_NAME; import static android.net.dhcp.DhcpPacket.DHCP_SERVER; @@ -33,6 +32,7 @@ import static android.system.OsConstants.SOL_SOCKET; import static android.system.OsConstants.SO_BROADCAST; import static android.system.OsConstants.SO_REUSEADDR; +import static com.android.internal.util.TrafficStatsConstants.TAG_SYSTEM_DHCP_SERVER; import static com.android.server.util.NetworkStackConstants.INFINITE_LEASE; import static com.android.server.util.NetworkStackConstants.IPV4_ADDR_ALL; import static com.android.server.util.NetworkStackConstants.IPV4_ADDR_ANY; diff --git a/packages/NetworkStack/src/com/android/server/connectivity/NetworkMonitor.java b/packages/NetworkStack/src/com/android/server/connectivity/NetworkMonitor.java index 2a612503620f..27d420328017 100644 --- a/packages/NetworkStack/src/com/android/server/connectivity/NetworkMonitor.java +++ b/packages/NetworkStack/src/com/android/server/connectivity/NetworkMonitor.java @@ -98,6 +98,7 @@ import com.android.internal.annotations.VisibleForTesting; import com.android.internal.util.RingBufferIndices; import com.android.internal.util.State; import com.android.internal.util.StateMachine; +import com.android.internal.util.TrafficStatsConstants; import com.android.networkstack.R; import com.android.networkstack.metrics.DataStallDetectionStats; import com.android.networkstack.metrics.DataStallStatsUtils; @@ -1478,7 +1479,8 @@ public class NetworkMonitor extends StateMachine { int httpResponseCode = CaptivePortalProbeResult.FAILED_CODE; String redirectUrl = null; final Stopwatch probeTimer = new Stopwatch().start(); - final int oldTag = TrafficStats.getAndSetThreadStatsTag(TrafficStats.TAG_SYSTEM_PROBE); + final int oldTag = TrafficStats.getAndSetThreadStatsTag( + TrafficStatsConstants.TAG_SYSTEM_PROBE); try { urlConnection = (HttpURLConnection) mNetwork.openConnection(url); urlConnection.setInstanceFollowRedirects(probeType == ValidationProbeEvent.PROBE_PAC); diff --git a/packages/SettingsLib/src/com/android/settingslib/graph/ThemedBatteryDrawable.kt b/packages/SettingsLib/src/com/android/settingslib/graph/ThemedBatteryDrawable.kt index eff02d24431e..257943e16149 100644 --- a/packages/SettingsLib/src/com/android/settingslib/graph/ThemedBatteryDrawable.kt +++ b/packages/SettingsLib/src/com/android/settingslib/graph/ThemedBatteryDrawable.kt @@ -83,6 +83,8 @@ open class ThemedBatteryDrawable(private val context: Context, frameColor: Int) // Dual tone implies that battery level is a clipped overlay over top of the whole shape private var dualTone = false + private var batteryLevel = 0 + private val invalidateRunnable: () -> Unit = { invalidateSelf() } @@ -177,9 +179,9 @@ open class ThemedBatteryDrawable(private val context: Context, frameColor: Int) unifiedPath.reset() levelPath.reset() levelRect.set(fillRect) - val fillFraction = level / 100f + val fillFraction = batteryLevel / 100f val fillTop = - if (level >= 95) + if (batteryLevel >= 95) fillRect.top else fillRect.top + (fillRect.height() * (1 - fillFraction)) @@ -223,7 +225,7 @@ open class ThemedBatteryDrawable(private val context: Context, frameColor: Int) fillPaint.color = levelColor // Show colorError below this level - if (level <= Companion.CRITICAL_LEVEL && !charging) { + if (batteryLevel <= Companion.CRITICAL_LEVEL && !charging) { c.save() c.clipPath(scaledFill) c.drawPath(levelPath, fillPaint) @@ -310,13 +312,13 @@ open class ThemedBatteryDrawable(private val context: Context, frameColor: Int) */ public open fun setBatteryLevel(l: Int) { invertFillIcon = if (l >= 67) true else if (l <= 33) false else invertFillIcon - level = l - levelColor = batteryColorForLevel(level) + batteryLevel = l + levelColor = batteryColorForLevel(batteryLevel) invalidateSelf() } public fun getBatteryLevel(): Int { - return level + return batteryLevel } override fun onBoundsChange(bounds: Rect?) { @@ -343,7 +345,7 @@ open class ThemedBatteryDrawable(private val context: Context, frameColor: Int) dualToneBackgroundFill.color = bgColor // Also update the level color, since fillColor may have changed - levelColor = batteryColorForLevel(level) + levelColor = batteryColorForLevel(batteryLevel) invalidateSelf() } diff --git a/packages/SettingsLib/src/com/android/settingslib/wifi/AccessPoint.java b/packages/SettingsLib/src/com/android/settingslib/wifi/AccessPoint.java index 1976ec45bf45..d1e4fdf472c0 100644 --- a/packages/SettingsLib/src/com/android/settingslib/wifi/AccessPoint.java +++ b/packages/SettingsLib/src/com/android/settingslib/wifi/AccessPoint.java @@ -337,6 +337,7 @@ public class AccessPoint implements Comparable<AccessPoint> { mContext = context; networkId = config.networkId; mConfig = config; + mFqdn = config.FQDN; setScanResultsPasspoint(homeScans, roamingScans); updateKey(); } @@ -673,6 +674,13 @@ public class AccessPoint implements Comparable<AccessPoint> { return mKey; } + /** + * Determines if the other AccessPoint represents the same network as this AccessPoint + */ + public boolean matches(AccessPoint other) { + return getKey().equals(other.getKey()); + } + public boolean matches(WifiConfiguration config) { if (config.isPasspoint()) { return (isPasspoint() && config.FQDN.equals(mConfig.FQDN)); diff --git a/packages/SystemUI/res-keyguard/drawable-xxxhdpi/stretch_thumbnail.png b/packages/SystemUI/res-keyguard/drawable-xxxhdpi/analog_thumbnail.png Binary files differindex 83d714bfcb05..83d714bfcb05 100644 --- a/packages/SystemUI/res-keyguard/drawable-xxxhdpi/stretch_thumbnail.png +++ b/packages/SystemUI/res-keyguard/drawable-xxxhdpi/analog_thumbnail.png diff --git a/packages/SystemUI/res-keyguard/drawable/analog_frame.xml b/packages/SystemUI/res-keyguard/drawable/analog_frame.xml new file mode 100644 index 000000000000..a663ac826127 --- /dev/null +++ b/packages/SystemUI/res-keyguard/drawable/analog_frame.xml @@ -0,0 +1,7 @@ +<vector xmlns:android="http://schemas.android.com/apk/res/android" + android:height="250dp" + android:width="250dp" + android:viewportHeight="380" + android:viewportWidth="380"> + <path android:fillColor="#000000" android:pathData="M190,190m0,2a2,2 0,1 1,0 -4a2,2 0,1 1,0 4"/> +</vector> diff --git a/packages/SystemUI/res-keyguard/drawable/analog_hour_hand.xml b/packages/SystemUI/res-keyguard/drawable/analog_hour_hand.xml new file mode 100644 index 000000000000..c7b6d60b319f --- /dev/null +++ b/packages/SystemUI/res-keyguard/drawable/analog_hour_hand.xml @@ -0,0 +1,7 @@ +<vector xmlns:android="http://schemas.android.com/apk/res/android" + android:height="250dp" + android:width="250dp" + android:viewportHeight="380" + android:viewportWidth="380"> + <path android:fillColor="#777777" android:fillType="evenOdd" android:pathData="M203,190C203,185.398 200.608,181.354 197,179.044L197,58C197,54.134 193.866,51 190,51C186.134,51 183,54.134 183,58L183,179.043C179.392,181.354 177,185.397 177,190C177,197.18 182.82,203 190,203C197.18,203 203,197.18 203,190Z"/> +</vector> diff --git a/packages/SystemUI/res-keyguard/drawable/analog_minute_hand.xml b/packages/SystemUI/res-keyguard/drawable/analog_minute_hand.xml new file mode 100644 index 000000000000..458275bec23a --- /dev/null +++ b/packages/SystemUI/res-keyguard/drawable/analog_minute_hand.xml @@ -0,0 +1,7 @@ +<vector xmlns:android="http://schemas.android.com/apk/res/android" + android:height="250dp" + android:width="250dp" + android:viewportHeight="380" + android:viewportWidth="380"> + <path android:fillColor="#FFFFFF" android:pathData="M192,182.252C195.45,183.14 198,186.272 198,190C198,194.418 194.418,198 190,198C185.582,198 182,194.418 182,190C182,186.272 184.55,183.14 188,182.252L188,10C188,8.895 188.895,8 190,8C191.105,8 192,8.895 192,10L192,182.252Z"/> +</vector> diff --git a/packages/SystemUI/res-keyguard/layout/stretchanalog_clock.xml b/packages/SystemUI/res-keyguard/layout/analog_clock.xml index dd25df864733..cf6d35ee27af 100644 --- a/packages/SystemUI/res-keyguard/layout/stretchanalog_clock.xml +++ b/packages/SystemUI/res-keyguard/layout/analog_clock.xml @@ -35,9 +35,27 @@ android:format24Hour="@string/keyguard_widget_24_hours_format" android:elegantTextHeight="false" /> - <com.android.keyguard.clock.StretchAnalogClock + <com.android.keyguard.clock.ImageClock android:id="@+id/analog_clock" - android:layout_width="match_parent" - android:layout_height="match_parent" - /> + android:layout_width="wrap_content" + android:layout_height="wrap_content" + > + <ImageView + android:id="@+id/hour_hand" + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:src="@drawable/analog_hour_hand" + /> + <ImageView + android:id="@+id/minute_hand" + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:src="@drawable/analog_minute_hand" + /> + <ImageView + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:src="@drawable/analog_frame" + /> + </com.android.keyguard.clock.ImageClock> </com.android.keyguard.clock.ClockLayout> diff --git a/packages/SystemUI/res-keyguard/values/strings.xml b/packages/SystemUI/res-keyguard/values/strings.xml index dc985cc8fdbd..1e98189fa238 100644 --- a/packages/SystemUI/res-keyguard/values/strings.xml +++ b/packages/SystemUI/res-keyguard/values/strings.xml @@ -413,6 +413,6 @@ number">%d</xliff:g> remaining attempts before SIM becomes permanently unusable. <string name="clock_title_bubble" translatable="false">Bubble</string> <!-- Title for Stretch clock face that will appear in the picker app next to a preview image of the clock face. [CHAR LIMIT=8] --> - <string name="clock_title_stretch" translatable="false">Stretch</string> + <string name="clock_title_analog" translatable="false">Analog</string> </resources> diff --git a/packages/SystemUI/res/values/strings.xml b/packages/SystemUI/res/values/strings.xml index 444cabfcc50e..f121c8ec5de0 100644 --- a/packages/SystemUI/res/values/strings.xml +++ b/packages/SystemUI/res/values/strings.xml @@ -1331,6 +1331,14 @@ <!-- Content description for accessibility: Clear the odi caption tool tip. [CHAR LIMIT=NONE] --> <string name="accessibility_volume_close_odi_captions_tip">Close captions tip</string> + <!-- Content description for accessibility: Captions button. [CHAR LIMIT=NONE] --> + <string name="volume_odi_captions_content_description">Captions overlay</string> + + <!-- Content description for accessibility: Hint if click will enable. [CHAR LIMIT=NONE] --> + <string name="volume_odi_captions_hint_enable">enable</string> + <!-- Content description for accessibility: Hint if click will disable. [CHAR LIMIT=NONE] --> + <string name="volume_odi_captions_hint_disable">disable</string> + <!-- content description for audio output chooser [CHAR LIMIT=NONE]--> <string name="accessibility_output_chooser">Switch output device</string> diff --git a/packages/SystemUI/src/com/android/keyguard/CarrierTextController.java b/packages/SystemUI/src/com/android/keyguard/CarrierTextController.java index 64517bae6d6b..eca39262dfb5 100644 --- a/packages/SystemUI/src/com/android/keyguard/CarrierTextController.java +++ b/packages/SystemUI/src/com/android/keyguard/CarrierTextController.java @@ -16,14 +16,22 @@ package com.android.keyguard; +import static android.telephony.PhoneStateListener.LISTEN_ACTIVE_DATA_SUBSCRIPTION_ID_CHANGE; +import static android.telephony.PhoneStateListener.LISTEN_NONE; + +import static com.android.internal.telephony.PhoneConstants.MAX_PHONE_COUNT_DUAL_SIM; + import android.content.Context; import android.content.Intent; import android.content.IntentFilter; import android.net.ConnectivityManager; import android.net.wifi.WifiManager; import android.os.Handler; +import android.telephony.CarrierConfigManager; +import android.telephony.PhoneStateListener; import android.telephony.ServiceState; import android.telephony.SubscriptionInfo; +import android.telephony.SubscriptionManager; import android.telephony.TelephonyManager; import android.text.TextUtils; import android.util.Log; @@ -36,6 +44,7 @@ import com.android.settingslib.WirelessUtils; import com.android.systemui.Dependency; import com.android.systemui.keyguard.WakefulnessLifecycle; +import java.util.ArrayList; import java.util.List; import java.util.Objects; @@ -113,6 +122,17 @@ public class CarrierTextController { } }; + private int mActiveMobileDataSubscription = SubscriptionManager.INVALID_SUBSCRIPTION_ID; + private PhoneStateListener mPhoneStateListener = new PhoneStateListener() { + @Override + public void onActiveDataSubscriptionIdChanged(int subId) { + mActiveMobileDataSubscription = subId; + if (mKeyguardUpdateMonitor != null) { + updateCarrierText(); + } + } + }; + /** * The status of this lock screen. Primarily used for widgets on LockScreen. */ @@ -200,6 +220,8 @@ public class CarrierTextController { * @param callback Callback to provide text updates */ public void setListening(CarrierTextCallback callback) { + TelephonyManager telephonyManager = ((TelephonyManager) mContext + .getSystemService(Context.TELEPHONY_SERVICE)); if (callback != null) { mCarrierTextCallback = callback; if (ConnectivityManager.from(mContext).isNetworkSupported( @@ -207,6 +229,8 @@ public class CarrierTextController { mKeyguardUpdateMonitor = KeyguardUpdateMonitor.getInstance(mContext); mKeyguardUpdateMonitor.registerCallback(mCallback); mWakefulnessLifecycle.addObserver(mWakefulnessObserver); + telephonyManager.listen(mPhoneStateListener, + LISTEN_ACTIVE_DATA_SUBSCRIPTION_ID_CHANGE); } else { // Don't listen and clear out the text when the device isn't a phone. mKeyguardUpdateMonitor = null; @@ -218,6 +242,35 @@ public class CarrierTextController { mKeyguardUpdateMonitor.removeCallback(mCallback); mWakefulnessLifecycle.removeObserver(mWakefulnessObserver); } + telephonyManager.listen(mPhoneStateListener, LISTEN_NONE); + } + } + + /** + * STOPSHIP(b/130246708) remove when no longer needed for testing purpose. + * @param subscriptions + */ + private void filterMobileSubscriptionInSameGroup(List<SubscriptionInfo> subscriptions) { + if (subscriptions.size() == MAX_PHONE_COUNT_DUAL_SIM) { + SubscriptionInfo info1 = subscriptions.get(0); + SubscriptionInfo info2 = subscriptions.get(1); + if (info1.getGroupUuid() != null && info1.getGroupUuid().equals(info2.getGroupUuid())) { + // If both subscriptions are primary, show both. + if (!info1.isOpportunistic() && !info2.isOpportunistic()) return; + + // If carrier required, always show signal bar of primary subscription. + // Otherwise, show whichever subscription is currently active for Internet. + boolean alwaysShowPrimary = CarrierConfigManager.getDefaultConfig() + .getBoolean(CarrierConfigManager + .KEY_ALWAYS_SHOW_PRIMARY_SIGNAL_BAR_IN_OPPORTUNISTIC_NETWORK_BOOLEAN); + if (alwaysShowPrimary) { + subscriptions.remove(info1.isOpportunistic() ? info1 : info2); + } else { + subscriptions.remove(info1.getSubscriptionId() == mActiveMobileDataSubscription + ? info2 : info1); + } + + } } } @@ -226,7 +279,17 @@ public class CarrierTextController { boolean anySimReadyAndInService = false; CharSequence displayText = null; - List<SubscriptionInfo> subs = mKeyguardUpdateMonitor.getSubscriptionInfo(false); + // STOPSHIP(b/130246708) revert to mKeyguardUpdateMonitor.getSubscriptionInfo(false). + SubscriptionManager subscriptionManager = ((SubscriptionManager) mContext.getSystemService( + Context.TELEPHONY_SUBSCRIPTION_SERVICE)); + List<SubscriptionInfo> subs = subscriptionManager.getActiveSubscriptionInfoList(false); + + if (subs == null) { + subs = new ArrayList<>(); + } else { + filterMobileSubscriptionInSameGroup(subs); + } + final int numSubs = subs.size(); final int[] subsIds = new int[numSubs]; // This array will contain in position i, the index of subscription in slot ID i. @@ -311,20 +374,23 @@ public class CarrierTextController { displayText = updateCarrierTextWithSimIoError(displayText, carrierNames, subOrderBySlot, allSimsMissing); + boolean airplaneMode = false; // APM (airplane mode) != no carrier state. There are carrier services // (e.g. WFC = Wi-Fi calling) which may operate in APM. if (!anySimReadyAndInService && WirelessUtils.isAirplaneModeOn(mContext)) { displayText = getAirplaneModeMessage(); + airplaneMode = true; } - if (TextUtils.isEmpty(displayText)) { + if (TextUtils.isEmpty(displayText) && !airplaneMode) { displayText = TextUtils.join(mSeparator, carrierNames); } final CarrierTextCallbackInfo info = new CarrierTextCallbackInfo( displayText, carrierNames, !allSimsMissing, - subsIds); + subsIds, + airplaneMode); postToCallback(info); } @@ -525,14 +591,22 @@ public class CarrierTextController { public final CharSequence[] listOfCarriers; public final boolean anySimReady; public final int[] subscriptionIds; + public boolean airplaneMode; @VisibleForTesting public CarrierTextCallbackInfo(CharSequence carrierText, CharSequence[] listOfCarriers, boolean anySimReady, int[] subscriptionIds) { + this(carrierText, listOfCarriers, anySimReady, subscriptionIds, false); + } + + @VisibleForTesting + public CarrierTextCallbackInfo(CharSequence carrierText, CharSequence[] listOfCarriers, + boolean anySimReady, int[] subscriptionIds, boolean airplaneMode) { this.carrierText = carrierText; this.listOfCarriers = listOfCarriers; this.anySimReady = anySimReady; this.subscriptionIds = subscriptionIds; + this.airplaneMode = airplaneMode; } } diff --git a/packages/SystemUI/src/com/android/keyguard/clock/StretchAnalogClockController.java b/packages/SystemUI/src/com/android/keyguard/clock/AnalogClockController.java index 81b6a600f398..1652121f350d 100644 --- a/packages/SystemUI/src/com/android/keyguard/clock/StretchAnalogClockController.java +++ b/packages/SystemUI/src/com/android/keyguard/clock/AnalogClockController.java @@ -35,7 +35,7 @@ import java.util.TimeZone; /** * Controller for Stretch clock that can appear on lock screen and AOD. */ -public class StretchAnalogClockController implements ClockPlugin { +public class AnalogClockController implements ClockPlugin { /** * Resources used to get title and thumbnail. @@ -60,9 +60,9 @@ public class StretchAnalogClockController implements ClockPlugin { /** * Custom clock shown on AOD screen and behind stack scroller on lock. */ - private View mBigClockView; + private ClockLayout mBigClockView; private TextClock mDigitalClock; - private StretchAnalogClock mAnalogClock; + private ImageClock mAnalogClock; /** * Small clock shown on lock screen above stack scroller. @@ -82,7 +82,7 @@ public class StretchAnalogClockController implements ClockPlugin { * @param inflater Inflater used to inflate custom clock views. * @param colorExtractor Extracts accent color from wallpaper. */ - public StretchAnalogClockController(Resources res, LayoutInflater inflater, + public AnalogClockController(Resources res, LayoutInflater inflater, SysuiColorExtractor colorExtractor) { mResources = res; mLayoutInflater = inflater; @@ -90,7 +90,7 @@ public class StretchAnalogClockController implements ClockPlugin { } private void createViews() { - mBigClockView = mLayoutInflater.inflate(R.layout.stretchanalog_clock, null); + mBigClockView = (ClockLayout) mLayoutInflater.inflate(R.layout.analog_clock, null); mAnalogClock = mBigClockView.findViewById(R.id.analog_clock); mDigitalClock = mBigClockView.findViewById(R.id.digital_clock); @@ -114,17 +114,17 @@ public class StretchAnalogClockController implements ClockPlugin { @Override public String getName() { - return "stretch"; + return "analog"; } @Override public String getTitle() { - return mResources.getString(R.string.clock_title_stretch); + return mResources.getString(R.string.clock_title_analog); } @Override public Bitmap getThumbnail() { - return BitmapFactory.decodeResource(mResources, R.drawable.stretch_thumbnail); + return BitmapFactory.decodeResource(mResources, R.drawable.analog_thumbnail); } @Override @@ -175,13 +175,14 @@ public class StretchAnalogClockController implements ClockPlugin { } final int length = colorPalette.length; mDigitalClock.setTextColor(colorPalette[Math.max(0, length - 5)]); - mAnalogClock.setClockColor(colorPalette[Math.max(0, length - 5)], + mAnalogClock.setClockColors(colorPalette[Math.max(0, length - 5)], colorPalette[Math.max(0, length - 2)]); } @Override public void onTimeTick() { mAnalogClock.onTimeChanged(); + mBigClockView.onTimeChanged(); mDigitalClock.refresh(); mLockClock.refresh(); } diff --git a/packages/SystemUI/src/com/android/keyguard/clock/BubbleClockController.java b/packages/SystemUI/src/com/android/keyguard/clock/BubbleClockController.java index d30f45f212b9..6069a5e4783d 100644 --- a/packages/SystemUI/src/com/android/keyguard/clock/BubbleClockController.java +++ b/packages/SystemUI/src/com/android/keyguard/clock/BubbleClockController.java @@ -60,7 +60,7 @@ public class BubbleClockController implements ClockPlugin { /** * Custom clock shown on AOD screen and behind stack scroller on lock. */ - private View mView; + private ClockLayout mView; private TextClock mDigitalClock; private ImageClock mAnalogClock; @@ -90,7 +90,7 @@ public class BubbleClockController implements ClockPlugin { } private void createViews() { - mView = mLayoutInflater.inflate(R.layout.bubble_clock, null); + mView = (ClockLayout) mLayoutInflater.inflate(R.layout.bubble_clock, null); mDigitalClock = (TextClock) mView.findViewById(R.id.digital_clock); mAnalogClock = (ImageClock) mView.findViewById(R.id.analog_clock); @@ -186,6 +186,7 @@ public class BubbleClockController implements ClockPlugin { @Override public void onTimeTick() { mAnalogClock.onTimeChanged(); + mView.onTimeChanged(); mDigitalClock.refresh(); mLockClock.refresh(); } diff --git a/packages/SystemUI/src/com/android/keyguard/clock/ClockLayout.java b/packages/SystemUI/src/com/android/keyguard/clock/ClockLayout.java index 7ffee5d5232d..55088a800ec8 100644 --- a/packages/SystemUI/src/com/android/keyguard/clock/ClockLayout.java +++ b/packages/SystemUI/src/com/android/keyguard/clock/ClockLayout.java @@ -32,6 +32,7 @@ import com.android.keyguard.R; */ public class ClockLayout extends FrameLayout { + private static final int ANALOG_CLOCK_SHIFT_FACTOR = 3; /** * Clock face views. */ @@ -73,7 +74,14 @@ public class ClockLayout extends FrameLayout { @Override protected void onLayout(boolean changed, int left, int top, int right, int bottom) { super.onLayout(changed, left, top, right, bottom); + positionChildren(); + } + + void onTimeChanged() { + positionChildren(); + } + private void positionChildren() { final float offsetX = getBurnInOffset(mBurnInPreventionOffsetX * 2, true) - mBurnInPreventionOffsetX; final float offsetY = getBurnInOffset(mBurnInPreventionOffsetY * 2, false) @@ -89,9 +97,9 @@ public class ClockLayout extends FrameLayout { // Put the analog clock in the middle of the screen. if (mAnalogClock != null) { mAnalogClock.setX(Math.max(0f, 0.5f * (getWidth() - mAnalogClock.getWidth())) - + offsetX); + + ANALOG_CLOCK_SHIFT_FACTOR * offsetX); mAnalogClock.setY(Math.max(0f, 0.5f * (getHeight() - mAnalogClock.getHeight())) - + offsetY); + + ANALOG_CLOCK_SHIFT_FACTOR * offsetY); } } } diff --git a/packages/SystemUI/src/com/android/keyguard/clock/ClockManager.java b/packages/SystemUI/src/com/android/keyguard/clock/ClockManager.java index e373ca1d955c..06488b86c59e 100644 --- a/packages/SystemUI/src/com/android/keyguard/clock/ClockManager.java +++ b/packages/SystemUI/src/com/android/keyguard/clock/ClockManager.java @@ -142,8 +142,7 @@ public final class ClockManager { addBuiltinClock(() -> new DefaultClockController(res, layoutInflater, colorExtractor)); addBuiltinClock(() -> new BubbleClockController(res, layoutInflater, colorExtractor)); - addBuiltinClock(() -> new StretchAnalogClockController(res, layoutInflater, - colorExtractor)); + addBuiltinClock(() -> new AnalogClockController(res, layoutInflater, colorExtractor)); // Store the size of the display for generation of clock preview. DisplayMetrics dm = res.getDisplayMetrics(); diff --git a/packages/SystemUI/src/com/android/keyguard/clock/StretchAnalogClock.java b/packages/SystemUI/src/com/android/keyguard/clock/StretchAnalogClock.java deleted file mode 100644 index 8cdd6325638e..000000000000 --- a/packages/SystemUI/src/com/android/keyguard/clock/StretchAnalogClock.java +++ /dev/null @@ -1,145 +0,0 @@ -/* - * Copyright (C) 2019 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package com.android.keyguard.clock; - -import android.content.Context; -import android.graphics.Canvas; -import android.graphics.Color; -import android.graphics.Paint; -import android.util.AttributeSet; -import android.view.View; - -import java.util.Calendar; -import java.util.TimeZone; - -/** - * Analog clock where the minute hand extends off of the screen. - */ -public class StretchAnalogClock extends View { - - private static final int DEFAULT_COLOR = Color.parseColor("#F5C983"); - private static final float HOUR_STROKE_WIDTH = 60f; - private static final float MINUTE_STROKE_WIDTH = 20f; - private static final float CENTER_GAP_AND_CIRCLE_RADIUS = 80f; - - private final Paint mHourPaint = new Paint(); - private final Paint mMinutePaint = new Paint(); - private Calendar mTime = Calendar.getInstance(TimeZone.getDefault()); - private TimeZone mTimeZone; - - public StretchAnalogClock(Context context) { - this(context, null); - } - - public StretchAnalogClock(Context context, AttributeSet attrs) { - this(context, attrs, 0); - } - - public StretchAnalogClock(Context context, AttributeSet attrs, int defStyleAttr) { - this(context, attrs, defStyleAttr, 0); - } - - public StretchAnalogClock(Context context, AttributeSet attrs, int defStyleAttr, - int defStyleRes) { - super(context, attrs, defStyleAttr, defStyleRes); - init(); - } - - /** - * Call when the time changes to update the clock hands. - */ - public void onTimeChanged() { - mTime.setTimeInMillis(System.currentTimeMillis()); - invalidate(); - } - - /** - * Call when the time zone has changed to update clock hands. - * - * @param timeZone The updated time zone that will be used. - */ - public void onTimeZoneChanged(TimeZone timeZone) { - mTime.setTimeZone(timeZone); - } - - /** - * Set the colors to use on the clock face. - * @param dark Darker color obtained from color palette. - * @param light Lighter color obtained from color palette. - */ - public void setClockColor(int dark, int light) { - mHourPaint.setColor(dark); - invalidate(); - } - - private void init() { - mHourPaint.setColor(DEFAULT_COLOR); - mHourPaint.setStrokeWidth(HOUR_STROKE_WIDTH); - mHourPaint.setAntiAlias(true); - mHourPaint.setStrokeCap(Paint.Cap.ROUND); - - mMinutePaint.setColor(Color.WHITE); - mMinutePaint.setStrokeWidth(MINUTE_STROKE_WIDTH); - mMinutePaint.setAntiAlias(true); - mMinutePaint.setStrokeCap(Paint.Cap.ROUND); - } - - @Override - protected void onDraw(Canvas canvas) { - final float centerX = getWidth() / 2f; - final float centerY = getHeight() / 2f; - - final float minutesRotation = mTime.get(Calendar.MINUTE) * 6f; - final float hoursRotation = mTime.get(Calendar.HOUR) * 30 - + mTime.get(Calendar.MINUTE) * 0.5f; - - // Compute length of clock hands. Hour hand is 60% the length from center to edge - // and minute hand is twice the length to make sure it extends past screen edge. - double sMinuteHandLengthFactor = Math.sin(2d * Math.PI * minutesRotation / 360d); - float sMinuteHandLength = (float) (2d * (centerY + (centerX - centerY) - * sMinuteHandLengthFactor * sMinuteHandLengthFactor)); - double sHourHandLengthFactor = Math.sin(2d * Math.PI * hoursRotation / 360d); - float sHourHandLength = (float) (0.6d * (centerY + (centerX - centerY) - * sHourHandLengthFactor * sHourHandLengthFactor)); - - canvas.save(); - - canvas.rotate(minutesRotation, centerX, centerY); - canvas.drawLine( - centerX, - centerY + CENTER_GAP_AND_CIRCLE_RADIUS, - centerX, - centerY - sMinuteHandLength, - mMinutePaint); - - canvas.rotate(hoursRotation - minutesRotation, centerX, centerY); - canvas.drawLine( - centerX, - centerY + CENTER_GAP_AND_CIRCLE_RADIUS, - centerX, - centerY - sHourHandLength, - mHourPaint); - - canvas.restore(); - } - - @Override - protected void onAttachedToWindow() { - super.onAttachedToWindow(); - mTime.setTimeZone(mTimeZone != null ? mTimeZone : TimeZone.getDefault()); - onTimeChanged(); - } -} diff --git a/packages/SystemUI/src/com/android/systemui/bubbles/Bubble.java b/packages/SystemUI/src/com/android/systemui/bubbles/Bubble.java index 665df777c1c1..03324777e4ea 100644 --- a/packages/SystemUI/src/com/android/systemui/bubbles/Bubble.java +++ b/packages/SystemUI/src/com/android/systemui/bubbles/Bubble.java @@ -16,47 +16,65 @@ package com.android.systemui.bubbles; +import android.os.UserHandle; import android.view.LayoutInflater; import com.android.systemui.R; import com.android.systemui.statusbar.notification.collection.NotificationEntry; +import java.util.Objects; + /** * Encapsulates the data and UI elements of a bubble. */ class Bubble { + private static final boolean DEBUG = false; + private static final String TAG = "Bubble"; + private final String mKey; + private final String mGroupId; private final BubbleExpandedView.OnBubbleBlockedListener mListener; private boolean mInflated; - - public BubbleView iconView; - public BubbleExpandedView expandedView; public NotificationEntry entry; + BubbleView iconView; + BubbleExpandedView expandedView; + + private static String groupId(NotificationEntry entry) { + UserHandle user = entry.notification.getUser(); + return user.getIdentifier() + '|' + entry.notification.getPackageName(); + } Bubble(NotificationEntry e, BubbleExpandedView.OnBubbleBlockedListener listener) { entry = e; mKey = e.key; + mGroupId = groupId(e); mListener = listener; } - /** @deprecated use the other constructor to defer View creation. */ - @Deprecated - Bubble(NotificationEntry e, LayoutInflater inflater, BubbleStackView stackView, - BubbleExpandedView.OnBubbleBlockedListener listener) { - this(e, listener); - inflate(inflater, stackView); - } - public String getKey() { return mKey; } + public String getGroupId() { + return mGroupId; + } + + public String getPackageName() { + return entry.notification.getPackageName(); + } + boolean isInflated() { return mInflated; } + public void updateDotVisibility() { + if (iconView != null) { + iconView.updateDotVisibility(); + } + } + void inflate(LayoutInflater inflater, BubbleStackView stackView) { if (mInflated) { return; @@ -73,10 +91,32 @@ class Bubble { mInflated = true; } + void setDismissed() { + entry.setBubbleDismissed(true); + // TODO: move this somewhere where it can be guaranteed not to run until safe from flicker + if (expandedView != null) { + expandedView.cleanUpExpandedState(); + } + } + void setEntry(NotificationEntry entry) { + this.entry = entry; if (mInflated) { iconView.update(entry); expandedView.update(entry); } } + + @Override + public boolean equals(Object o) { + if (this == o) return true; + if (!(o instanceof Bubble)) return false; + Bubble bubble = (Bubble) o; + return Objects.equals(mKey, bubble.mKey); + } + + @Override + public int hashCode() { + return Objects.hash(mKey); + } } diff --git a/packages/SystemUI/src/com/android/systemui/bubbles/BubbleController.java b/packages/SystemUI/src/com/android/systemui/bubbles/BubbleController.java index 0fcc9501e367..acdcfb2ea688 100644 --- a/packages/SystemUI/src/com/android/systemui/bubbles/BubbleController.java +++ b/packages/SystemUI/src/com/android/systemui/bubbles/BubbleController.java @@ -77,8 +77,7 @@ import javax.inject.Singleton; * The controller manages addition, removal, and visible state of bubbles on screen. */ @Singleton -public class BubbleController implements BubbleExpandedView.OnBubbleBlockedListener, - ConfigurationController.ConfigurationListener { +public class BubbleController implements ConfigurationController.ConfigurationListener { private static final String TAG = "BubbleController"; @@ -174,6 +173,10 @@ public class BubbleController implements BubbleExpandedView.OnBubbleBlockedListe @Override public void onStateChanged(int newState) { mState = newState; + boolean shouldCollapse = (mState != SHADE); + if (shouldCollapse) { + collapseStack(); + } updateVisibility(); } } @@ -236,7 +239,6 @@ public class BubbleController implements BubbleExpandedView.OnBubbleBlockedListe if (mExpandListener != null) { mStackView.setExpandListener(mExpandListener); } - mStackView.setOnBlockedListener(this); } } @@ -284,28 +286,38 @@ public class BubbleController implements BubbleExpandedView.OnBubbleBlockedListe if (mStackView == null) { return false; } - for (Bubble bubble : mBubbleData.getBubbles()) { - if (!bubble.entry.isBubbleDismissed()) { - return true; - } - } - return false; + return mBubbleData.hasBubbles(); } /** * Whether the stack of bubbles is expanded or not. */ public boolean isStackExpanded() { - return mStackView != null && mStackView.isExpanded(); + return mBubbleData.isExpanded(); + } + + /** + * Tell the stack of bubbles to expand. + */ + public void expandStack() { + mBubbleData.setExpanded(true); } /** * Tell the stack of bubbles to collapse. */ public void collapseStack() { - if (mStackView != null) { - mStackView.collapseStack(); - } + mBubbleData.setExpanded(false /* expanded */); + } + + void selectBubble(Bubble bubble) { + mBubbleData.setSelectedBubble(bubble); + } + + @VisibleForTesting + void selectBubble(String key) { + Bubble bubble = mBubbleData.getBubbleWithKey(key); + selectBubble(bubble); } /** @@ -314,8 +326,10 @@ public class BubbleController implements BubbleExpandedView.OnBubbleBlockedListe * @param notificationKey the notification key for the bubble to be selected */ public void expandStackAndSelectBubble(String notificationKey) { - if (mStackView != null && mBubbleData.getBubble(notificationKey) != null) { - mStackView.setExpandedBubble(notificationKey); + Bubble bubble = mBubbleData.getBubbleWithKey(notificationKey); + if (bubble != null) { + mBubbleData.setSelectedBubble(bubble); + mBubbleData.setExpanded(true); } } @@ -323,13 +337,7 @@ public class BubbleController implements BubbleExpandedView.OnBubbleBlockedListe * Tell the stack of bubbles to be dismissed, this will remove all of the bubbles in the stack. */ void dismissStack(@DismissReason int reason) { - if (mStackView == null) { - return; - } - mStackView.stackDismissed(reason); - - updateVisibility(); - mNotificationEntryManager.updateNotifications(); + mBubbleData.dismissAll(reason); } /** @@ -348,20 +356,7 @@ public class BubbleController implements BubbleExpandedView.OnBubbleBlockedListe * @param notif the notification associated with this bubble. */ void updateBubble(NotificationEntry notif) { - if (mStackView != null && mBubbleData.getBubble(notif.key) != null) { - // It's an update - mStackView.updateBubble(notif); - } else { - // It's new - ensureStackViewCreated(); - mStackView.addBubble(notif); - } - Bubble bubble = mBubbleData.getBubble(notif.key); - if (shouldAutoExpand(notif)) { - mStackView.setSelectedBubble(bubble); - mStackView.setExpanded(true); - } - updateVisibility(); + mBubbleData.notificationEntryUpdated(notif); } /** @@ -371,23 +366,10 @@ public class BubbleController implements BubbleExpandedView.OnBubbleBlockedListe */ @MainThread void removeBubble(String key, int reason) { - if (mStackView != null) { - mStackView.removeBubble(key, reason); - } - mNotificationEntryManager.updateNotifications(); - updateVisibility(); - } - - @Override - public void onBubbleBlocked(NotificationEntry entry) { - Object[] bubbles = mBubbleData.getBubbles().toArray(); - for (int i = 0; i < bubbles.length; i++) { - NotificationEntry e = ((Bubble) bubbles[i]).entry; - boolean samePackage = entry.notification.getPackageName().equals( - e.notification.getPackageName()); - if (samePackage) { - removeBubble(entry.key, DISMISS_BLOCKED); - } + // TEMP: refactor to change this to pass entry + Bubble bubble = mBubbleData.getBubbleWithKey(key); + if (bubble != null) { + mBubbleData.notificationEntryRemoved(bubble.entry, reason); } } @@ -424,7 +406,6 @@ public class BubbleController implements BubbleExpandedView.OnBubbleBlockedListe updateShowInShadeForSuppressNotification(entry); entry.setBubbleDismissed(false); // updates come back as bubbles even if dismissed updateBubble(entry); - mStackView.updateDotVisibility(entry.key); } } @@ -446,44 +427,57 @@ public class BubbleController implements BubbleExpandedView.OnBubbleBlockedListe } }; + @SuppressWarnings("FieldCanBeLocal") private final BubbleData.Listener mBubbleDataListener = new BubbleData.Listener() { + @Override public void onBubbleAdded(Bubble bubble) { - + ensureStackViewCreated(); + mStackView.addBubble(bubble); } @Override public void onBubbleRemoved(Bubble bubble, int reason) { - + if (mStackView != null) { + mStackView.removeBubble(bubble); + } } public void onBubbleUpdated(Bubble bubble) { - + if (mStackView != null) { + mStackView.updateBubble(bubble); + } } @Override public void onOrderChanged(List<Bubble> bubbles) { - } @Override public void onSelectionChanged(Bubble selectedBubble) { - + if (mStackView != null) { + mStackView.setSelectedBubble(selectedBubble); + } } @Override public void onExpandedChanged(boolean expanded) { - + if (mStackView != null) { + mStackView.setExpanded(expanded); + } } @Override public void showFlyoutText(Bubble bubble, String text) { - + if (mStackView != null) { + mStackView.animateInFlyoutForBubble(bubble); + } } @Override public void apply() { - + mNotificationEntryManager.updateNotifications(); + updateVisibility(); } }; @@ -514,7 +508,6 @@ public class BubbleController implements BubbleExpandedView.OnBubbleBlockedListe mStackView.setVisibility(hasBubbles() ? VISIBLE : INVISIBLE); } else if (mStackView != null) { mStackView.setVisibility(INVISIBLE); - collapseStack(); } updateBubblesShowing(); } @@ -621,14 +614,14 @@ public class BubbleController implements BubbleExpandedView.OnBubbleBlockedListe @Override public void onTaskMovedToFront(RunningTaskInfo taskInfo) { if (mStackView != null && taskInfo.displayId == Display.DEFAULT_DISPLAY) { - mStackView.collapseStack(); + mBubbleData.setExpanded(false); } } @Override public void onActivityLaunchOnSecondaryDisplayRerouted() { if (mStackView != null) { - mStackView.collapseStack(); + mBubbleData.setExpanded(false); } } } diff --git a/packages/SystemUI/src/com/android/systemui/bubbles/BubbleData.java b/packages/SystemUI/src/com/android/systemui/bubbles/BubbleData.java index fe3f9d192cd5..259665dedf5b 100644 --- a/packages/SystemUI/src/com/android/systemui/bubbles/BubbleData.java +++ b/packages/SystemUI/src/com/android/systemui/bubbles/BubbleData.java @@ -15,14 +15,24 @@ */ package com.android.systemui.bubbles; -import androidx.annotation.Nullable; +import static com.android.internal.annotations.VisibleForTesting.Visibility.PRIVATE; + +import android.app.ActivityManager; +import android.app.Notification; +import android.app.PendingIntent; +import android.content.Context; +import android.util.Log; import com.android.internal.annotations.VisibleForTesting; +import com.android.systemui.bubbles.BubbleController.DismissReason; import com.android.systemui.statusbar.notification.collection.NotificationEntry; +import java.util.ArrayList; import java.util.Collection; -import java.util.HashMap; +import java.util.Collections; +import java.util.Iterator; import java.util.List; +import java.util.Objects; import javax.inject.Inject; import javax.inject.Singleton; @@ -33,6 +43,8 @@ import javax.inject.Singleton; @Singleton public class BubbleData { + private static final String TAG = "BubbleData"; + /** * This interface reports changes to the state and appearance of bubbles which should be applied * as necessary to the UI. @@ -53,7 +65,7 @@ public class BubbleData { * A Bubble has been removed. A call to {@link #onOrderChanged(List)} will * follow. */ - void onBubbleRemoved(Bubble bubble, @BubbleController.DismissReason int reason); + void onBubbleRemoved(Bubble bubble, @DismissReason int reason); /** * An existing bubble has been updated. @@ -86,46 +98,253 @@ public class BubbleData { void apply(); } - private HashMap<String, Bubble> mBubbles = new HashMap<>(); + private final Context mContext; + private final List<Bubble> mBubbles = new ArrayList<>(); + private Bubble mSelectedBubble; + private boolean mExpanded; private Listener mListener; @VisibleForTesting @Inject - public BubbleData() {} + public BubbleData(Context context) { + mContext = context; + } + + public boolean hasBubbles() { + return !mBubbles.isEmpty(); + } + + public boolean isExpanded() { + return mExpanded; + } + + public boolean hasBubbleWithKey(String key) { + return getBubbleWithKey(key) != null; + } + + public void setExpanded(boolean expanded) { + if (setExpandedInternal(expanded)) { + mListener.apply(); + } + } + + public void setSelectedBubble(Bubble bubble) { + if (setSelectedBubbleInternal(bubble)) { + mListener.apply(); + } + } + + public void notificationEntryUpdated(NotificationEntry entry) { + Bubble bubble = getBubbleWithKey(entry.key); + if (bubble == null) { + // Create a new bubble + bubble = new Bubble(entry, this::onBubbleBlocked); + mBubbles.add(0, bubble); // TODO: reorder/group + mListener.onBubbleAdded(bubble); + } else { + // Updates an existing bubble + bubble.setEntry(entry); + mListener.onBubbleUpdated(bubble); + } + if (shouldAutoExpand(entry)) { + setSelectedBubbleInternal(bubble); + if (!mExpanded) { + setExpandedInternal(true); + } + } else if (mSelectedBubble == null) { + setSelectedBubbleInternal(bubble); + } + // TODO: reorder/group + mListener.apply(); + } + + public void notificationEntryRemoved(NotificationEntry entry, @DismissReason int reason) { + int indexToRemove = indexForKey(entry.key); + if (indexToRemove >= 0) { + Bubble removed = mBubbles.remove(indexToRemove); + removed.setDismissed(); + mListener.onBubbleRemoved(removed, reason); + maybeSendDeleteIntent(reason, removed.entry); + + if (mBubbles.isEmpty()) { + setExpandedInternal(false); + setSelectedBubbleInternal(null); + } else if (removed == mSelectedBubble) { + int newIndex = Math.min(indexToRemove, mBubbles.size() - 1); + Bubble newSelected = mBubbles.get(newIndex); + setSelectedBubbleInternal(newSelected); + } + // TODO: reorder/group + mListener.apply(); + } + } + + public void dismissAll(@DismissReason int reason) { + boolean changed = setExpandedInternal(false); + while (!mBubbles.isEmpty()) { + Bubble bubble = mBubbles.remove(0); + bubble.setDismissed(); + maybeSendDeleteIntent(reason, bubble.entry); + mListener.onBubbleRemoved(bubble, reason); + changed = true; + } + if (setSelectedBubbleInternal(null)) { + changed = true; + } + if (changed) { + // TODO: reorder/group + mListener.apply(); + } + } /** - * The set of bubbles. + * Requests a change to the selected bubble. Calls {@link Listener#onSelectionChanged} if + * the value changes. + * + * @param bubble the new selected bubble + * @return true if the state changed as a result */ - public Collection<Bubble> getBubbles() { - return mBubbles.values(); + private boolean setSelectedBubbleInternal(Bubble bubble) { + if (Objects.equals(bubble, mSelectedBubble)) { + return false; + } + if (bubble != null && !mBubbles.contains(bubble)) { + Log.e(TAG, "Cannot select bubble which doesn't exist!" + + " (" + bubble + ") bubbles=" + mBubbles); + return false; + } + if (mExpanded) { + // TODO: bubble.markAsActive() ? + bubble.entry.setShowInShadeWhenBubble(false); + } + mSelectedBubble = bubble; + mListener.onSelectionChanged(mSelectedBubble); + return true; + } + + + /** + * Requests a change to the expanded state. Calls {@link Listener#onExpandedChanged} if + * the value changes. + * + * @param shouldExpand the new requested state + * @return true if the state changed as a result + */ + private boolean setExpandedInternal(boolean shouldExpand) { + if (mExpanded == shouldExpand) { + return false; + } + if (shouldExpand) { + if (mBubbles.isEmpty()) { + Log.e(TAG, "Attempt to expand stack when empty!"); + return false; + } + if (mSelectedBubble == null) { + Log.e(TAG, "Attempt to expand stack without selected bubble!"); + return false; + } + // TODO: bubble.markAsActive() ? + mSelectedBubble.entry.setShowInShadeWhenBubble(false); + } + // TODO: reorder/regroup + mExpanded = shouldExpand; + mListener.onExpandedChanged(mExpanded); + return true; } - @Nullable - public Bubble getBubble(String key) { - return mBubbles.get(key); + private void maybeSendDeleteIntent(@DismissReason int reason, NotificationEntry entry) { + if (reason == BubbleController.DISMISS_USER_GESTURE) { + Notification.BubbleMetadata bubbleMetadata = entry.getBubbleMetadata(); + PendingIntent deleteIntent = bubbleMetadata != null + ? bubbleMetadata.getDeleteIntent() + : null; + if (deleteIntent != null) { + try { + deleteIntent.send(); + } catch (PendingIntent.CanceledException e) { + Log.w(TAG, "Failed to send delete intent for bubble with key: " + entry.key); + } + } + } } - public void addBubble(Bubble b) { - mBubbles.put(b.getKey(), b); + private void onBubbleBlocked(NotificationEntry entry) { + boolean changed = false; + final String blockedPackage = entry.notification.getPackageName(); + for (Iterator<Bubble> i = mBubbles.iterator(); i.hasNext(); ) { + Bubble bubble = i.next(); + if (bubble.getPackageName().equals(blockedPackage)) { + i.remove(); + mListener.onBubbleRemoved(bubble, BubbleController.DISMISS_BLOCKED); + changed = true; + } + } + if (changed) { + // TODO: reorder/group + mListener.apply(); + } } - @Nullable - public Bubble removeBubble(String key) { - return mBubbles.remove(key); + private int indexForKey(String key) { + for (int i = 0; i < mBubbles.size(); i++) { + Bubble bubble = mBubbles.get(i); + if (bubble.getKey().equals(key)) { + return i; + } + } + return -1; } - public void updateBubble(String key, NotificationEntry newEntry) { - Bubble oldBubble = mBubbles.get(key); - if (oldBubble != null) { - oldBubble.setEntry(newEntry); + private Bubble removeBubbleWithKey(String key) { + for (int i = 0; i < mBubbles.size(); i++) { + Bubble bubble = mBubbles.get(i); + if (bubble.getKey().equals(key)) { + mBubbles.remove(i); + return bubble; + } } + return null; + } + + /** + * The set of bubbles. + * + * @deprecated + */ + @Deprecated + public Collection<Bubble> getBubbles() { + return Collections.unmodifiableList(mBubbles); } - public void clear() { - mBubbles.clear(); + @VisibleForTesting(visibility = PRIVATE) + Bubble getBubbleWithKey(String key) { + for (int i = 0; i < mBubbles.size(); i++) { + Bubble bubble = mBubbles.get(i); + if (bubble.getKey().equals(key)) { + return bubble; + } + } + return null; } public void setListener(Listener listener) { mListener = listener; } -} + + boolean shouldAutoExpand(NotificationEntry entry) { + Notification.BubbleMetadata metadata = entry.getBubbleMetadata(); + return metadata != null && metadata.getAutoExpandBubble() + && isForegroundApp(entry.notification.getPackageName()); + } + + /** + * Return true if the applications with the package name is running in foreground. + * + * @param pkgName application package name. + */ + boolean isForegroundApp(String pkgName) { + ActivityManager am = mContext.getSystemService(ActivityManager.class); + List<ActivityManager.RunningTaskInfo> tasks = am.getRunningTasks(1 /* maxNum */); + return !tasks.isEmpty() && pkgName.equals(tasks.get(0).topActivity.getPackageName()); + } +}
\ No newline at end of file diff --git a/packages/SystemUI/src/com/android/systemui/bubbles/BubbleStackView.java b/packages/SystemUI/src/com/android/systemui/bubbles/BubbleStackView.java index 424cd5544e50..18b2e37e31ec 100644 --- a/packages/SystemUI/src/com/android/systemui/bubbles/BubbleStackView.java +++ b/packages/SystemUI/src/com/android/systemui/bubbles/BubbleStackView.java @@ -20,8 +20,6 @@ import static android.view.ViewGroup.LayoutParams.MATCH_PARENT; import static android.view.ViewGroup.LayoutParams.WRAP_CONTENT; import android.annotation.NonNull; -import android.app.Notification; -import android.app.PendingIntent; import android.content.Context; import android.content.res.Resources; import android.graphics.Outline; @@ -54,9 +52,7 @@ import androidx.dynamicanimation.animation.SpringForce; import com.android.internal.annotations.VisibleForTesting; import com.android.internal.widget.ViewClippingUtil; -import com.android.systemui.Dependency; import com.android.systemui.R; -import com.android.systemui.bubbles.BubbleController.DismissReason; import com.android.systemui.bubbles.animation.ExpandedAnimationController; import com.android.systemui.bubbles.animation.PhysicsAnimationLayout; import com.android.systemui.bubbles.animation.StackAnimationController; @@ -359,14 +355,13 @@ public class BubbleStackView extends FrameLayout { } switch (action) { case AccessibilityNodeInfo.ACTION_DISMISS: - Dependency.get(BubbleController.class).dismissStack( - BubbleController.DISMISS_ACCESSIBILITY_ACTION); + mBubbleData.dismissAll(BubbleController.DISMISS_ACCESSIBILITY_ACTION); return true; case AccessibilityNodeInfo.ACTION_COLLAPSE: - collapseStack(); + mBubbleData.setExpanded(false); return true; case AccessibilityNodeInfo.ACTION_EXPAND: - expandStack(); + mBubbleData.setExpanded(true); return true; } return false; @@ -393,9 +388,9 @@ public class BubbleStackView extends FrameLayout { * @param key the {@link NotificationEntry#key} associated with the bubble. */ public void updateDotVisibility(String key) { - Bubble b = mBubbleData.getBubble(key); + Bubble b = mBubbleData.getBubbleWithKey(key); if (b != null) { - b.iconView.updateDotVisibility(); + b.updateDotVisibility(); } } @@ -407,16 +402,6 @@ public class BubbleStackView extends FrameLayout { } /** - * Sets the listener to notify when a bubble is blocked. - */ - public void setOnBlockedListener(BubbleExpandedView.OnBubbleBlockedListener listener) { - mBlockedListener = listener; - for (Bubble b : mBubbleData.getBubbles()) { - b.expandedView.setOnBlockedListener(mBlockedListener); - } - } - - /** * Whether the stack of bubbles is expanded or not. */ public boolean isExpanded() { @@ -445,7 +430,7 @@ public class BubbleStackView extends FrameLayout { */ @Deprecated void setExpandedBubble(String key) { - Bubble bubbleToExpand = mBubbleData.getBubble(key); + Bubble bubbleToExpand = mBubbleData.getBubbleWithKey(key); if (bubbleToExpand != null) { setSelectedBubble(bubbleToExpand); bubbleToExpand.entry.setShowInShadeWhenBubble(false); @@ -466,11 +451,36 @@ public class BubbleStackView extends FrameLayout { } } + // via BubbleData.Listener + void addBubble(Bubble bubble) { + bubble.inflate(mInflater, this); + mBubbleContainer.addView(bubble.iconView, 0, + new FrameLayout.LayoutParams(WRAP_CONTENT, WRAP_CONTENT)); + ViewClippingUtil.setClippingDeactivated(bubble.iconView, true, mClippingParameters); + requestUpdate(); + logBubbleEvent(bubble, StatsLog.BUBBLE_UICHANGED__ACTION__POSTED); + } + + // via BubbleData.Listener + void removeBubble(Bubble bubble) { + // Remove it from the views + int removedIndex = mBubbleContainer.indexOfChild(bubble.iconView); + mBubbleContainer.removeViewAt(removedIndex); + logBubbleEvent(bubble, StatsLog.BUBBLE_UICHANGED__ACTION__DISMISSED); + } + + // via BubbleData.Listener + void updateBubble(Bubble bubble) { + requestUpdate(); + logBubbleEvent(bubble, StatsLog.BUBBLE_UICHANGED__ACTION__UPDATED); + } + /** * Changes the currently selected bubble. If the stack is already expanded, the newly selected * bubble will be shown immediately. This does not change the expanded state or change the * position of any bubble. */ + // via BubbleData.Listener public void setSelectedBubble(Bubble bubbleToSelect) { if (mExpandedBubble != null && mExpandedBubble.equals(bubbleToSelect)) { return; @@ -489,7 +499,8 @@ public class BubbleStackView extends FrameLayout { logBubbleEvent(previouslySelected, StatsLog.BUBBLE_UICHANGED__ACTION__COLLAPSED); logBubbleEvent(bubbleToSelect, StatsLog.BUBBLE_UICHANGED__ACTION__EXPANDED); notifyExpansionChanged(previouslySelected.entry, false /* expanded */); - notifyExpansionChanged(bubbleToSelect.entry, true /* expanded */); + notifyExpansionChanged(bubbleToSelect == null ? null : bubbleToSelect.entry, + true /* expanded */); }); } } @@ -497,13 +508,15 @@ public class BubbleStackView extends FrameLayout { /** * Changes the expanded state of the stack. * - * @param expanded whether the bubble stack should appear expanded + * @param shouldExpand whether the bubble stack should appear expanded */ - public void setExpanded(boolean expanded) { - if (expanded == mIsExpanded) { + // via BubbleData.Listener + public void setExpanded(boolean shouldExpand) { + boolean wasExpanded = mIsExpanded; + if (shouldExpand == wasExpanded) { return; } - if (mIsExpanded) { + if (wasExpanded) { // Collapse the stack animateExpansion(false /* expand */); logBubbleEvent(mExpandedBubble, StatsLog.BUBBLE_UICHANGED__ACTION__COLLAPSED); @@ -518,131 +531,17 @@ public class BubbleStackView extends FrameLayout { } /** - * Adds a bubble to the top of the stack. - * - * @param entry the notification to add to the stack of bubbles. - */ - void addBubble(NotificationEntry entry) { - Bubble b = new Bubble(entry, mInflater, this /* stackView */, mBlockedListener); - mBubbleData.addBubble(b); - - mBubbleContainer.addView(b.iconView, 0, - new FrameLayout.LayoutParams(WRAP_CONTENT, WRAP_CONTENT)); - ViewClippingUtil.setClippingDeactivated(b.iconView, true, mClippingParameters); - - requestUpdate(); - logBubbleEvent(b, StatsLog.BUBBLE_UICHANGED__ACTION__POSTED); - - animateInFlyoutForBubble(b); - } - - /** - * Remove a bubble from the stack. - */ - void removeBubble(String key, int reason) { - Bubble b = mBubbleData.removeBubble(key); - if (b == null) { - return; - } - setBubbleDismissed(b, reason); - - // Remove it from the views - int removedIndex = mBubbleContainer.indexOfChild(b.iconView); - mBubbleContainer.removeViewAt(removedIndex); - - int bubbleCount = mBubbleContainer.getChildCount(); - if (bubbleCount == 0) { - // If no bubbles remain, collapse the entire stack. - collapseStack(); - return; - } else if (b.equals(mExpandedBubble)) { - // Was the current bubble just removed? - // If we have other bubbles and are expanded go to the next one or previous - // if the bubble removed was last - int nextIndex = bubbleCount > removedIndex ? removedIndex : bubbleCount - 1; - BubbleView expandedBubble = (BubbleView) mBubbleContainer.getChildAt(nextIndex); - if (mIsExpanded) { - setExpandedBubble(expandedBubble.getKey()); - } else { - mExpandedBubble = null; - } - } - // TODO: consider logging reason code - logBubbleEvent(b, StatsLog.BUBBLE_UICHANGED__ACTION__DISMISSED); - } - - /** * Dismiss the stack of bubbles. + * @deprecated */ + @Deprecated void stackDismissed(int reason) { - for (Bubble bubble : mBubbleData.getBubbles()) { - setBubbleDismissed(bubble, reason); - } - mBubbleData.clear(); - collapseStack(); - mBubbleContainer.removeAllViews(); - mExpandedViewContainer.removeAllViews(); - // TODO: consider logging reason code + mBubbleData.dismissAll(reason); logBubbleEvent(null /* no bubble associated with bubble stack dismiss */, StatsLog.BUBBLE_UICHANGED__ACTION__STACK_DISMISSED); } /** - * Marks the notification entry as dismissed & calls any delete intents for the bubble. - * - * <p>Note: This does not remove the Bubble from BubbleData. - * - * @param bubble the Bubble being dismissed - * @param reason code for the reason the dismiss was triggered - * @see BubbleController.DismissReason - */ - private void setBubbleDismissed(Bubble bubble, @DismissReason int reason) { - if (DEBUG) { - Log.d(TAG, "dismissBubble: " + bubble + " reason=" + reason); - } - bubble.entry.setBubbleDismissed(true); - bubble.expandedView.cleanUpExpandedState(); - - if (reason == BubbleController.DISMISS_USER_GESTURE) { - Notification.BubbleMetadata bubbleMetadata = bubble.entry.getBubbleMetadata(); - PendingIntent deleteIntent = bubbleMetadata != null - ? bubbleMetadata.getDeleteIntent() - : null; - if (deleteIntent != null) { - try { - deleteIntent.send(); - } catch (PendingIntent.CanceledException e) { - Log.w(TAG, "Failed to send delete intent for bubble with key: " - + (bubble.entry != null ? bubble.entry.key : " null entry")); - } - } - } - } - - /** - * Updates a bubble in the stack. - * @param entry the entry to update in the stack. - */ - public void updateBubble(NotificationEntry entry) { - Bubble b = mBubbleData.getBubble(entry.key); - mBubbleData.updateBubble(entry.key, entry); - - if (!mIsExpanded) { - // If alerting it gets promoted to top of the stack. - if (mBubbleContainer.indexOfChild(b.iconView) != 0) { - mBubbleContainer.moveViewTo(b.iconView, 0); - } - requestUpdate(); - animateInFlyoutForBubble(b /* bubble */); - } - if (mIsExpanded && entry.equals(mExpandedBubble.entry)) { - entry.setShowInShadeWhenBubble(false); - requestUpdate(); - } - logBubbleEvent(b, StatsLog.BUBBLE_UICHANGED__ACTION__UPDATED); - } - - /** * @return the view the touch event is on */ @Nullable @@ -670,7 +569,7 @@ public class BubbleStackView extends FrameLayout { return this; } - public View getFlyoutView() { + View getFlyoutView() { return mFlyout; } @@ -683,13 +582,8 @@ public class BubbleStackView extends FrameLayout { */ @Deprecated @MainThread - public void collapseStack() { - if (mIsExpanded) { - // TODO: Save opened bubble & move it to top of stack - animateExpansion(false /* shouldExpand */); - notifyExpansionChanged(mExpandedBubble.entry, mIsExpanded); - logBubbleEvent(mExpandedBubble, StatsLog.BUBBLE_UICHANGED__ACTION__COLLAPSED); - } + void collapseStack() { + mBubbleData.setExpanded(false); } /** @@ -712,12 +606,8 @@ public class BubbleStackView extends FrameLayout { */ @Deprecated @MainThread - public void expandStack() { - if (!mIsExpanded) { - String expandedBubbleKey = getBubbleAt(0).getKey(); - setExpandedBubble(expandedBubbleKey); - logBubbleEvent(mExpandedBubble, StatsLog.BUBBLE_UICHANGED__ACTION__STACK_EXPANDED); - } + void expandStack() { + mBubbleData.setExpanded(true); } /** diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardSliceProvider.java b/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardSliceProvider.java index 6f50baa53e38..4590470697ea 100644 --- a/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardSliceProvider.java +++ b/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardSliceProvider.java @@ -25,9 +25,7 @@ import android.content.ContentResolver; import android.content.Context; import android.content.Intent; import android.content.IntentFilter; -import android.graphics.Rect; import android.graphics.Typeface; -import android.graphics.drawable.Drawable; import android.graphics.drawable.Icon; import android.icu.text.DateFormat; import android.icu.text.DisplayContext; @@ -37,13 +35,8 @@ import android.os.Handler; import android.os.Trace; import android.provider.Settings; import android.service.notification.ZenModeConfig; -import android.text.SpannableStringBuilder; -import android.text.Spanned; import android.text.TextUtils; -import android.text.style.DynamicDrawableSpan; -import android.text.style.ImageSpan; import android.text.style.StyleSpan; -import android.util.MathUtils; import androidx.core.graphics.drawable.IconCompat; import androidx.slice.Slice; @@ -205,9 +198,9 @@ public class KeyguardSliceProvider extends SliceProvider implements addMediaLocked(builder); } else { builder.addRow(new RowBuilder(mDateUri).setTitle(mLastText)); - addNextAlarmLocked(builder); - addZenModeLocked(builder); } + addNextAlarmLocked(builder); + addZenModeLocked(builder); addPrimaryActionLocked(builder); slice = builder.build(); } @@ -221,32 +214,25 @@ public class KeyguardSliceProvider extends SliceProvider implements protected void addMediaLocked(ListBuilder listBuilder) { if (mMediaMetaData != null) { - SpannableStringBuilder builder = new SpannableStringBuilder(); - - Icon notificationIcon = mMediaManager == null ? null : mMediaManager.getMediaIcon(); - if (notificationIcon != null) { - Drawable drawable = notificationIcon.loadDrawable(getContext()); - Rect mediaBounds = new Rect(0 /* left */, 0 /* top */, - drawable.getIntrinsicWidth(), drawable.getIntrinsicHeight()); - int iconHeaderSize = getContext().getResources() - .getDimensionPixelSize(R.dimen.header_icon_size); - MathUtils.fitRect(mediaBounds, iconHeaderSize); - drawable.setBounds(mediaBounds); - builder.append("# "); - builder.setSpan(new ImageSpan(drawable, DynamicDrawableSpan.ALIGN_CENTER), - 0 /* start */, 1 /* end */, Spanned.SPAN_INCLUSIVE_EXCLUSIVE); - } - CharSequence title = mMediaMetaData.getText(MediaMetadata.METADATA_KEY_TITLE); if (TextUtils.isEmpty(title)) { title = getContext().getResources().getString(R.string.music_controls_no_title); } - builder.append(title); - listBuilder.setHeader(new ListBuilder.HeaderBuilder(mHeaderUri).setTitle(builder)); + listBuilder.setHeader(new ListBuilder.HeaderBuilder(mHeaderUri).setTitle(title)); CharSequence album = mMediaMetaData.getText(MediaMetadata.METADATA_KEY_ARTIST); if (!TextUtils.isEmpty(album)) { - listBuilder.addRow(new RowBuilder(mMediaUri).setTitle(album)); + RowBuilder albumBuilder = new RowBuilder(mMediaUri); + albumBuilder.setTitle(album); + + Icon mediaIcon = mMediaManager == null ? null : mMediaManager.getMediaIcon(); + IconCompat mediaIconCompat = mediaIcon == null ? null + : IconCompat.createFromIcon(getContext(), mediaIcon); + if (mediaIconCompat != null) { + albumBuilder.addEndItem(mediaIconCompat, ListBuilder.ICON_IMAGE); + } + + listBuilder.addRow(albumBuilder); } } } diff --git a/packages/SystemUI/src/com/android/systemui/qs/QSCarrierGroup.java b/packages/SystemUI/src/com/android/systemui/qs/QSCarrierGroup.java index 0eeaa9b75382..7de8b74f30cc 100644 --- a/packages/SystemUI/src/com/android/systemui/qs/QSCarrierGroup.java +++ b/packages/SystemUI/src/com/android/systemui/qs/QSCarrierGroup.java @@ -139,43 +139,49 @@ public class QSCarrierGroup extends LinearLayout implements @Override public void updateCarrierInfo(CarrierTextController.CarrierTextCallbackInfo info) { - if (info.anySimReady) { - boolean[] slotSeen = new boolean[SIM_SLOTS]; - if (info.listOfCarriers.length == info.subscriptionIds.length) { - for (int i = 0; i < SIM_SLOTS && i < info.listOfCarriers.length; i++) { - int slot = getSlotIndex(info.subscriptionIds[i]); - if (slot >= SIM_SLOTS) { - Log.w(TAG, "updateInfoCarrier - slot: " + slot); - continue; - } - if (slot == SubscriptionManager.INVALID_SIM_SLOT_INDEX) { - Log.e(TAG, - "Invalid SIM slot index for subscription: " - + info.subscriptionIds[i]); - continue; + if (info.airplaneMode) { + setVisibility(View.GONE); + } else { + setVisibility(View.VISIBLE); + if (info.anySimReady) { + boolean[] slotSeen = new boolean[SIM_SLOTS]; + if (info.listOfCarriers.length == info.subscriptionIds.length) { + for (int i = 0; i < SIM_SLOTS && i < info.listOfCarriers.length; i++) { + int slot = getSlotIndex(info.subscriptionIds[i]); + if (slot >= SIM_SLOTS) { + Log.w(TAG, "updateInfoCarrier - slot: " + slot); + continue; + } + if (slot == SubscriptionManager.INVALID_SIM_SLOT_INDEX) { + Log.e(TAG, + "Invalid SIM slot index for subscription: " + + info.subscriptionIds[i]); + continue; + } + mInfos[slot].visible = true; + slotSeen[slot] = true; + mCarrierGroups[slot].setCarrierText( + info.listOfCarriers[i].toString().trim()); + mCarrierGroups[slot].setVisibility(View.VISIBLE); } - mInfos[slot].visible = true; - slotSeen[slot] = true; - mCarrierGroups[slot].setCarrierText(info.listOfCarriers[i].toString().trim()); - mCarrierGroups[slot].setVisibility(View.VISIBLE); - } - for (int i = 0; i < SIM_SLOTS; i++) { - if (!slotSeen[i]) { - mInfos[i].visible = false; - mCarrierGroups[i].setVisibility(View.GONE); + for (int i = 0; i < SIM_SLOTS; i++) { + if (!slotSeen[i]) { + mInfos[i].visible = false; + mCarrierGroups[i].setVisibility(View.GONE); + } } + } else { + Log.e(TAG, "Carrier information arrays not of same length"); } } else { - Log.e(TAG, "Carrier information arrays not of same length"); - } - } else { - mInfos[0].visible = false; - mCarrierGroups[0].setCarrierText(info.carrierText); - mCarrierGroups[0].setVisibility(View.VISIBLE); - for (int i = 1; i < SIM_SLOTS; i++) { - mInfos[i].visible = false; - mCarrierGroups[i].setCarrierText(""); - mCarrierGroups[i].setVisibility(View.GONE); + mInfos[0].visible = false; + mCarrierGroups[0].setCarrierText(info.carrierText); + mCarrierGroups[0].setVisibility(View.VISIBLE); + for (int i = 1; i < SIM_SLOTS; i++) { + mInfos[i].visible = false; + mCarrierGroups[i].setCarrierText(""); + mCarrierGroups[i].setVisibility(View.GONE); + } } } handleUpdateState(); diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationViewHierarchyManager.java b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationViewHierarchyManager.java index 2e35f0686d55..97f2dd0749f2 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationViewHierarchyManager.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationViewHierarchyManager.java @@ -22,7 +22,6 @@ import android.os.Trace; import android.util.Log; import android.view.View; import android.view.ViewGroup; -import android.view.ViewParent; import com.android.systemui.R; import com.android.systemui.bubbles.BubbleData; @@ -120,7 +119,7 @@ public class NotificationViewHierarchyManager { for (int i = 0; i < N; i++) { NotificationEntry ent = activeNotifications.get(i); if (ent.isRowDismissed() || ent.isRowRemoved() - || (mBubbleData.getBubble(ent.key) != null && !ent.showInShadeWhenBubble())) { + || (mBubbleData.hasBubbleWithKey(ent.key) && !ent.showInShadeWhenBubble())) { // we don't want to update removed notifications because they could // temporarily become children if they were isolated before. continue; diff --git a/packages/SystemUI/src/com/android/systemui/volume/CaptionsToggleImageButton.java b/packages/SystemUI/src/com/android/systemui/volume/CaptionsToggleImageButton.java index 45fc7563a5c3..1862ed3a4de8 100644 --- a/packages/SystemUI/src/com/android/systemui/volume/CaptionsToggleImageButton.java +++ b/packages/SystemUI/src/com/android/systemui/volume/CaptionsToggleImageButton.java @@ -22,6 +22,9 @@ import android.util.AttributeSet; import android.view.GestureDetector; import android.view.MotionEvent; +import androidx.core.view.ViewCompat; +import androidx.core.view.accessibility.AccessibilityNodeInfoCompat.AccessibilityActionCompat; + import com.android.keyguard.AlphaOptimizedImageButton; import com.android.systemui.R; @@ -31,7 +34,7 @@ public class CaptionsToggleImageButton extends AlphaOptimizedImageButton { private static final int[] OPTED_OUT_STATE = new int[] { R.attr.optedOut }; private ConfirmedTapListener mConfirmedTapListener; - private boolean mComponentEnabled = false; + private boolean mCaptionsEnabled = false; private boolean mOptedOut = false; private GestureDetector mGestureDetector; @@ -39,16 +42,14 @@ public class CaptionsToggleImageButton extends AlphaOptimizedImageButton { new GestureDetector.SimpleOnGestureListener() { @Override public boolean onSingleTapConfirmed(MotionEvent e) { - if (mConfirmedTapListener != null) { - mConfirmedTapListener.onConfirmedTap(); - return true; - } - return false; + return tryToSendTapConfirmedEvent(); } }; public CaptionsToggleImageButton(Context context, AttributeSet attrs) { super(context, attrs); + this.setContentDescription( + getContext().getString(R.string.volume_odi_captions_content_description)); } @Override @@ -66,16 +67,32 @@ public class CaptionsToggleImageButton extends AlphaOptimizedImageButton { return state; } - Runnable setComponentEnabled(boolean isComponentEnabled) { - this.mComponentEnabled = isComponentEnabled; + Runnable setCaptionsEnabled(boolean areCaptionsEnabled) { + this.mCaptionsEnabled = areCaptionsEnabled; + + ViewCompat.replaceAccessibilityAction( + this, + AccessibilityActionCompat.ACTION_CLICK, + mCaptionsEnabled + ? getContext().getString(R.string.volume_odi_captions_hint_disable) + : getContext().getString(R.string.volume_odi_captions_hint_enable), + (view, commandArguments) -> tryToSendTapConfirmedEvent()); - return this.setImageResourceAsync(this.mComponentEnabled + return this.setImageResourceAsync(mCaptionsEnabled ? R.drawable.ic_volume_odi_captions : R.drawable.ic_volume_odi_captions_disabled); } - boolean getComponentEnabled() { - return this.mComponentEnabled; + private boolean tryToSendTapConfirmedEvent() { + if (mConfirmedTapListener != null) { + mConfirmedTapListener.onConfirmedTap(); + return true; + } + return false; + } + + boolean getCaptionsEnabled() { + return this.mCaptionsEnabled; } /** Sets whether or not the current stream has opted out of captions */ diff --git a/packages/SystemUI/src/com/android/systemui/volume/VolumeDialogImpl.java b/packages/SystemUI/src/com/android/systemui/volume/VolumeDialogImpl.java index 509537089bf8..8d9c5a3740b2 100644 --- a/packages/SystemUI/src/com/android/systemui/volume/VolumeDialogImpl.java +++ b/packages/SystemUI/src/com/android/systemui/volume/VolumeDialogImpl.java @@ -596,9 +596,9 @@ public class VolumeDialogImpl implements VolumeDialog, } private void updateCaptionsIcon() { - boolean componentEnabled = mController.areCaptionsEnabled(); - if (mODICaptionsIcon.getComponentEnabled() != componentEnabled) { - mHandler.post(mODICaptionsIcon.setComponentEnabled(componentEnabled)); + boolean captionsEnabled = mController.areCaptionsEnabled(); + if (mODICaptionsIcon.getCaptionsEnabled() != captionsEnabled) { + mHandler.post(mODICaptionsIcon.setCaptionsEnabled(captionsEnabled)); } boolean isOptedOut = mController.isCaptionStreamOptedOut(); @@ -878,7 +878,6 @@ public class VolumeDialogImpl implements VolumeDialog, } view.setContentDescription(mContext.getString(currStateResId)); - view.setAccessibilityDelegate(new AccessibilityDelegate() { public void onInitializeAccessibilityNodeInfo(View host, AccessibilityNodeInfo info) { super.onInitializeAccessibilityNodeInfo(host, info); diff --git a/packages/SystemUI/tests/src/com/android/keyguard/CarrierTextControllerTest.java b/packages/SystemUI/tests/src/com/android/keyguard/CarrierTextControllerTest.java index 8e061cc84396..df534d79f730 100644 --- a/packages/SystemUI/tests/src/com/android/keyguard/CarrierTextControllerTest.java +++ b/packages/SystemUI/tests/src/com/android/keyguard/CarrierTextControllerTest.java @@ -37,6 +37,7 @@ import android.net.ConnectivityManager; import android.net.wifi.WifiManager; import android.os.Handler; import android.telephony.SubscriptionInfo; +import android.telephony.SubscriptionManager; import android.telephony.TelephonyManager; import android.test.suitebuilder.annotation.SmallTest; import android.testing.AndroidTestingRunner; @@ -81,6 +82,8 @@ public class CarrierTextControllerTest extends SysuiTestCase { private ConnectivityManager mConnectivityManager; @Mock private TelephonyManager mTelephonyManager; + @Mock + private SubscriptionManager mSubscriptionManager; private CarrierTextController.CarrierTextCallbackInfo mCarrierTextCallbackInfo; private CarrierTextController mCarrierTextController; @@ -94,6 +97,7 @@ public class CarrierTextControllerTest extends SysuiTestCase { mContext.addMockSystemService(WifiManager.class, mWifiManager); mContext.addMockSystemService(ConnectivityManager.class, mConnectivityManager); mContext.addMockSystemService(TelephonyManager.class, mTelephonyManager); + mContext.addMockSystemService(SubscriptionManager.class, mSubscriptionManager); mDependency.injectMockDependency(WakefulnessLifecycle.class); mDependency.injectTestDependency(Dependency.MAIN_HANDLER, new Handler(mTestableLooper.getLooper())); @@ -169,6 +173,7 @@ public class CarrierTextControllerTest extends SysuiTestCase { list.add(TEST_SUBSCRIPTION); when(mKeyguardUpdateMonitor.getSimState(anyInt())).thenReturn(IccCardConstants.State.READY); when(mKeyguardUpdateMonitor.getSubscriptionInfo(anyBoolean())).thenReturn(list); + when(mSubscriptionManager.getActiveSubscriptionInfoList(anyBoolean())).thenReturn(list); mKeyguardUpdateMonitor.mServiceStates = new HashMap<>(); ArgumentCaptor<CarrierTextController.CarrierTextCallbackInfo> captor = @@ -192,6 +197,7 @@ public class CarrierTextControllerTest extends SysuiTestCase { list.add(TEST_SUBSCRIPTION_ROAMING); when(mKeyguardUpdateMonitor.getSimState(anyInt())).thenReturn(IccCardConstants.State.READY); when(mKeyguardUpdateMonitor.getSubscriptionInfo(anyBoolean())).thenReturn(list); + when(mSubscriptionManager.getActiveSubscriptionInfoList(anyBoolean())).thenReturn(list); mKeyguardUpdateMonitor.mServiceStates = new HashMap<>(); ArgumentCaptor<CarrierTextController.CarrierTextCallbackInfo> captor = diff --git a/packages/SystemUI/tests/src/com/android/keyguard/clock/StretchAnalogClockControllerTest.java b/packages/SystemUI/tests/src/com/android/keyguard/clock/AnalogClockControllerTest.java index 26fa62b77d9a..4bb2395cc92d 100644 --- a/packages/SystemUI/tests/src/com/android/keyguard/clock/StretchAnalogClockControllerTest.java +++ b/packages/SystemUI/tests/src/com/android/keyguard/clock/AnalogClockControllerTest.java @@ -38,9 +38,9 @@ import org.mockito.MockitoAnnotations; @SmallTest @RunWith(AndroidTestingRunner.class) @RunWithLooper -public final class StretchAnalogClockControllerTest extends SysuiTestCase { +public final class AnalogClockControllerTest extends SysuiTestCase { - private StretchAnalogClockController mClockController; + private AnalogClockController mClockController; @Mock SysuiColorExtractor mMockColorExtractor; @Before @@ -49,7 +49,7 @@ public final class StretchAnalogClockControllerTest extends SysuiTestCase { Resources res = getContext().getResources(); LayoutInflater layoutInflater = LayoutInflater.from(getContext()); - mClockController = new StretchAnalogClockController(res, layoutInflater, + mClockController = new AnalogClockController(res, layoutInflater, mMockColorExtractor); } diff --git a/packages/SystemUI/tests/src/com/android/systemui/bubbles/BubbleControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/bubbles/BubbleControllerTest.java index 20f539bbebe5..40a53571ddf0 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/bubbles/BubbleControllerTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/bubbles/BubbleControllerTest.java @@ -146,7 +146,7 @@ public class BubbleControllerTest extends SysuiTestCase { when(mNotificationEntryManager.getNotificationData()).thenReturn(mNotificationData); when(mNotificationData.getChannel(mRow.getEntry().key)).thenReturn(mRow.getEntry().channel); - mBubbleData = new BubbleData(); + mBubbleData = new BubbleData(mContext); mBubbleController = new TestableBubbleController(mContext, mStatusBarWindowController, mBubbleData, mConfigurationController); mBubbleController.setBubbleStateChangeListener(mBubbleStateChangeListener); @@ -177,25 +177,27 @@ public class BubbleControllerTest extends SysuiTestCase { public void testRemoveBubble() { mBubbleController.updateBubble(mRow.getEntry()); assertTrue(mBubbleController.hasBubbles()); - + verify(mNotificationEntryManager).updateNotifications(); verify(mBubbleStateChangeListener).onHasBubblesChanged(true); mBubbleController.removeBubble(mRow.getEntry().key, BubbleController.DISMISS_USER_GESTURE); assertFalse(mStatusBarWindowController.getBubblesShowing()); assertTrue(mRow.getEntry().isBubbleDismissed()); - verify(mNotificationEntryManager).updateNotifications(); + verify(mNotificationEntryManager, times(2)).updateNotifications(); verify(mBubbleStateChangeListener).onHasBubblesChanged(false); } @Test public void testDismissStack() { mBubbleController.updateBubble(mRow.getEntry()); + verify(mNotificationEntryManager, times(1)).updateNotifications(); mBubbleController.updateBubble(mRow2.getEntry()); + verify(mNotificationEntryManager, times(2)).updateNotifications(); assertTrue(mBubbleController.hasBubbles()); mBubbleController.dismissStack(BubbleController.DISMISS_USER_GESTURE); assertFalse(mStatusBarWindowController.getBubblesShowing()); - verify(mNotificationEntryManager).updateNotifications(); + verify(mNotificationEntryManager, times(3)).updateNotifications(); assertTrue(mRow.getEntry().isBubbleDismissed()); assertTrue(mRow2.getEntry().isBubbleDismissed()); } @@ -215,7 +217,7 @@ public class BubbleControllerTest extends SysuiTestCase { // Expand the stack BubbleStackView stackView = mBubbleController.getStackView(); - stackView.expandStack(); + mBubbleController.expandStack(); assertTrue(mBubbleController.isStackExpanded()); verify(mBubbleExpandListener).onBubbleExpandChanged(true, mRow.getEntry().key); assertTrue(mStatusBarWindowController.getBubbleExpanded()); @@ -224,7 +226,7 @@ public class BubbleControllerTest extends SysuiTestCase { assertFalse(mRow.getEntry().showInShadeWhenBubble()); // Collapse - stackView.collapseStack(); + mBubbleController.collapseStack(); verify(mBubbleExpandListener).onBubbleExpandChanged(false, mRow.getEntry().key); assertFalse(mBubbleController.isStackExpanded()); assertFalse(mStatusBarWindowController.getBubbleExpanded()); @@ -245,23 +247,24 @@ public class BubbleControllerTest extends SysuiTestCase { // Expand BubbleStackView stackView = mBubbleController.getStackView(); - stackView.expandStack(); + mBubbleController.expandStack(); assertTrue(mBubbleController.isStackExpanded()); - verify(mBubbleExpandListener).onBubbleExpandChanged(true, mRow2.getEntry().key); - - // Last added is the one that is expanded - assertEquals(mRow2.getEntry(), stackView.getExpandedBubbleView().getEntry()); - assertFalse(mRow2.getEntry().showInShadeWhenBubble()); + verify(mBubbleExpandListener).onBubbleExpandChanged(true, mRow.getEntry().key); - // Switch which bubble is expanded - stackView.setExpandedBubble(mRow.getEntry()); + // First added is the one that is expanded assertEquals(mRow.getEntry(), stackView.getExpandedBubbleView().getEntry()); assertFalse(mRow.getEntry().showInShadeWhenBubble()); + // Switch which bubble is expanded + mBubbleController.selectBubble(mRow2.getEntry().key); + stackView.setExpandedBubble(mRow2.getEntry()); + assertEquals(mRow2.getEntry(), stackView.getExpandedBubbleView().getEntry()); + assertFalse(mRow2.getEntry().showInShadeWhenBubble()); + // collapse for previous bubble - verify(mBubbleExpandListener).onBubbleExpandChanged(false, mRow2.getEntry().key); + verify(mBubbleExpandListener).onBubbleExpandChanged(false, mRow.getEntry().key); // expand for selected bubble - verify(mBubbleExpandListener).onBubbleExpandChanged(true, mRow.getEntry().key); + verify(mBubbleExpandListener).onBubbleExpandChanged(true, mRow2.getEntry().key); // Collapse mBubbleController.collapseStack(); @@ -280,7 +283,7 @@ public class BubbleControllerTest extends SysuiTestCase { // Expand BubbleStackView stackView = mBubbleController.getStackView(); - stackView.expandStack(); + mBubbleController.expandStack(); assertTrue(mBubbleController.isStackExpanded()); verify(mBubbleExpandListener).onBubbleExpandChanged(true, mRow.getEntry().key); @@ -299,30 +302,30 @@ public class BubbleControllerTest extends SysuiTestCase { // Expand BubbleStackView stackView = mBubbleController.getStackView(); - stackView.expandStack(); + mBubbleController.expandStack(); assertTrue(mBubbleController.isStackExpanded()); - verify(mBubbleExpandListener).onBubbleExpandChanged(true, mRow2.getEntry().key); + verify(mBubbleExpandListener).onBubbleExpandChanged(true, mRow.getEntry().key); - // Last added is the one that is expanded - assertEquals(mRow2.getEntry(), stackView.getExpandedBubbleView().getEntry()); - assertFalse(mRow2.getEntry().showInShadeWhenBubble()); + // First added is the one that is expanded + assertEquals(mRow.getEntry(), stackView.getExpandedBubbleView().getEntry()); + assertFalse(mRow.getEntry().showInShadeWhenBubble()); // Dismiss currently expanded mBubbleController.removeBubble(stackView.getExpandedBubbleView().getKey(), BubbleController.DISMISS_USER_GESTURE); - verify(mBubbleExpandListener).onBubbleExpandChanged(false, mRow2.getEntry().key); + verify(mBubbleExpandListener).onBubbleExpandChanged(false, mRow.getEntry().key); // Make sure next bubble is selected - assertEquals(mRow.getEntry(), stackView.getExpandedBubbleView().getEntry()); - verify(mBubbleExpandListener).onBubbleExpandChanged(true, mRow.getEntry().key); + assertEquals(mRow2.getEntry(), stackView.getExpandedBubbleView().getEntry()); + verify(mBubbleExpandListener).onBubbleExpandChanged(true, mRow2.getEntry().key); // Dismiss that one mBubbleController.removeBubble(stackView.getExpandedBubbleView().getKey(), BubbleController.DISMISS_USER_GESTURE); // Make sure state changes and collapse happens - verify(mBubbleExpandListener).onBubbleExpandChanged(false, mRow.getEntry().key); + verify(mBubbleExpandListener).onBubbleExpandChanged(false, mRow2.getEntry().key); verify(mBubbleStateChangeListener).onHasBubblesChanged(false); assertFalse(mBubbleController.hasBubbles()); } diff --git a/packages/SystemUI/tests/src/com/android/systemui/bubbles/BubbleStackViewTest.java b/packages/SystemUI/tests/src/com/android/systemui/bubbles/BubbleStackViewTest.java index 801308fc77da..da31134d13b4 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/bubbles/BubbleStackViewTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/bubbles/BubbleStackViewTest.java @@ -48,7 +48,7 @@ public class BubbleStackViewTest extends SysuiTestCase { @Before public void setUp() throws Exception { MockitoAnnotations.initMocks(this); - mStackView = new BubbleStackView(mContext, new BubbleData(), null); + mStackView = new BubbleStackView(mContext, new BubbleData(getContext()), null); mBubble.entry = mNotifEntry; } diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyguard/KeyguardSliceProviderTest.java b/packages/SystemUI/tests/src/com/android/systemui/keyguard/KeyguardSliceProviderTest.java index 63d42a70aa98..c534de7e24a3 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/keyguard/KeyguardSliceProviderTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/keyguard/KeyguardSliceProviderTest.java @@ -24,6 +24,7 @@ import static org.mockito.Mockito.never; import static org.mockito.Mockito.reset; import static org.mockito.Mockito.spy; import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.when; import android.app.AlarmManager; import android.content.ContentResolver; @@ -102,6 +103,7 @@ public class KeyguardSliceProviderTest extends SysuiTestCase { @Test public void onBindSlice_readsMedia() { MediaMetadata metadata = mock(MediaMetadata.class); + when(metadata.getText(any())).thenReturn("metadata"); mProvider.onDozingChanged(true); mProvider.onMetadataChanged(metadata); mProvider.onBindSlice(mProvider.getUri()); diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/NotificationViewHierarchyManagerTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/NotificationViewHierarchyManagerTest.java index 2172a125ba85..3662c3860177 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/NotificationViewHierarchyManagerTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/NotificationViewHierarchyManagerTest.java @@ -98,7 +98,7 @@ public class NotificationViewHierarchyManagerTest extends SysuiTestCase { mViewHierarchyManager = new NotificationViewHierarchyManager(mContext, mLockscreenUserManager, mGroupManager, mVisualStabilityManager, mock(StatusBarStateControllerImpl.class), mEntryManager, - () -> mShadeController, new BubbleData()); + () -> mShadeController, new BubbleData(mContext)); Dependency.get(InitController.class).executePostInitTasks(); mViewHierarchyManager.setUpWithPresenter(mPresenter, mListContainer); } diff --git a/services/autofill/java/com/android/server/autofill/AutofillManagerService.java b/services/autofill/java/com/android/server/autofill/AutofillManagerService.java index fdc3567aa002..a94d1dc7f243 100644 --- a/services/autofill/java/com/android/server/autofill/AutofillManagerService.java +++ b/services/autofill/java/com/android/server/autofill/AutofillManagerService.java @@ -806,6 +806,17 @@ public final class AutofillManagerService mAugmentedAutofillState.injectAugmentedAutofillInfo(options, userId, packageName); return options; } + + @Override + public boolean isAugmentedAutofillServiceForUser(int callingUid, int userId) { + synchronized (mLock) { + final AutofillManagerServiceImpl service = peekServiceForUserLocked(userId); + if (service != null) { + return service.isAugmentedAutofillServiceForUserLocked(callingUid); + } + } + return false; + } } /** diff --git a/services/autofill/java/com/android/server/autofill/AutofillManagerServiceImpl.java b/services/autofill/java/com/android/server/autofill/AutofillManagerServiceImpl.java index 4bd6fbd39de4..f1963b37a6cf 100644 --- a/services/autofill/java/com/android/server/autofill/AutofillManagerServiceImpl.java +++ b/services/autofill/java/com/android/server/autofill/AutofillManagerServiceImpl.java @@ -1165,6 +1165,11 @@ final class AutofillManagerServiceImpl return true; } + boolean isAugmentedAutofillServiceForUserLocked(int callingUid) { + return mRemoteAugmentedAutofillServiceInfo != null + && mRemoteAugmentedAutofillServiceInfo.applicationInfo.uid == callingUid; + } + /** * Sets which packages and activities can trigger augmented autofill. * diff --git a/services/backup/java/com/android/server/backup/UserBackupManagerService.java b/services/backup/java/com/android/server/backup/UserBackupManagerService.java index 447bd8c237dd..03f21498766d 100644 --- a/services/backup/java/com/android/server/backup/UserBackupManagerService.java +++ b/services/backup/java/com/android/server/backup/UserBackupManagerService.java @@ -566,10 +566,6 @@ public class UserBackupManagerService { // require frequent starting and stopping. mConstants.start(); - // Set up the various sorts of package tracking we do - mFullBackupScheduleFile = new File(mBaseStateDir, "fb-schedule"); - initPackageTracking(); - // Build our mapping of uid to backup client services. This implicitly // schedules a backup pass on the Package Manager metadata the first // time anything needs to be backed up. @@ -589,6 +585,10 @@ public class UserBackupManagerService { // Power management mWakelock = mPowerManager.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK, "*backup*-" + userId); + + // Set up the various sorts of package tracking we do + mFullBackupScheduleFile = new File(mBaseStateDir, "fb-schedule"); + initPackageTracking(); } void initializeBackupEnableState() { @@ -744,6 +744,11 @@ public class UserBackupManagerService { return mDataDir; } + @VisibleForTesting + BroadcastReceiver getPackageTrackingReceiver() { + return mBroadcastReceiver; + } + @Nullable public DataChangedJournal getJournal() { return mJournal; diff --git a/services/backup/java/com/android/server/backup/utils/AppBackupUtils.java b/services/backup/java/com/android/server/backup/utils/AppBackupUtils.java index 593bb2c41587..35dfccf32924 100644 --- a/services/backup/java/com/android/server/backup/utils/AppBackupUtils.java +++ b/services/backup/java/com/android/server/backup/utils/AppBackupUtils.java @@ -23,22 +23,20 @@ import static com.android.server.backup.UserBackupManagerService.SHARED_BACKUP_A import static com.android.server.pm.PackageManagerService.PLATFORM_PACKAGE_NAME; import android.annotation.Nullable; -import android.app.AppGlobals; import android.app.backup.BackupTransport; import android.content.pm.ApplicationInfo; -import android.content.pm.IPackageManager; import android.content.pm.PackageInfo; import android.content.pm.PackageManager; import android.content.pm.PackageManagerInternal; import android.content.pm.Signature; import android.content.pm.SigningInfo; -import android.os.RemoteException; import android.os.UserHandle; import android.util.Slog; import com.android.internal.annotations.VisibleForTesting; import com.android.internal.backup.IBackupTransport; import com.android.internal.util.ArrayUtils; +import com.android.server.LocalServices; import com.android.server.backup.transport.TransportClient; import com.google.android.collect.Sets; @@ -67,12 +65,13 @@ public class AppBackupUtils { * </ol> */ public static boolean appIsEligibleForBackup(ApplicationInfo app, int userId) { - return appIsEligibleForBackup(app, AppGlobals.getPackageManager(), userId); + return appIsEligibleForBackup( + app, LocalServices.getService(PackageManagerInternal.class), userId); } @VisibleForTesting - static boolean appIsEligibleForBackup(ApplicationInfo app, - IPackageManager packageManager, int userId) { + static boolean appIsEligibleForBackup( + ApplicationInfo app, PackageManagerInternal packageManager, int userId) { // 1. their manifest states android:allowBackup="false" if ((app.flags & ApplicationInfo.FLAG_ALLOW_BACKUP) == 0) { return false; @@ -108,9 +107,9 @@ public class AppBackupUtils { /** * Returns whether an app is eligible for backup at runtime. That is, the app has to: * <ol> - * <li>Return true for {@link #appIsEligibleForBackup(ApplicationInfo, PackageManager)} + * <li>Return true for {@link #appIsEligibleForBackup(ApplicationInfo, int)} * <li>Return false for {@link #appIsStopped(ApplicationInfo)} - * <li>Return false for {@link #appIsDisabled(ApplicationInfo, PackageManager)} + * <li>Return false for {@link #appIsDisabled(ApplicationInfo, int)} * <li>Be eligible for the transport via * {@link BackupTransport#isAppEligibleForBackup(PackageInfo, boolean)} * </ol> @@ -149,19 +148,13 @@ public class AppBackupUtils { /** Avoid backups of 'disabled' apps. */ static boolean appIsDisabled(ApplicationInfo app, int userId) { - return appIsDisabled(app, AppGlobals.getPackageManager(), userId); + return appIsDisabled(app, LocalServices.getService(PackageManagerInternal.class), userId); } @VisibleForTesting - static boolean appIsDisabled(ApplicationInfo app, - IPackageManager packageManager, int userId) { - int enabledSetting; - try { - enabledSetting = packageManager.getApplicationEnabledSetting(app.packageName, userId); - } catch (RemoteException e) { - Slog.e(TAG, "Failed to get application enabled setting: " + e); - return false; - } + static boolean appIsDisabled( + ApplicationInfo app, PackageManagerInternal packageManager, int userId) { + int enabledSetting = packageManager.getApplicationEnabledState(app.packageName, userId); switch (enabledSetting) { case PackageManager.COMPONENT_ENABLED_STATE_DISABLED: diff --git a/services/core/java/com/android/server/StorageManagerService.java b/services/core/java/com/android/server/StorageManagerService.java index 382fdec94f08..5c1806d447f6 100644 --- a/services/core/java/com/android/server/StorageManagerService.java +++ b/services/core/java/com/android/server/StorageManagerService.java @@ -1074,6 +1074,15 @@ class StorageManagerService extends IStorageManager.Stub mVold.onUserStarted(userId, packages, appIds, sandboxIds); } + private boolean supportsBlockCheckpoint() throws RemoteException { + // Only the system process is permitted to start checkpoints + if (Binder.getCallingUid() != android.os.Process.SYSTEM_UID) { + throw new SecurityException("no permission to check block based checkpoint support"); + } + + return mVold.supportsBlockCheckpoint(); + } + @Override public void onAwakeStateChanged(boolean isAwake) { // Ignored @@ -2116,37 +2125,45 @@ class StorageManagerService extends IStorageManager.Stub enforcePermission(android.Manifest.permission.MOUNT_FORMAT_FILESYSTEMS); try { - mVold.fstrim(flags, new IVoldTaskListener.Stub() { - @Override - public void onStatus(int status, PersistableBundle extras) { - dispatchOnStatus(listener, status, extras); + // Block based checkpoint process runs fstrim. So, if checkpoint is in progress + // (first boot after OTA), We skip idle maintenance and make sure the last + // fstrim time is still updated. If file based checkpoints are used, we run + // idle maintenance (GC + fstrim) regardless of checkpoint status. + if (!needsCheckpoint() || !supportsBlockCheckpoint()) { + mVold.fstrim(flags, new IVoldTaskListener.Stub() { + @Override + public void onStatus(int status, PersistableBundle extras) { + dispatchOnStatus(listener, status, extras); - // Ignore trim failures - if (status != 0) return; + // Ignore trim failures + if (status != 0) return; - final String path = extras.getString("path"); - final long bytes = extras.getLong("bytes"); - final long time = extras.getLong("time"); + final String path = extras.getString("path"); + final long bytes = extras.getLong("bytes"); + final long time = extras.getLong("time"); - final DropBoxManager dropBox = mContext.getSystemService(DropBoxManager.class); - dropBox.addText(TAG_STORAGE_TRIM, scrubPath(path) + " " + bytes + " " + time); + final DropBoxManager dropBox = mContext.getSystemService(DropBoxManager.class); + dropBox.addText(TAG_STORAGE_TRIM, scrubPath(path) + " " + bytes + " " + time); - synchronized (mLock) { - final VolumeRecord rec = findRecordForPath(path); - if (rec != null) { - rec.lastTrimMillis = System.currentTimeMillis(); - writeSettingsLocked(); + synchronized (mLock) { + final VolumeRecord rec = findRecordForPath(path); + if (rec != null) { + rec.lastTrimMillis = System.currentTimeMillis(); + writeSettingsLocked(); + } } } - } - @Override - public void onFinished(int status, PersistableBundle extras) { - dispatchOnFinished(listener, status, extras); + @Override + public void onFinished(int status, PersistableBundle extras) { + dispatchOnFinished(listener, status, extras); - // TODO: benchmark when desired - } - }); + // TODO: benchmark when desired + } + }); + } else { + Slog.i(TAG, "Skipping fstrim - block based checkpoint in progress"); + } } catch (RemoteException e) { throw e.rethrowAsRuntimeException(); } @@ -2156,18 +2173,26 @@ class StorageManagerService extends IStorageManager.Stub enforcePermission(android.Manifest.permission.MOUNT_FORMAT_FILESYSTEMS); try { - mVold.runIdleMaint(new IVoldTaskListener.Stub() { - @Override - public void onStatus(int status, PersistableBundle extras) { - // Not currently used - } - @Override - public void onFinished(int status, PersistableBundle extras) { - if (callback != null) { - BackgroundThread.getHandler().post(callback); + // Block based checkpoint process runs fstrim. So, if checkpoint is in progress + // (first boot after OTA), We skip idle maintenance and make sure the last + // fstrim time is still updated. If file based checkpoints are used, we run + // idle maintenance (GC + fstrim) regardless of checkpoint status. + if (!needsCheckpoint() || !supportsBlockCheckpoint()) { + mVold.runIdleMaint(new IVoldTaskListener.Stub() { + @Override + public void onStatus(int status, PersistableBundle extras) { + // Not currently used } - } - }); + @Override + public void onFinished(int status, PersistableBundle extras) { + if (callback != null) { + BackgroundThread.getHandler().post(callback); + } + } + }); + } else { + Slog.i(TAG, "Skipping idle maintenance - block based checkpoint in progress"); + } } catch (Exception e) { Slog.wtf(TAG, e); } @@ -3766,7 +3791,7 @@ class StorageManagerService extends IStorageManager.Stub return Zygote.MOUNT_EXTERNAL_NONE; } if (mPmInternal.isInstantApp(packageName, UserHandle.getUserId(uid))) { - return Zygote.MOUNT_EXTERNAL_NONE; + return Zygote.MOUNT_EXTERNAL_DEFAULT; } // Determine if caller is holding runtime permission diff --git a/services/core/java/com/android/server/am/ActiveServices.java b/services/core/java/com/android/server/am/ActiveServices.java index 4ec90ba5b803..0271354b3fff 100644 --- a/services/core/java/com/android/server/am/ActiveServices.java +++ b/services/core/java/com/android/server/am/ActiveServices.java @@ -1806,7 +1806,7 @@ public final class ActiveServices { || (callerApp.getCurProcState() <= ActivityManager.PROCESS_STATE_TOP && (flags & Context.BIND_TREAT_LIKE_ACTIVITY) != 0), b.client); - mAm.updateOomAdjLocked(); + mAm.updateOomAdjLocked(OomAdjuster.OOM_ADJ_REASON_BIND_SERVICE); } if (DEBUG_SERVICE) Slog.v(TAG_SERVICE, "Bind " + s + " with " + b @@ -1957,11 +1957,12 @@ public final class ActiveServices { r.binding.service.app.hasClientActivities() || r.binding.service.app.treatLikeActivity, null); } - mAm.updateOomAdjLocked(r.binding.service.app, false); + mAm.updateOomAdjLocked(r.binding.service.app, false, + OomAdjuster.OOM_ADJ_REASON_UNBIND_SERVICE); } } - mAm.updateOomAdjLocked(); + mAm.updateOomAdjLocked(OomAdjuster.OOM_ADJ_REASON_UNBIND_SERVICE); } finally { Binder.restoreCallingIdentity(origId); @@ -2676,7 +2677,7 @@ public final class ActiveServices { bumpServiceExecutingLocked(r, execInFg, "create"); mAm.updateLruProcessLocked(app, false, null); updateServiceForegroundLocked(r.app, /* oomAdj= */ false); - mAm.updateOomAdjLocked(); + mAm.updateOomAdjLocked(OomAdjuster.OOM_ADJ_REASON_START_SERVICE); boolean created = false; try { @@ -2797,7 +2798,7 @@ public final class ActiveServices { bumpServiceExecutingLocked(r, execInFg, "start"); if (!oomAdjusted) { oomAdjusted = true; - mAm.updateOomAdjLocked(r.app, true); + mAm.updateOomAdjLocked(r.app, true, OomAdjuster.OOM_ADJ_REASON_START_SERVICE); } if (r.fgRequired && !r.fgWaiting) { if (!r.isForeground) { @@ -2923,7 +2924,8 @@ public final class ActiveServices { if (ibr.hasBound) { try { bumpServiceExecutingLocked(r, false, "bring down unbind"); - mAm.updateOomAdjLocked(r.app, true); + mAm.updateOomAdjLocked(r.app, true, + OomAdjuster.OOM_ADJ_REASON_UNBIND_SERVICE); ibr.hasBound = false; ibr.requested = false; r.app.thread.scheduleUnbindService(r, @@ -3038,7 +3040,8 @@ public final class ActiveServices { bumpServiceExecutingLocked(r, false, "destroy"); mDestroyingServices.add(r); r.destroying = true; - mAm.updateOomAdjLocked(r.app, true); + mAm.updateOomAdjLocked(r.app, true, + OomAdjuster.OOM_ADJ_REASON_UNBIND_SERVICE); r.app.thread.scheduleStopService(r); } catch (Exception e) { Slog.w(TAG, "Exception when destroying service " @@ -3143,7 +3146,8 @@ public final class ActiveServices { // it to go down there and we want it to start out near the top. mAm.updateLruProcessLocked(s.app, false, null); } - mAm.updateOomAdjLocked(s.app, true); + mAm.updateOomAdjLocked(s.app, true, + OomAdjuster.OOM_ADJ_REASON_UNBIND_SERVICE); b.intent.hasBound = false; // Assume the client doesn't want to know about a rebind; // we will deal with that later if it asks for one. @@ -3296,7 +3300,7 @@ public final class ActiveServices { mDestroyingServices.remove(r); r.bindings.clear(); } - mAm.updateOomAdjLocked(r.app, true); + mAm.updateOomAdjLocked(r.app, true, OomAdjuster.OOM_ADJ_REASON_UNBIND_SERVICE); } r.executeFg = false; if (r.tracker != null) { diff --git a/services/core/java/com/android/server/am/ActivityManagerService.java b/services/core/java/com/android/server/am/ActivityManagerService.java index 3b6b404170c5..cdcd9e1ad58c 100644 --- a/services/core/java/com/android/server/am/ActivityManagerService.java +++ b/services/core/java/com/android/server/am/ActivityManagerService.java @@ -1972,7 +1972,7 @@ public class ActivityManagerService extends IActivityManager.Stub app.makeActive(mSystemThread.getApplicationThread(), mProcessStats); mPidsSelfLocked.put(app.pid, app); mProcessList.updateLruProcessLocked(app, false, null); - updateOomAdjLocked(); + updateOomAdjLocked(OomAdjuster.OOM_ADJ_REASON_NONE); } } catch (PackageManager.NameNotFoundException e) { throw new RuntimeException( @@ -2456,7 +2456,7 @@ public class ActivityManagerService extends IActivityManager.Stub // bind background threads to little cores // this is expected to fail inside of framework tests because apps can't touch cpusets directly // make sure we've already adjusted system_server's internal view of itself first - updateOomAdjLocked(); + updateOomAdjLocked(OomAdjuster.OOM_ADJ_REASON_NONE); try { Process.setThreadGroupAndCpuset(BackgroundThread.get().getThreadId(), Process.THREAD_GROUP_SYSTEM); @@ -3634,7 +3634,7 @@ public class ActivityManagerService extends IActivityManager.Stub handleAppDiedLocked(app, false, true); if (doOomAdj) { - updateOomAdjLocked(); + updateOomAdjLocked(OomAdjuster.OOM_ADJ_REASON_PROCESS_END); } if (doLowMem) { doLowMemReportIfNeededLocked(app); @@ -5002,7 +5002,7 @@ public class ActivityManagerService extends IActivityManager.Stub } if (!didSomething) { - updateOomAdjLocked(); + updateOomAdjLocked(OomAdjuster.OOM_ADJ_REASON_PROCESS_BEGIN); checkTime(startTime, "attachApplicationLocked: after updateOomAdjLocked"); } @@ -5485,7 +5485,7 @@ public class ActivityManagerService extends IActivityManager.Stub synchronized (this) { mConstants.setOverrideMaxCachedProcesses(max); } - trimApplications(); + trimApplications(OomAdjuster.OOM_ADJ_REASON_PROCESS_END); } @Override @@ -5511,7 +5511,7 @@ public class ActivityManagerService extends IActivityManager.Stub pr.forcingToImportant = null; updateProcessForegroundLocked(pr, false, 0, false); } - updateOomAdjLocked(); + updateOomAdjLocked(OomAdjuster.OOM_ADJ_REASON_UI_VISIBILITY); } } @@ -5557,7 +5557,7 @@ public class ActivityManagerService extends IActivityManager.Stub } if (changed) { - updateOomAdjLocked(); + updateOomAdjLocked(OomAdjuster.OOM_ADJ_REASON_UI_VISIBILITY); } } } @@ -6714,7 +6714,8 @@ public class ActivityManagerService extends IActivityManager.Stub checkTime(startTime, "getContentProviderImpl: before updateOomAdj"); final int verifiedAdj = cpr.proc.verifiedAdj; - boolean success = updateOomAdjLocked(cpr.proc, true); + boolean success = updateOomAdjLocked(cpr.proc, true, + OomAdjuster.OOM_ADJ_REASON_GET_PROVIDER); // XXX things have changed so updateOomAdjLocked doesn't actually tell us // if the process has been successfully adjusted. So to reduce races with // it, we will check whether the process still exists. Note that this doesn't @@ -7147,7 +7148,7 @@ public class ActivityManagerService extends IActivityManager.Stub throw new NullPointerException("connection is null"); } if (decProviderCountLocked(conn, null, null, stable)) { - updateOomAdjLocked(); + updateOomAdjLocked(OomAdjuster.OOM_ADJ_REASON_REMOVE_PROVIDER); } } } finally { @@ -7188,7 +7189,7 @@ public class ActivityManagerService extends IActivityManager.Stub ContentProviderRecord localCpr = mProviderMap.getProviderByClass(comp, userId); if (localCpr.hasExternalProcessHandles()) { if (localCpr.removeExternalProcessHandleLocked(token)) { - updateOomAdjLocked(); + updateOomAdjLocked(OomAdjuster.OOM_ADJ_REASON_REMOVE_PROVIDER); } else { Slog.e(TAG, "Attmpt to remove content provider " + localCpr + " with no external reference for token: " @@ -7255,7 +7256,7 @@ public class ActivityManagerService extends IActivityManager.Stub dst.setProcess(r); dst.notifyAll(); } - updateOomAdjLocked(r, true); + updateOomAdjLocked(r, true, OomAdjuster.OOM_ADJ_REASON_GET_PROVIDER); maybeUpdateProviderUsageStatsLocked(r, src.info.packageName, src.info.authority); } @@ -7645,7 +7646,7 @@ public class ActivityManagerService extends IActivityManager.Stub new HostingRecord("added application", customProcess != null ? customProcess : info.processName)); mProcessList.updateLruProcessLocked(app, false, null); - updateOomAdjLocked(); + updateOomAdjLocked(OomAdjuster.OOM_ADJ_REASON_PROCESS_BEGIN); } // This package really, really can not be stopped. @@ -7740,7 +7741,7 @@ public class ActivityManagerService extends IActivityManager.Stub mActivityTaskManager.onScreenAwakeChanged(isAwake); mOomAdjProfiler.onWakefulnessChanged(wakefulness); } - updateOomAdjLocked(); + updateOomAdjLocked(OomAdjuster.OOM_ADJ_REASON_UI_VISIBILITY); } } @@ -8320,7 +8321,7 @@ public class ActivityManagerService extends IActivityManager.Stub } } if (changed) { - updateOomAdjLocked(pr, true); + updateOomAdjLocked(pr, true, OomAdjuster.OOM_ADJ_REASON_UI_VISIBILITY); } } } finally { @@ -8350,7 +8351,7 @@ public class ActivityManagerService extends IActivityManager.Stub Slog.i(TAG, "Setting runningRemoteAnimation=" + pr.runningRemoteAnimation + " for pid=" + pid); } - updateOomAdjLocked(pr, true); + updateOomAdjLocked(pr, true, OomAdjuster.OOM_ADJ_REASON_UI_VISIBILITY); } } @@ -13984,7 +13985,7 @@ public class ActivityManagerService extends IActivityManager.Stub mBackupTargets.put(targetUserId, r); // Try not to kill the process during backup - updateOomAdjLocked(proc, true); + updateOomAdjLocked(proc, true, OomAdjuster.OOM_ADJ_REASON_NONE); // If the process is already attached, schedule the creation of the backup agent now. // If it is not yet live, this will be done when it attaches to the framework. @@ -14099,7 +14100,7 @@ public class ActivityManagerService extends IActivityManager.Stub // Not backing this app up any more; reset its OOM adjustment final ProcessRecord proc = backupTarget.app; - updateOomAdjLocked(proc, true); + updateOomAdjLocked(proc, true, OomAdjuster.OOM_ADJ_REASON_NONE); proc.inFullBackup = false; oldBackupUid = backupTarget != null ? backupTarget.appInfo.uid : -1; @@ -14388,7 +14389,7 @@ public class ActivityManagerService extends IActivityManager.Stub // If we actually concluded any broadcasts, we might now be able // to trim the recipients' apps from our working set if (doTrim) { - trimApplications(); + trimApplications(OomAdjuster.OOM_ADJ_REASON_FINISH_RECEIVER); return; } @@ -15463,7 +15464,7 @@ public class ActivityManagerService extends IActivityManager.Stub r.queue.processNextBroadcastLocked(/*fromMsg=*/ false, /*skipOomAdj=*/ true); } // updateOomAdjLocked() will be done here - trimApplicationsLocked(); + trimApplicationsLocked(OomAdjuster.OOM_ADJ_REASON_FINISH_RECEIVER); } } finally { @@ -16448,7 +16449,7 @@ public class ActivityManagerService extends IActivityManager.Stub item.foregroundServiceTypes = fgServiceTypes; if (oomAdj) { - updateOomAdjLocked(); + updateOomAdjLocked(OomAdjuster.OOM_ADJ_REASON_UI_VISIBILITY); } } } @@ -16496,11 +16497,13 @@ public class ActivityManagerService extends IActivityManager.Stub * @param app The process to update * @param oomAdjAll If it's ok to call updateOomAdjLocked() for all running apps * if necessary, or skip. + * @param oomAdjReason * @return whether updateOomAdjLocked(app) was successful. */ @GuardedBy("this") - final boolean updateOomAdjLocked(ProcessRecord app, boolean oomAdjAll) { - return mOomAdjuster.updateOomAdjLocked(app, oomAdjAll); + final boolean updateOomAdjLocked(ProcessRecord app, boolean oomAdjAll, + String oomAdjReason) { + return mOomAdjuster.updateOomAdjLocked(app, oomAdjAll, oomAdjReason); } static final class ProcStatsRunnable implements Runnable { @@ -16693,8 +16696,8 @@ public class ActivityManagerService extends IActivityManager.Stub } @GuardedBy("this") - final void updateOomAdjLocked() { - mOomAdjuster.updateOomAdjLocked(); + final void updateOomAdjLocked(String oomAdjReason) { + mOomAdjuster.updateOomAdjLocked(oomAdjReason); } @Override @@ -16979,14 +16982,14 @@ public class ActivityManagerService extends IActivityManager.Stub mOomAdjuster.setUidTempWhitelistStateLocked(uid, onWhitelist); } - final void trimApplications() { + final void trimApplications(String oomAdjReason) { synchronized (this) { - trimApplicationsLocked(); + trimApplicationsLocked(oomAdjReason); } } @GuardedBy("this") - final void trimApplicationsLocked() { + final void trimApplicationsLocked(String oomAdjReason) { // First remove any unused application processes whose package // has been removed. for (int i = mProcessList.mRemovedProcesses.size() - 1; i >= 0; i--) { @@ -17018,7 +17021,7 @@ public class ActivityManagerService extends IActivityManager.Stub // Now update the oom adj for all processes. Don't skip this, since other callers // might be depending on it. - updateOomAdjLocked(); + updateOomAdjLocked(oomAdjReason); } /** This method sends the specified signal to each of the persistent apps */ @@ -17627,7 +17630,7 @@ public class ActivityManagerService extends IActivityManager.Stub } pr.setHasOverlayUi(hasOverlayUi); //Slog.i(TAG, "Setting hasOverlayUi=" + pr.hasOverlayUi + " for pid=" + pid); - updateOomAdjLocked(pr, true); + updateOomAdjLocked(pr, true, OomAdjuster.OOM_ADJ_REASON_UI_VISIBILITY); } } @@ -17784,7 +17787,7 @@ public class ActivityManagerService extends IActivityManager.Stub @Override public void trimApplications() { - ActivityManagerService.this.trimApplications(); + ActivityManagerService.this.trimApplications(OomAdjuster.OOM_ADJ_REASON_ACTIVITY); } public void killProcessesForRemovedTask(ArrayList<Object> procsToKill) { @@ -17836,7 +17839,7 @@ public class ActivityManagerService extends IActivityManager.Stub @Override public void updateOomAdj() { synchronized (ActivityManagerService.this) { - ActivityManagerService.this.updateOomAdjLocked(); + ActivityManagerService.this.updateOomAdjLocked(OomAdjuster.OOM_ADJ_REASON_NONE); } } diff --git a/services/core/java/com/android/server/am/BroadcastQueue.java b/services/core/java/com/android/server/am/BroadcastQueue.java index 3c57c3bcb7d6..ee9b5614584f 100644 --- a/services/core/java/com/android/server/am/BroadcastQueue.java +++ b/services/core/java/com/android/server/am/BroadcastQueue.java @@ -309,7 +309,7 @@ public final class BroadcastQueue { app.forceProcessStateUpTo(ActivityManager.PROCESS_STATE_RECEIVER); mService.mProcessList.updateLruProcessLocked(app, false, null); if (!skipOomAdj) { - mService.updateOomAdjLocked(); + mService.updateOomAdjLocked(OomAdjuster.OOM_ADJ_REASON_NONE); } // Tell the application to launch this receiver. @@ -791,7 +791,8 @@ public final class BroadcastQueue { // are already core system stuff so don't matter for this. r.curApp = filter.receiverList.app; filter.receiverList.app.curReceivers.add(r); - mService.updateOomAdjLocked(r.curApp, true); + mService.updateOomAdjLocked(r.curApp, true, + OomAdjuster.OOM_ADJ_REASON_START_RECEIVER); } } try { @@ -1028,7 +1029,7 @@ public final class BroadcastQueue { // If we had finished the last ordered broadcast, then // make sure all processes have correct oom and sched // adjustments. - mService.updateOomAdjLocked(); + mService.updateOomAdjLocked(OomAdjuster.OOM_ADJ_REASON_START_RECEIVER); } // when we have no more ordered broadcast on this queue, stop logging diff --git a/services/core/java/com/android/server/am/OomAdjuster.java b/services/core/java/com/android/server/am/OomAdjuster.java index 44d435f2b539..8ae7c7d3e0c8 100644 --- a/services/core/java/com/android/server/am/OomAdjuster.java +++ b/services/core/java/com/android/server/am/OomAdjuster.java @@ -90,6 +90,21 @@ import java.util.Arrays; public final class OomAdjuster { private static final String TAG = "OomAdjuster"; + static final String OOM_ADJ_REASON_METHOD = "updateOomAdj"; + static final String OOM_ADJ_REASON_NONE = OOM_ADJ_REASON_METHOD + "_meh"; + static final String OOM_ADJ_REASON_ACTIVITY = OOM_ADJ_REASON_METHOD + "_activityChange"; + static final String OOM_ADJ_REASON_FINISH_RECEIVER = OOM_ADJ_REASON_METHOD + "_finishReceiver"; + static final String OOM_ADJ_REASON_START_RECEIVER = OOM_ADJ_REASON_METHOD + "_startReceiver"; + static final String OOM_ADJ_REASON_BIND_SERVICE = OOM_ADJ_REASON_METHOD + "_bindService"; + static final String OOM_ADJ_REASON_UNBIND_SERVICE = OOM_ADJ_REASON_METHOD + "_unbindService"; + static final String OOM_ADJ_REASON_START_SERVICE = OOM_ADJ_REASON_METHOD + "_startService"; + static final String OOM_ADJ_REASON_GET_PROVIDER = OOM_ADJ_REASON_METHOD + "_getProvider"; + static final String OOM_ADJ_REASON_REMOVE_PROVIDER = OOM_ADJ_REASON_METHOD + "_removeProvider"; + static final String OOM_ADJ_REASON_UI_VISIBILITY = OOM_ADJ_REASON_METHOD + "_uiVisibility"; + static final String OOM_ADJ_REASON_WHITELIST = OOM_ADJ_REASON_METHOD + "_whitelistChange"; + static final String OOM_ADJ_REASON_PROCESS_BEGIN = OOM_ADJ_REASON_METHOD + "_processBegin"; + static final String OOM_ADJ_REASON_PROCESS_END = OOM_ADJ_REASON_METHOD + "_processEnd"; + /** * For some direct access we need to power manager. */ @@ -156,10 +171,12 @@ public final class OomAdjuster { * @param app The process to update * @param oomAdjAll If it's ok to call updateOomAdjLocked() for all running apps * if necessary, or skip. + * @param oomAdjReason * @return whether updateOomAdjLocked(app) was successful. */ @GuardedBy("mService") - final boolean updateOomAdjLocked(ProcessRecord app, boolean oomAdjAll) { + boolean updateOomAdjLocked(ProcessRecord app, boolean oomAdjAll, + String oomAdjReason) { final ProcessRecord TOP_APP = mService.getTopAppLocked(); final boolean wasCached = app.cached; @@ -177,7 +194,7 @@ public final class OomAdjuster { && (wasCached != app.cached || app.getCurRawAdj() == ProcessList.UNKNOWN_ADJ)) { // Changed to/from cached state, so apps after it in the LRU // list may also be changed. - updateOomAdjLocked(); + updateOomAdjLocked(oomAdjReason); } return success; } @@ -195,8 +212,8 @@ public final class OomAdjuster { } @GuardedBy("mService") - final void updateOomAdjLocked() { - Trace.traceBegin(Trace.TRACE_TAG_ACTIVITY_MANAGER, "updateOomAdj"); + void updateOomAdjLocked(String oomAdjReason) { + Trace.traceBegin(Trace.TRACE_TAG_ACTIVITY_MANAGER, oomAdjReason); mService.mOomAdjProfiler.oomAdjStarted(); final ProcessRecord TOP_APP = mService.getTopAppLocked(); final long now = SystemClock.uptimeMillis(); @@ -2009,7 +2026,7 @@ public final class OomAdjuster { } } if (changed) { - updateOomAdjLocked(); + updateOomAdjLocked(OOM_ADJ_REASON_WHITELIST); } } @@ -2019,7 +2036,7 @@ public final class OomAdjuster { final UidRecord uidRec = mActiveUids.get(uid); if (uidRec != null && uidRec.curWhitelist != onWhitelist) { uidRec.curWhitelist = onWhitelist; - updateOomAdjLocked(); + updateOomAdjLocked(OOM_ADJ_REASON_WHITELIST); } } diff --git a/services/core/java/com/android/server/am/ProcessList.java b/services/core/java/com/android/server/am/ProcessList.java index 0a926f9a801a..316368a52ce2 100644 --- a/services/core/java/com/android/server/am/ProcessList.java +++ b/services/core/java/com/android/server/am/ProcessList.java @@ -2227,9 +2227,10 @@ public final class ProcessList { for (AppZygote appZygote : zygotesToKill) { killAppZygoteIfNeededLocked(appZygote); } - mService.updateOomAdjLocked(); + mService.updateOomAdjLocked(OomAdjuster.OOM_ADJ_REASON_PROCESS_END); return N > 0; } + @GuardedBy("mService") boolean removeProcessLocked(ProcessRecord app, boolean callerWillRestart, boolean allowRestart, String reason) { diff --git a/services/core/java/com/android/server/am/ProcessRecord.java b/services/core/java/com/android/server/am/ProcessRecord.java index 933f41c47167..49930c1f4d62 100644 --- a/services/core/java/com/android/server/am/ProcessRecord.java +++ b/services/core/java/com/android/server/am/ProcessRecord.java @@ -1301,7 +1301,7 @@ class ProcessRecord implements WindowProcessListener { } mService.mProcessList.updateLruProcessLocked(this, activityChange, null /* client */); if (updateOomAdj) { - mService.updateOomAdjLocked(); + mService.updateOomAdjLocked(OomAdjuster.OOM_ADJ_REASON_ACTIVITY); } } } diff --git a/services/core/java/com/android/server/attention/AttentionManagerService.java b/services/core/java/com/android/server/attention/AttentionManagerService.java index 411dd794d1bd..d7d4851bf33f 100644 --- a/services/core/java/com/android/server/attention/AttentionManagerService.java +++ b/services/core/java/com/android/server/attention/AttentionManagerService.java @@ -58,6 +58,7 @@ import android.util.StatsLog; import com.android.internal.R; import com.android.internal.annotations.GuardedBy; +import com.android.internal.annotations.VisibleForTesting; import com.android.internal.util.DumpUtils; import com.android.internal.util.IndentingPrintWriter; import com.android.internal.util.Preconditions; @@ -100,16 +101,25 @@ public class AttentionManagerService extends SystemService { private final Object mLock; @GuardedBy("mLock") private final SparseArray<UserState> mUserStates = new SparseArray<>(); - private final AttentionHandler mAttentionHandler; + private AttentionHandler mAttentionHandler; - private ComponentName mComponentName; + @VisibleForTesting + ComponentName mComponentName; public AttentionManagerService(Context context) { + this(context, (PowerManager) context.getSystemService(Context.POWER_SERVICE), + new Object(), null); + mAttentionHandler = new AttentionHandler(); + } + + @VisibleForTesting + AttentionManagerService(Context context, PowerManager powerManager, Object lock, + AttentionHandler handler) { super(context); mContext = Preconditions.checkNotNull(context); - mPowerManager = (PowerManager) mContext.getSystemService(Context.POWER_SERVICE); - mLock = new Object(); - mAttentionHandler = new AttentionHandler(); + mPowerManager = powerManager; + mLock = lock; + mAttentionHandler = handler; } @Override @@ -147,7 +157,8 @@ public class AttentionManagerService extends SystemService { return isServiceEnabled() && isServiceAvailable(); } - private boolean isServiceEnabled() { + @VisibleForTesting + protected boolean isServiceEnabled() { return DeviceConfig.getBoolean(NAMESPACE_ATTENTION_MANAGER_SERVICE, SERVICE_ENABLED, DEFAULT_SERVICE_ENABLED); } @@ -161,7 +172,8 @@ public class AttentionManagerService extends SystemService { * * @return {@code true} if the framework was able to dispatch the request */ - private boolean checkAttention(long timeout, AttentionCallbackInternal callbackInternal) { + @VisibleForTesting + boolean checkAttention(long timeout, AttentionCallbackInternal callbackInternal) { Preconditions.checkNotNull(callbackInternal); if (!isAttentionServiceSupported()) { @@ -257,24 +269,24 @@ public class AttentionManagerService extends SystemService { } /** Cancels the specified attention check. */ - private void cancelAttentionCheck(AttentionCallbackInternal callbackInternal) { + @VisibleForTesting + void cancelAttentionCheck(AttentionCallbackInternal callbackInternal) { synchronized (mLock) { final UserState userState = peekCurrentUserStateLocked(); if (userState == null) { return; } - if (!userState.mCurrentAttentionCheck.mCallbackInternal.equals(callbackInternal)) { Slog.e(LOG_TAG, "Cannot cancel a non-current request"); return; } - cancel(userState); } } @GuardedBy("mLock") - private void freeIfInactiveLocked() { + @VisibleForTesting + protected void freeIfInactiveLocked() { // If we are called here, it means someone used the API again - reset the timer then. mAttentionHandler.removeMessages(AttentionHandler.CHECK_CONNECTION_EXPIRATION); @@ -291,7 +303,8 @@ public class AttentionManagerService extends SystemService { @GuardedBy("mLock") - private UserState getOrCreateCurrentUserStateLocked() { + @VisibleForTesting + protected UserState getOrCreateCurrentUserStateLocked() { return getOrCreateUserStateLocked(ActivityManager.getCurrentUser()); } @@ -307,7 +320,8 @@ public class AttentionManagerService extends SystemService { @GuardedBy("mLock") @Nullable - private UserState peekCurrentUserStateLocked() { + @VisibleForTesting + protected UserState peekCurrentUserStateLocked() { return peekUserStateLocked(ActivityManager.getCurrentUser()); } @@ -418,7 +432,8 @@ public class AttentionManagerService extends SystemService { } } - private static final class AttentionCheck { + @VisibleForTesting + static final class AttentionCheck { private final AttentionCallbackInternal mCallbackInternal; private final IAttentionCallback mIAttentionCallback; private boolean mIsDispatched; @@ -435,7 +450,8 @@ public class AttentionManagerService extends SystemService { } } - private static final class UserState { + @VisibleForTesting + protected static class UserState { final ComponentName mComponentName; final AttentionServiceConnection mConnection = new AttentionServiceConnection(); @@ -453,7 +469,7 @@ public class AttentionManagerService extends SystemService { final Context mContext; final Object mLock; - private UserState(int userId, Context context, Object lock, ComponentName componentName) { + UserState(int userId, Context context, Object lock, ComponentName componentName) { mUserId = userId; mContext = Preconditions.checkNotNull(context); mLock = Preconditions.checkNotNull(lock); @@ -509,7 +525,7 @@ public class AttentionManagerService extends SystemService { } } - private final class AttentionServiceConnection implements ServiceConnection { + private class AttentionServiceConnection implements ServiceConnection { @Override public void onServiceConnected(ComponentName name, IBinder service) { init(IAttentionService.Stub.asInterface(service)); @@ -544,7 +560,8 @@ public class AttentionManagerService extends SystemService { } } - private class AttentionHandler extends Handler { + @VisibleForTesting + protected class AttentionHandler extends Handler { private static final int CHECK_CONNECTION_EXPIRATION = 1; private static final int ATTENTION_CHECK_TIMEOUT = 2; @@ -576,7 +593,8 @@ public class AttentionManagerService extends SystemService { } } - private void cancel(UserState userState) { + @VisibleForTesting + void cancel(UserState userState) { if (userState == null || userState.mCurrentAttentionCheck == null) { return; } diff --git a/services/core/java/com/android/server/audio/AudioDeviceBroker.java b/services/core/java/com/android/server/audio/AudioDeviceBroker.java index 44c1715cfed5..c573332235d8 100644 --- a/services/core/java/com/android/server/audio/AudioDeviceBroker.java +++ b/services/core/java/com/android/server/audio/AudioDeviceBroker.java @@ -114,8 +114,10 @@ import java.util.ArrayList; // All post* methods are asynchronous /*package*/ void onSystemReady() { - synchronized (mDeviceStateLock) { - mBtHelper.onSystemReady(); + synchronized (mSetModeLock) { + synchronized (mDeviceStateLock) { + mBtHelper.onSystemReady(); + } } } @@ -151,8 +153,10 @@ import java.util.ArrayList; * @param intent */ /*package*/ void receiveBtEvent(@NonNull Intent intent) { - synchronized (mDeviceStateLock) { - mBtHelper.receiveBtEvent(intent); + synchronized (mSetModeLock) { + synchronized (mDeviceStateLock) { + mBtHelper.receiveBtEvent(intent); + } } } @@ -350,13 +354,19 @@ import java.util.ArrayList; sendLMsgNoDelay(MSG_L_A2DP_DEVICE_CONFIG_CHANGE, SENDMSG_QUEUE, device); } + @GuardedBy("mSetModeLock") /*package*/ void startBluetoothScoForClient_Sync(IBinder cb, int scoAudioMode, @NonNull String eventSource) { - mBtHelper.startBluetoothScoForClient(cb, scoAudioMode, eventSource); + synchronized (mDeviceStateLock) { + mBtHelper.startBluetoothScoForClient(cb, scoAudioMode, eventSource); + } } + @GuardedBy("mSetModeLock") /*package*/ void stopBluetoothScoForClient_Sync(IBinder cb, @NonNull String eventSource) { - mBtHelper.stopBluetoothScoForClient(cb, eventSource); + synchronized (mDeviceStateLock) { + mBtHelper.stopBluetoothScoForClient(cb, eventSource); + } } //--------------------------------------------------------------------- @@ -479,6 +489,10 @@ import java.util.ArrayList; hearingAidProfile); } + /*package*/ void postScoClientDied(Object obj) { + sendLMsgNoDelay(MSG_L_SCOCLIENT_DIED, SENDMSG_QUEUE, obj); + } + //--------------------------------------------------------------------- // Method forwarding between the helper classes (BtHelper, AudioDeviceInventory) // only call from a "handle"* method or "on"* method @@ -708,8 +722,10 @@ import java.util.ArrayList; } break; case MSG_BT_HEADSET_CNCT_FAILED: - synchronized (mDeviceStateLock) { - mBtHelper.resetBluetoothSco(); + synchronized (mSetModeLock) { + synchronized (mDeviceStateLock) { + mBtHelper.resetBluetoothSco(); + } } break; case MSG_IL_BTA2DP_DOCK_TIMEOUT: @@ -742,8 +758,17 @@ import java.util.ArrayList; } break; case MSG_I_DISCONNECT_BT_SCO: - synchronized (mDeviceStateLock) { - mBtHelper.disconnectBluetoothSco(msg.arg1); + synchronized (mSetModeLock) { + synchronized (mDeviceStateLock) { + mBtHelper.disconnectBluetoothSco(msg.arg1); + } + } + break; + case MSG_L_SCOCLIENT_DIED: + synchronized (mSetModeLock) { + synchronized (mDeviceStateLock) { + mBtHelper.scoClientDied(msg.arg1); + } } break; case MSG_TOGGLE_HDMI: @@ -774,8 +799,10 @@ import java.util.ArrayList; } break; case MSG_DISCONNECT_BT_HEADSET: - synchronized (mDeviceStateLock) { - mBtHelper.disconnectHeadset(); + synchronized (mSetModeLock) { + synchronized (mDeviceStateLock) { + mBtHelper.disconnectHeadset(); + } } break; case MSG_L_BT_SERVICE_CONNECTED_PROFILE_A2DP: @@ -794,8 +821,10 @@ import java.util.ArrayList; } break; case MSG_L_BT_SERVICE_CONNECTED_PROFILE_HEADSET: - synchronized (mDeviceStateLock) { - mBtHelper.onHeadsetProfileConnected((BluetoothHeadset) msg.obj); + synchronized (mSetModeLock) { + synchronized (mDeviceStateLock) { + mBtHelper.onHeadsetProfileConnected((BluetoothHeadset) msg.obj); + } } break; case MSG_L_A2DP_DEVICE_CONNECTION_CHANGE_EXT: { @@ -892,6 +921,8 @@ import java.util.ArrayList; private static final int MSG_L_HEARING_AID_DEVICE_CONNECTION_CHANGE_EXT = 28; // process external command to (dis)connect or change active A2DP device private static final int MSG_L_A2DP_ACTIVE_DEVICE_CHANGE_EXT = 29; + // a ScoClient died in BtHelper + private static final int MSG_L_SCOCLIENT_DIED = 30; private static boolean isMessageHandledUnderWakelock(int msgId) { diff --git a/services/core/java/com/android/server/audio/AudioService.java b/services/core/java/com/android/server/audio/AudioService.java index aee08bb09401..d30a9d2b158e 100644 --- a/services/core/java/com/android/server/audio/AudioService.java +++ b/services/core/java/com/android/server/audio/AudioService.java @@ -3484,7 +3484,9 @@ public class AudioService extends IAudioService.Stub !mSystemReady) { return; } - mDeviceBroker.startBluetoothScoForClient_Sync(cb, scoAudioMode, eventSource); + synchronized (mDeviceBroker.mSetModeLock) { + mDeviceBroker.startBluetoothScoForClient_Sync(cb, scoAudioMode, eventSource); + } } /** @see AudioManager#stopBluetoothSco() */ @@ -3496,7 +3498,9 @@ public class AudioService extends IAudioService.Stub final String eventSource = new StringBuilder("stopBluetoothSco()") .append(") from u/pid:").append(Binder.getCallingUid()).append("/") .append(Binder.getCallingPid()).toString(); - mDeviceBroker.stopBluetoothScoForClient_Sync(cb, eventSource); + synchronized (mDeviceBroker.mSetModeLock) { + mDeviceBroker.stopBluetoothScoForClient_Sync(cb, eventSource); + } } diff --git a/services/core/java/com/android/server/audio/BtHelper.java b/services/core/java/com/android/server/audio/BtHelper.java index 2d9156b8782e..332ff362392a 100644 --- a/services/core/java/com/android/server/audio/BtHelper.java +++ b/services/core/java/com/android/server/audio/BtHelper.java @@ -36,6 +36,8 @@ import android.os.UserHandle; import android.provider.Settings; import android.util.Log; +import com.android.internal.annotations.GuardedBy; + import java.util.ArrayList; import java.util.List; import java.util.NoSuchElementException; @@ -163,6 +165,8 @@ public class BtHelper { //---------------------------------------------------------------------- // Interface for AudioDeviceBroker + // @GuardedBy("AudioDeviceBroker.mSetModeLock") + @GuardedBy("AudioDeviceBroker.mDeviceStateLock") /*package*/ synchronized void onSystemReady() { mScoConnectionState = android.media.AudioManager.SCO_AUDIO_STATE_ERROR; resetBluetoothSco(); @@ -231,6 +235,8 @@ public class BtHelper { return mapBluetoothCodecToAudioFormat(btCodecConfig.getCodecType()); } + // @GuardedBy("AudioDeviceBroker.mSetModeLock") + @GuardedBy("AudioDeviceBroker.mDeviceStateLock") /*package*/ synchronized void receiveBtEvent(Intent intent) { final String action = intent.getAction(); if (action.equals(BluetoothHeadset.ACTION_ACTIVE_DEVICE_CHANGED)) { @@ -317,6 +323,8 @@ public class BtHelper { * * @param exceptPid pid whose SCO connections through {@link AudioManager} should be kept */ + // @GuardedBy("AudioDeviceBroker.mSetModeLock") + @GuardedBy("AudioDeviceBroker.mDeviceStateLock") /*package*/ synchronized void disconnectBluetoothSco(int exceptPid) { checkScoAudioState(); if (mScoAudioState == SCO_STATE_ACTIVE_EXTERNAL) { @@ -325,6 +333,8 @@ public class BtHelper { clearAllScoClients(exceptPid, true); } + // @GuardedBy("AudioDeviceBroker.mSetModeLock") + @GuardedBy("AudioDeviceBroker.mDeviceStateLock") /*package*/ synchronized void startBluetoothScoForClient(IBinder cb, int scoAudioMode, @NonNull String eventSource) { ScoClient client = getScoClient(cb, true); @@ -344,6 +354,8 @@ public class BtHelper { Binder.restoreCallingIdentity(ident); } + // @GuardedBy("AudioDeviceBroker.mSetModeLock") + @GuardedBy("AudioDeviceBroker.mDeviceStateLock") /*package*/ synchronized void stopBluetoothScoForClient(IBinder cb, @NonNull String eventSource) { ScoClient client = getScoClient(cb, false); @@ -401,6 +413,8 @@ public class BtHelper { mDeviceBroker.postDisconnectHearingAid(); } + // @GuardedBy("AudioDeviceBroker.mSetModeLock") + @GuardedBy("AudioDeviceBroker.mDeviceStateLock") /*package*/ synchronized void resetBluetoothSco() { clearAllScoClients(0, false); mScoAudioState = SCO_STATE_INACTIVE; @@ -409,6 +423,8 @@ public class BtHelper { mDeviceBroker.setBluetoothScoOn(false, "resetBluetoothSco"); } + // @GuardedBy("AudioDeviceBroker.mSetModeLock") + @GuardedBy("AudioDeviceBroker.mDeviceStateLock") /*package*/ synchronized void disconnectHeadset() { setBtScoActiveDevice(null); mBluetoothHeadset = null; @@ -454,6 +470,8 @@ public class BtHelper { /*eventSource*/ "mBluetoothProfileServiceListener"); } + // @GuardedBy("AudioDeviceBroker.mSetModeLock") + @GuardedBy("AudioDeviceBroker.mDeviceStateLock") /*package*/ synchronized void onHeadsetProfileConnected(BluetoothHeadset headset) { // Discard timeout message mDeviceBroker.handleCancelFailureToConnectToBtHeadsetService(); @@ -540,6 +558,9 @@ public class BtHelper { return result; } + // @GuardedBy("AudioDeviceBroker.mSetModeLock") + //@GuardedBy("AudioDeviceBroker.mDeviceStateLock") + @GuardedBy("BtHelper.this") private void setBtScoActiveDevice(BluetoothDevice btDevice) { Log.i(TAG, "setBtScoActiveDevice: " + mBluetoothHeadsetDevice + " -> " + btDevice); final BluetoothDevice previousActiveDevice = mBluetoothHeadsetDevice; @@ -621,6 +642,20 @@ public class BtHelper { }; //---------------------------------------------------------------------- + // @GuardedBy("AudioDeviceBroker.mSetModeLock") + @GuardedBy("AudioDeviceBroker.mDeviceStateLock") + /*package*/ synchronized void scoClientDied(Object obj) { + final ScoClient client = (ScoClient) obj; + Log.w(TAG, "SCO client died"); + int index = mScoClients.indexOf(client); + if (index < 0) { + Log.w(TAG, "unregistered SCO client died"); + } else { + client.clearCount(true); + mScoClients.remove(client); + } + } + private class ScoClient implements IBinder.DeathRecipient { private IBinder mCb; // To be notified of client's death private int mCreatorPid; @@ -634,21 +669,14 @@ public class BtHelper { @Override public void binderDied() { - // this is the only place the implementation of ScoClient needs to be synchronized - // on the instance, as all other methods are directly or indirectly called from - // package-private methods, which are synchronized - synchronized (BtHelper.this) { - Log.w(TAG, "SCO client died"); - int index = mScoClients.indexOf(this); - if (index < 0) { - Log.w(TAG, "unregistered SCO client died"); - } else { - clearCount(true); - mScoClients.remove(this); - } - } + // process this from DeviceBroker's message queue to take the right locks since + // this event can impact SCO mode and requires querying audio mode stack + mDeviceBroker.postScoClientDied(this); } + // @GuardedBy("AudioDeviceBroker.mSetModeLock") + // @GuardedBy("AudioDeviceBroker.mDeviceStateLock") + @GuardedBy("BtHelper.this") void incCount(int scoAudioMode) { requestScoState(BluetoothHeadset.STATE_AUDIO_CONNECTED, scoAudioMode); if (mStartcount == 0) { @@ -663,6 +691,9 @@ public class BtHelper { mStartcount++; } + // @GuardedBy("AudioDeviceBroker.mSetModeLock") + // @GuardedBy("AudioDeviceBroker.mDeviceStateLock") + @GuardedBy("BtHelper.this") void decCount() { if (mStartcount == 0) { Log.w(TAG, "ScoClient.decCount() already 0"); @@ -679,6 +710,9 @@ public class BtHelper { } } + // @GuardedBy("AudioDeviceBroker.mSetModeLock") + // @GuardedBy("AudioDeviceBroker.mDeviceStateLock") + @GuardedBy("BtHelper.this") void clearCount(boolean stopSco) { if (mStartcount != 0) { try { @@ -714,6 +748,9 @@ public class BtHelper { return count; } + // @GuardedBy("AudioDeviceBroker.mSetModeLock") + //@GuardedBy("AudioDeviceBroker.mDeviceStateLock") + @GuardedBy("BtHelper.this") private void requestScoState(int state, int scoAudioMode) { checkScoAudioState(); int clientCount = totalCount(); @@ -728,74 +765,71 @@ public class BtHelper { broadcastScoConnectionState(AudioManager.SCO_AUDIO_STATE_CONNECTING); // Accept SCO audio activation only in NORMAL audio mode or if the mode is // currently controlled by the same client process. - // TODO do not sync that way, see b/123769055 - synchronized (mDeviceBroker.mSetModeLock) { - int modeOwnerPid = mDeviceBroker.getSetModeDeathHandlers().isEmpty() - ? 0 : mDeviceBroker.getSetModeDeathHandlers().get(0).getPid(); - if (modeOwnerPid != 0 && (modeOwnerPid != mCreatorPid)) { - Log.w(TAG, "requestScoState: audio mode is not NORMAL and modeOwnerPid " - + modeOwnerPid + " != creatorPid " + mCreatorPid); - broadcastScoConnectionState(AudioManager.SCO_AUDIO_STATE_DISCONNECTED); - return; - } - switch (mScoAudioState) { - case SCO_STATE_INACTIVE: - mScoAudioMode = scoAudioMode; - if (scoAudioMode == SCO_MODE_UNDEFINED) { - mScoAudioMode = SCO_MODE_VIRTUAL_CALL; - if (mBluetoothHeadsetDevice != null) { - mScoAudioMode = Settings.Global.getInt( - mDeviceBroker.getContentResolver(), - "bluetooth_sco_channel_" - + mBluetoothHeadsetDevice.getAddress(), - SCO_MODE_VIRTUAL_CALL); - if (mScoAudioMode > SCO_MODE_MAX || mScoAudioMode < 0) { - mScoAudioMode = SCO_MODE_VIRTUAL_CALL; - } - } - } - if (mBluetoothHeadset == null) { - if (getBluetoothHeadset()) { - mScoAudioState = SCO_STATE_ACTIVATE_REQ; - } else { - Log.w(TAG, "requestScoState: getBluetoothHeadset failed during" - + " connection, mScoAudioMode=" + mScoAudioMode); - broadcastScoConnectionState( - AudioManager.SCO_AUDIO_STATE_DISCONNECTED); + int modeOwnerPid = mDeviceBroker.getSetModeDeathHandlers().isEmpty() + ? 0 : mDeviceBroker.getSetModeDeathHandlers().get(0).getPid(); + if (modeOwnerPid != 0 && (modeOwnerPid != mCreatorPid)) { + Log.w(TAG, "requestScoState: audio mode is not NORMAL and modeOwnerPid " + + modeOwnerPid + " != creatorPid " + mCreatorPid); + broadcastScoConnectionState(AudioManager.SCO_AUDIO_STATE_DISCONNECTED); + return; + } + switch (mScoAudioState) { + case SCO_STATE_INACTIVE: + mScoAudioMode = scoAudioMode; + if (scoAudioMode == SCO_MODE_UNDEFINED) { + mScoAudioMode = SCO_MODE_VIRTUAL_CALL; + if (mBluetoothHeadsetDevice != null) { + mScoAudioMode = Settings.Global.getInt( + mDeviceBroker.getContentResolver(), + "bluetooth_sco_channel_" + + mBluetoothHeadsetDevice.getAddress(), + SCO_MODE_VIRTUAL_CALL); + if (mScoAudioMode > SCO_MODE_MAX || mScoAudioMode < 0) { + mScoAudioMode = SCO_MODE_VIRTUAL_CALL; } - break; - } - if (mBluetoothHeadsetDevice == null) { - Log.w(TAG, "requestScoState: no active device while connecting," - + " mScoAudioMode=" + mScoAudioMode); - broadcastScoConnectionState( - AudioManager.SCO_AUDIO_STATE_DISCONNECTED); - break; } - if (connectBluetoothScoAudioHelper(mBluetoothHeadset, - mBluetoothHeadsetDevice, mScoAudioMode)) { - mScoAudioState = SCO_STATE_ACTIVE_INTERNAL; + } + if (mBluetoothHeadset == null) { + if (getBluetoothHeadset()) { + mScoAudioState = SCO_STATE_ACTIVATE_REQ; } else { - Log.w(TAG, "requestScoState: connect to " + mBluetoothHeadsetDevice - + " failed, mScoAudioMode=" + mScoAudioMode); + Log.w(TAG, "requestScoState: getBluetoothHeadset failed during" + + " connection, mScoAudioMode=" + mScoAudioMode); broadcastScoConnectionState( AudioManager.SCO_AUDIO_STATE_DISCONNECTED); } break; - case SCO_STATE_DEACTIVATING: - mScoAudioState = SCO_STATE_ACTIVATE_REQ; + } + if (mBluetoothHeadsetDevice == null) { + Log.w(TAG, "requestScoState: no active device while connecting," + + " mScoAudioMode=" + mScoAudioMode); + broadcastScoConnectionState( + AudioManager.SCO_AUDIO_STATE_DISCONNECTED); break; - case SCO_STATE_DEACTIVATE_REQ: + } + if (connectBluetoothScoAudioHelper(mBluetoothHeadset, + mBluetoothHeadsetDevice, mScoAudioMode)) { mScoAudioState = SCO_STATE_ACTIVE_INTERNAL; - broadcastScoConnectionState(AudioManager.SCO_AUDIO_STATE_CONNECTED); - break; - default: - Log.w(TAG, "requestScoState: failed to connect in state " - + mScoAudioState + ", scoAudioMode=" + scoAudioMode); - broadcastScoConnectionState(AudioManager.SCO_AUDIO_STATE_DISCONNECTED); - break; + } else { + Log.w(TAG, "requestScoState: connect to " + mBluetoothHeadsetDevice + + " failed, mScoAudioMode=" + mScoAudioMode); + broadcastScoConnectionState( + AudioManager.SCO_AUDIO_STATE_DISCONNECTED); + } + break; + case SCO_STATE_DEACTIVATING: + mScoAudioState = SCO_STATE_ACTIVATE_REQ; + break; + case SCO_STATE_DEACTIVATE_REQ: + mScoAudioState = SCO_STATE_ACTIVE_INTERNAL; + broadcastScoConnectionState(AudioManager.SCO_AUDIO_STATE_CONNECTED); + break; + default: + Log.w(TAG, "requestScoState: failed to connect in state " + + mScoAudioState + ", scoAudioMode=" + scoAudioMode); + broadcastScoConnectionState(AudioManager.SCO_AUDIO_STATE_DISCONNECTED); + break; - } } } else if (state == BluetoothHeadset.STATE_AUDIO_DISCONNECTED) { switch (mScoAudioState) { @@ -906,6 +940,9 @@ public class BtHelper { return null; } + // @GuardedBy("AudioDeviceBroker.mSetModeLock") + //@GuardedBy("AudioDeviceBroker.mDeviceStateLock") + @GuardedBy("BtHelper.this") private void clearAllScoClients(int exceptPid, boolean stopSco) { ScoClient savedClient = null; for (ScoClient cl : mScoClients) { diff --git a/services/core/java/com/android/server/clipboard/ClipboardService.java b/services/core/java/com/android/server/clipboard/ClipboardService.java index 258c325716f2..d28482ef82ab 100644 --- a/services/core/java/com/android/server/clipboard/ClipboardService.java +++ b/services/core/java/com/android/server/clipboard/ClipboardService.java @@ -52,6 +52,7 @@ import android.provider.Settings; import android.text.TextUtils; import android.util.Slog; import android.util.SparseArray; +import android.view.autofill.AutofillManagerInternal; import com.android.server.LocalServices; import com.android.server.SystemService; @@ -159,6 +160,7 @@ public class ClipboardService extends SystemService { private final PackageManager mPm; private final AppOpsManager mAppOps; private final ContentCaptureManagerInternal mContentCaptureInternal; + private final AutofillManagerInternal mAutofillInternal; private final IBinder mPermissionOwner; private HostClipboardMonitor mHostClipboardMonitor = null; private Thread mHostMonitorThread = null; @@ -179,6 +181,7 @@ public class ClipboardService extends SystemService { mUm = (IUserManager) ServiceManager.getService(Context.USER_SERVICE); mAppOps = (AppOpsManager) getContext().getSystemService(Context.APP_OPS_SERVICE); mContentCaptureInternal = LocalServices.getService(ContentCaptureManagerInternal.class); + mAutofillInternal = LocalServices.getService(AutofillManagerInternal.class); final IBinder permOwner = mUgmInternal.newUriPermissionOwner("clipboard"); mPermissionOwner = permOwner; if (IS_EMULATOR) { @@ -653,13 +656,18 @@ public class ClipboardService extends SystemService { // Clipboard can only be read by applications with focus.. boolean allowed = mWm.isUidFocused(callingUid); if (!allowed && mContentCaptureInternal != null) { - // ...or the Intelligence Service + // ...or the Content Capture Service allowed = mContentCaptureInternal.isContentCaptureServiceForUser(callingUid, userId); } + if (!allowed && mAutofillInternal != null) { + // ...or the Augmented Autofill Service + allowed = mAutofillInternal.isAugmentedAutofillServiceForUser(callingUid, + userId); + } if (!allowed) { Slog.e(TAG, "Denying clipboard access to " + callingPackage - + ", application is not in focus neither is the IntelligeService for " + + ", application is not in focus neither is a system service for " + "user " + userId); } return allowed; diff --git a/services/core/java/com/android/server/connectivity/NetworkDiagnostics.java b/services/core/java/com/android/server/connectivity/NetworkDiagnostics.java index 948c690956d3..a1a8e355dcec 100644 --- a/services/core/java/com/android/server/connectivity/NetworkDiagnostics.java +++ b/services/core/java/com/android/server/connectivity/NetworkDiagnostics.java @@ -33,6 +33,7 @@ import android.text.TextUtils; import android.util.Pair; import com.android.internal.util.IndentingPrintWriter; +import com.android.internal.util.TrafficStatsConstants; import libcore.io.IoUtils; @@ -381,7 +382,8 @@ public class NetworkDiagnostics { protected void setupSocket( int sockType, int protocol, long writeTimeout, long readTimeout, int dstPort) throws ErrnoException, IOException { - final int oldTag = TrafficStats.getAndSetThreadStatsTag(TrafficStats.TAG_SYSTEM_PROBE); + final int oldTag = TrafficStats.getAndSetThreadStatsTag( + TrafficStatsConstants.TAG_SYSTEM_PROBE); try { mFileDescriptor = Os.socket(mAddressFamily, sockType, protocol); } finally { diff --git a/services/core/java/com/android/server/connectivity/PacManager.java b/services/core/java/com/android/server/connectivity/PacManager.java index 1ac09ad462a1..f6ce2dc68b99 100644 --- a/services/core/java/com/android/server/connectivity/PacManager.java +++ b/services/core/java/com/android/server/connectivity/PacManager.java @@ -39,6 +39,7 @@ import android.provider.Settings; import android.util.Log; import com.android.internal.annotations.GuardedBy; +import com.android.internal.util.TrafficStatsConstants; import com.android.net.IProxyCallback; import com.android.net.IProxyPortListener; import com.android.net.IProxyService; @@ -111,7 +112,8 @@ public class PacManager { String file; final Uri pacUrl = mPacUrl; if (Uri.EMPTY.equals(pacUrl)) return; - final int oldTag = TrafficStats.getAndSetThreadStatsTag(TrafficStats.TAG_SYSTEM_PAC); + final int oldTag = TrafficStats.getAndSetThreadStatsTag( + TrafficStatsConstants.TAG_SYSTEM_PAC); try { file = get(pacUrl); } catch (IOException ioe) { diff --git a/services/core/java/com/android/server/location/GpsXtraDownloader.java b/services/core/java/com/android/server/location/GpsXtraDownloader.java index c012ee41b29b..7dffcb4c32a9 100644 --- a/services/core/java/com/android/server/location/GpsXtraDownloader.java +++ b/services/core/java/com/android/server/location/GpsXtraDownloader.java @@ -20,6 +20,8 @@ import android.net.TrafficStats; import android.text.TextUtils; import android.util.Log; +import com.android.internal.util.TrafficStatsConstants; + import java.io.ByteArrayOutputStream; import java.io.IOException; import java.io.InputStream; @@ -92,7 +94,8 @@ public class GpsXtraDownloader { // load balance our requests among the available servers while (result == null) { - final int oldTag = TrafficStats.getAndSetThreadStatsTag(TrafficStats.TAG_SYSTEM_GPS); + final int oldTag = TrafficStats.getAndSetThreadStatsTag( + TrafficStatsConstants.TAG_SYSTEM_GPS); try { result = doDownload(mXtraServers[mNextServerIndex]); } finally { diff --git a/services/core/java/com/android/server/media/MediaSessionServiceImpl.java b/services/core/java/com/android/server/media/MediaSessionServiceImpl.java index a5f22175331d..fdb499b5cd0e 100644 --- a/services/core/java/com/android/server/media/MediaSessionServiceImpl.java +++ b/services/core/java/com/android/server/media/MediaSessionServiceImpl.java @@ -140,6 +140,7 @@ public class MediaSessionServiceImpl extends MediaSessionService.ServiceImpl { private AudioPlayerStateMonitor mAudioPlayerStateMonitor; // Used to notify System UI and Settings when remote volume was changed. + @GuardedBy("mLock") final RemoteCallbackList<IRemoteVolumeController> mRemoteVolumeControllers = new RemoteCallbackList<>(); @@ -287,17 +288,19 @@ public class MediaSessionServiceImpl extends MediaSessionService.ServiceImpl { if (!session.isActive()) { return; } - int size = mRemoteVolumeControllers.beginBroadcast(); - MediaSession.Token token = session.getSessionToken(); - for (int i = size - 1; i >= 0; i--) { - try { - IRemoteVolumeController cb = mRemoteVolumeControllers.getBroadcastItem(i); - cb.remoteVolumeChanged(token, flags); - } catch (Exception e) { - Log.w(TAG, "Error sending volume change.", e); + synchronized (mLock) { + int size = mRemoteVolumeControllers.beginBroadcast(); + MediaSession.Token token = session.getSessionToken(); + for (int i = size - 1; i >= 0; i--) { + try { + IRemoteVolumeController cb = mRemoteVolumeControllers.getBroadcastItem(i); + cb.remoteVolumeChanged(token, flags); + } catch (Exception e) { + Log.w(TAG, "Error sending volume change.", e); + } } + mRemoteVolumeControllers.finishBroadcast(); } - mRemoteVolumeControllers.finishBroadcast(); } @Override @@ -647,19 +650,21 @@ public class MediaSessionServiceImpl extends MediaSessionService.ServiceImpl { return; } - int size = mRemoteVolumeControllers.beginBroadcast(); - MediaSessionRecord record = user.mPriorityStack.getDefaultRemoteSession(userId); - MediaSession.Token token = record == null ? null : record.getSessionToken(); + synchronized (mLock) { + int size = mRemoteVolumeControllers.beginBroadcast(); + MediaSessionRecord record = user.mPriorityStack.getDefaultRemoteSession(userId); + MediaSession.Token token = record == null ? null : record.getSessionToken(); - for (int i = size - 1; i >= 0; i--) { - try { - IRemoteVolumeController cb = mRemoteVolumeControllers.getBroadcastItem(i); - cb.updateRemoteController(token); - } catch (Exception e) { - Log.w(TAG, "Error sending default remote volume.", e); + for (int i = size - 1; i >= 0; i--) { + try { + IRemoteVolumeController cb = mRemoteVolumeControllers.getBroadcastItem(i); + cb.updateRemoteController(token); + } catch (Exception e) { + Log.w(TAG, "Error sending default remote volume.", e); + } } + mRemoteVolumeControllers.finishBroadcast(); } - mRemoteVolumeControllers.finishBroadcast(); } void pushSession2TokensChangedLocked(int userId) { @@ -1676,11 +1681,13 @@ public class MediaSessionServiceImpl extends MediaSessionService.ServiceImpl { final int pid = Binder.getCallingPid(); final int uid = Binder.getCallingUid(); final long token = Binder.clearCallingIdentity(); - try { - enforceStatusBarServicePermission("listen for volume changes", pid, uid); - mRemoteVolumeControllers.register(rvc); - } finally { - Binder.restoreCallingIdentity(token); + synchronized (mLock) { + try { + enforceStatusBarServicePermission("listen for volume changes", pid, uid); + mRemoteVolumeControllers.register(rvc); + } finally { + Binder.restoreCallingIdentity(token); + } } } @@ -1689,11 +1696,13 @@ public class MediaSessionServiceImpl extends MediaSessionService.ServiceImpl { final int pid = Binder.getCallingPid(); final int uid = Binder.getCallingUid(); final long token = Binder.clearCallingIdentity(); - try { - enforceStatusBarServicePermission("listen for volume changes", pid, uid); - mRemoteVolumeControllers.unregister(rvc); - } finally { - Binder.restoreCallingIdentity(token); + synchronized (mLock) { + try { + enforceStatusBarServicePermission("listen for volume changes", pid, uid); + mRemoteVolumeControllers.unregister(rvc); + } finally { + Binder.restoreCallingIdentity(token); + } } } diff --git a/services/core/java/com/android/server/notification/NotificationManagerService.java b/services/core/java/com/android/server/notification/NotificationManagerService.java index 7f1b25ca3ca3..042ac8c5760e 100644 --- a/services/core/java/com/android/server/notification/NotificationManagerService.java +++ b/services/core/java/com/android/server/notification/NotificationManagerService.java @@ -1689,6 +1689,9 @@ public class NotificationManagerService extends SystemService { mPreferencesHelper.lockChannelsForOEM(getContext().getResources().getStringArray( com.android.internal.R.array.config_nonBlockableNotificationPackages)); + + mZenModeHelper.setPriorityOnlyDndExemptPackages(getContext().getResources().getStringArray( + com.android.internal.R.array.config_priorityOnlyDndExemptPackages)); } @Override diff --git a/services/core/java/com/android/server/notification/ZenModeHelper.java b/services/core/java/com/android/server/notification/ZenModeHelper.java index 7e74cc2368cd..1f5b99cb3349 100644 --- a/services/core/java/com/android/server/notification/ZenModeHelper.java +++ b/services/core/java/com/android/server/notification/ZenModeHelper.java @@ -80,6 +80,7 @@ import org.xmlpull.v1.XmlSerializer; import java.io.IOException; import java.io.PrintWriter; import java.util.ArrayList; +import java.util.Arrays; import java.util.List; import java.util.Objects; @@ -96,7 +97,7 @@ public class ZenModeHelper { private final Context mContext; private final H mHandler; private final SettingsObserver mSettingsObserver; - @VisibleForTesting protected final AppOpsManager mAppOps; + private final AppOpsManager mAppOps; @VisibleForTesting protected final NotificationManager mNotificationManager; @VisibleForTesting protected ZenModeConfig mDefaultConfig; private final ArrayList<Callback> mCallbacks = new ArrayList<Callback>(); @@ -123,11 +124,13 @@ public class ZenModeHelper { @VisibleForTesting protected boolean mIsBootComplete; + private String[] mPriorityOnlyDndExemptPackages; + public ZenModeHelper(Context context, Looper looper, ConditionProviders conditionProviders) { mContext = context; mHandler = new H(looper); addCallback(mMetrics); - mAppOps = (AppOpsManager) context.getSystemService(Context.APP_OPS_SERVICE); + mAppOps = context.getSystemService(AppOpsManager.class); mNotificationManager = context.getSystemService(NotificationManager.class); mDefaultConfig = readDefaultConfig(mContext.getResources()); @@ -214,6 +217,10 @@ public class ZenModeHelper { loadConfigForUser(user, "onUserUnlocked"); } + void setPriorityOnlyDndExemptPackages(String[] packages) { + mPriorityOnlyDndExemptPackages = packages; + } + private void loadConfigForUser(int user, String reason) { if (mUser == user || user < UserHandle.USER_SYSTEM) return; mUser = user; @@ -994,53 +1001,47 @@ public class ZenModeHelper { for (int usage : AudioAttributes.SDK_USAGES) { final int suppressionBehavior = AudioAttributes.SUPPRESSIBLE_USAGES.get(usage); if (suppressionBehavior == AudioAttributes.SUPPRESSIBLE_NEVER) { - applyRestrictions(false /*mute*/, usage); + applyRestrictions(zenPriorityOnly, false /*mute*/, usage); } else if (suppressionBehavior == AudioAttributes.SUPPRESSIBLE_NOTIFICATION) { - applyRestrictions(muteNotifications || muteEverything, usage); + applyRestrictions(zenPriorityOnly, muteNotifications || muteEverything, usage); } else if (suppressionBehavior == AudioAttributes.SUPPRESSIBLE_CALL) { - applyRestrictions(muteCalls || muteEverything, usage); + applyRestrictions(zenPriorityOnly, muteCalls || muteEverything, usage); } else if (suppressionBehavior == AudioAttributes.SUPPRESSIBLE_ALARM) { - applyRestrictions(muteAlarms || muteEverything, usage); + applyRestrictions(zenPriorityOnly, muteAlarms || muteEverything, usage); } else if (suppressionBehavior == AudioAttributes.SUPPRESSIBLE_MEDIA) { - applyRestrictions(muteMedia || muteEverything, usage); + applyRestrictions(zenPriorityOnly, muteMedia || muteEverything, usage); } else if (suppressionBehavior == AudioAttributes.SUPPRESSIBLE_SYSTEM) { if (usage == AudioAttributes.USAGE_ASSISTANCE_SONIFICATION) { // normally DND will only restrict touch sounds, not haptic feedback/vibrations - applyRestrictions(muteSystem || muteEverything, usage, + applyRestrictions(zenPriorityOnly, muteSystem || muteEverything, usage, AppOpsManager.OP_PLAY_AUDIO); - applyRestrictions(false, usage, AppOpsManager.OP_VIBRATE); + applyRestrictions(zenPriorityOnly, false, usage, AppOpsManager.OP_VIBRATE); } else { - applyRestrictions(muteSystem || muteEverything, usage); + applyRestrictions(zenPriorityOnly, muteSystem || muteEverything, usage); } } else { - applyRestrictions(muteEverything, usage); + applyRestrictions(zenPriorityOnly, muteEverything, usage); } } } @VisibleForTesting - protected void applyRestrictions(boolean mute, int usage, int code) { - final String[] exceptionPackages = null; // none (for now) - - // Only do this if we are executing within the system process... otherwise - // we are running as test code, so don't have access to the protected call. - if (Process.myUid() == Process.SYSTEM_UID) { - final long ident = Binder.clearCallingIdentity(); - try { - mAppOps.setRestriction(code, usage, - mute ? AppOpsManager.MODE_IGNORED : AppOpsManager.MODE_ALLOWED, - exceptionPackages); - } finally { - Binder.restoreCallingIdentity(ident); - } + protected void applyRestrictions(boolean zenPriorityOnly, boolean mute, int usage, int code) { + final long ident = Binder.clearCallingIdentity(); + try { + mAppOps.setRestriction(code, usage, + mute ? AppOpsManager.MODE_IGNORED : AppOpsManager.MODE_ALLOWED, + zenPriorityOnly ? mPriorityOnlyDndExemptPackages : null); + } finally { + Binder.restoreCallingIdentity(ident); } } @VisibleForTesting - protected void applyRestrictions(boolean mute, int usage) { - applyRestrictions(mute, usage, AppOpsManager.OP_VIBRATE); - applyRestrictions(mute, usage, AppOpsManager.OP_PLAY_AUDIO); + protected void applyRestrictions(boolean zenPriorityOnly, boolean mute, int usage) { + applyRestrictions(zenPriorityOnly, mute, usage, AppOpsManager.OP_VIBRATE); + applyRestrictions(zenPriorityOnly, mute, usage, AppOpsManager.OP_PLAY_AUDIO); } diff --git a/services/core/java/com/android/server/pm/PackageManagerService.java b/services/core/java/com/android/server/pm/PackageManagerService.java index c12ee03fb86d..d7da06cc92bc 100644 --- a/services/core/java/com/android/server/pm/PackageManagerService.java +++ b/services/core/java/com/android/server/pm/PackageManagerService.java @@ -2801,6 +2801,11 @@ public class PackageManagerService extends IPackageManager.Stub if (disabledPs.codePath == null || !disabledPs.codePath.exists() || disabledPs.pkg == null) { possiblyDeletedUpdatedSystemApps.add(ps.name); + } else { + // We're expecting that the system app should remain disabled, but add + // it to expecting better to recover in case the data version cannot + // be scanned. + mExpectingBetter.put(disabledPs.name, disabledPs.codePath); } } } @@ -9334,6 +9339,7 @@ public class PackageManagerService extends IPackageManager.Stub | SCAN_UPDATE_SIGNATURE, currentTime, user); if (scanResult.success) { synchronized (mPackages) { + boolean appIdCreated = false; try { final String pkgName = scanResult.pkgSetting.name; final Map<String, ReconciledPackage> reconcileResult = reconcilePackagesLocked( @@ -9346,11 +9352,12 @@ public class PackageManagerService extends IPackageManager.Stub Collections.singletonMap(pkgName, getSharedLibLatestVersionSetting(scanResult))), mSettings.mKeySetManagerService); - prepareScanResultLocked(scanResult); - commitReconciledScanResultLocked( - reconcileResult.get(pkgName)); + appIdCreated = optimisticallyRegisterAppId(scanResult); + commitReconciledScanResultLocked(reconcileResult.get(pkgName)); } catch (PackageManagerException e) { - unprepareScanResultLocked(scanResult); + if (appIdCreated) { + cleanUpAppIdCreation(scanResult); + } throw e; } } @@ -10837,27 +10844,32 @@ public class PackageManagerService extends IPackageManager.Stub } - /** Prepares the system to commit a {@link ScanResult} in a way that will not fail. */ - private void prepareScanResultLocked(@NonNull ScanResult result) + /** + * Prepares the system to commit a {@link ScanResult} in a way that will not fail by registering + * the app ID required for reconcile. + * @return {@code true} if a new app ID was registered and will need to be cleaned up on + * failure. + */ + private boolean optimisticallyRegisterAppId(@NonNull ScanResult result) throws PackageManagerException { if (!result.existingSettingCopied) { // THROWS: when we can't allocate a user id. add call to check if there's // enough space to ensure we won't throw; otherwise, don't modify state - mSettings.registerAppIdLPw(result.pkgSetting); + return mSettings.registerAppIdLPw(result.pkgSetting); } + return false; } /** - * Reverts any changes to the system that were made by - * {@link #prepareScanResultLocked(ScanResult)} + * Reverts any app ID creation that were made by + * {@link #optimisticallyRegisterAppId(ScanResult)}. Note: this is only necessary if the + * referenced method returned true. */ - private void unprepareScanResultLocked(@NonNull ScanResult result) { - if (!result.existingSettingCopied) { - // iff we've acquired an app ID for a new package setting, remove it so that it can be - // acquired by another request. - if (result.pkgSetting.appId > 0) { - mSettings.removeAppIdLPw(result.pkgSetting.appId); - } + private void cleanUpAppIdCreation(@NonNull ScanResult result) { + // iff we've acquired an app ID for a new package setting, remove it so that it can be + // acquired by another request. + if (result.pkgSetting.appId > 0) { + mSettings.removeAppIdLPw(result.pkgSetting.appId); } } @@ -14608,6 +14620,20 @@ public class PackageManagerService extends IPackageManager.Stub return mUser; } + /** + * Gets the user handle for the user that the rollback agent should + * use to look up information about this installation when enabling + * rollback. + */ + UserHandle getRollbackUser() { + // The session for packages installed for "all" users is + // associated with the "system" user. + if (mUser == UserHandle.ALL) { + return UserHandle.SYSTEM; + } + return mUser; + } + HandlerParams setTraceMethod(String traceMethod) { this.traceMethod = traceMethod; return this; @@ -15226,7 +15252,7 @@ public class PackageManagerService extends IPackageManager.Stub installedUsers); enableRollbackIntent.putExtra( PackageManagerInternal.EXTRA_ENABLE_ROLLBACK_USER, - getUser().getIdentifier()); + getRollbackUser().getIdentifier()); enableRollbackIntent.setDataAndType(Uri.fromFile(new File(origin.resolvedPath)), PACKAGE_MIME_TYPE); enableRollbackIntent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION); @@ -16701,6 +16727,7 @@ public class PackageManagerService extends IPackageManager.Stub final Map<String, VersionInfo> versionInfos = new ArrayMap<>(requests.size()); final Map<String, PackageSetting> lastStaticSharedLibSettings = new ArrayMap<>(requests.size()); + final Map<String, Boolean> createdAppId = new ArrayMap<>(requests.size()); boolean success = false; try { Trace.traceBegin(TRACE_TAG_PACKAGE_MANAGER, "installPackagesLI"); @@ -16740,7 +16767,7 @@ public class PackageManagerService extends IPackageManager.Stub + " in multi-package install request."); return; } - prepareScanResultLocked(result); + createdAppId.put(packageName, optimisticallyRegisterAppId(result)); versionInfos.put(result.pkgSetting.pkg.packageName, getSettingsVersionForPackage(result.pkgSetting.pkg)); if (result.staticSharedLibraryInfo != null) { @@ -16797,7 +16824,9 @@ public class PackageManagerService extends IPackageManager.Stub } finally { if (!success) { for (ScanResult result : preparedScans.values()) { - unprepareScanResultLocked(result); + if (createdAppId.getOrDefault(result.request.pkg.packageName, false)) { + cleanUpAppIdCreation(result); + } } } for (PrepareResult result : prepareResults.values()) { @@ -19890,26 +19919,28 @@ public class PackageManagerService extends IPackageManager.Stub mPermissionManager.enforceCrossUserPermission(callingUid, userId, true /* requireFullPermission */, false /* checkShell */, "replace preferred activity"); - synchronized (mPackages) { - if (mContext.checkCallingOrSelfPermission( - android.Manifest.permission.SET_PREFERRED_APPLICATIONS) - != PackageManager.PERMISSION_GRANTED) { + if (mContext.checkCallingOrSelfPermission( + android.Manifest.permission.SET_PREFERRED_APPLICATIONS) + != PackageManager.PERMISSION_GRANTED) { + synchronized (mPackages) { if (getUidTargetSdkVersionLockedLPr(callingUid) < Build.VERSION_CODES.FROYO) { Slog.w(TAG, "Ignoring replacePreferredActivity() from uid " + Binder.getCallingUid()); return; } - mContext.enforceCallingOrSelfPermission( - android.Manifest.permission.SET_PREFERRED_APPLICATIONS, null); } + mContext.enforceCallingOrSelfPermission( + android.Manifest.permission.SET_PREFERRED_APPLICATIONS, null); + } - PreferredIntentResolver pir = mSettings.mPreferredActivities.get(userId); + synchronized (mPackages) { + final PreferredIntentResolver pir = mSettings.mPreferredActivities.get(userId); if (pir != null) { // Get all of the existing entries that exactly match this filter. - ArrayList<PreferredActivity> existing = pir.findFilters(filter); + final ArrayList<PreferredActivity> existing = pir.findFilters(filter); if (existing != null && existing.size() == 1) { - PreferredActivity cur = existing.get(0); + final PreferredActivity cur = existing.get(0); if (DEBUG_PREFERRED) { Slog.i(TAG, "Checking replace of preferred:"); filter.dump(new LogPrinter(Log.INFO, TAG), " "); @@ -19939,14 +19970,13 @@ public class PackageManagerService extends IPackageManager.Stub return; } } - if (existing != null) { if (DEBUG_PREFERRED) { Slog.i(TAG, existing.size() + " existing preferred matches for:"); filter.dump(new LogPrinter(Log.INFO, TAG), " "); } - for (int i = 0; i < existing.size(); i++) { - PreferredActivity pa = existing.get(i); + for (int i = existing.size() - 1; i >= 0; --i) { + final PreferredActivity pa = existing.get(i); if (DEBUG_PREFERRED) { Slog.i(TAG, "Removing existing preferred activity " + pa.mPref.mComponent + ":"); @@ -19956,9 +19986,9 @@ public class PackageManagerService extends IPackageManager.Stub } } } - addPreferredActivityInternal(filter, match, set, activity, true, userId, - "Replacing preferred"); } + addPreferredActivityInternal(filter, match, set, activity, true, userId, + "Replacing preferred"); } @Override diff --git a/services/core/java/com/android/server/pm/PackageManagerShellCommand.java b/services/core/java/com/android/server/pm/PackageManagerShellCommand.java index 4e4a0e420d86..6b804df2e068 100644 --- a/services/core/java/com/android/server/pm/PackageManagerShellCommand.java +++ b/services/core/java/com/android/server/pm/PackageManagerShellCommand.java @@ -2423,6 +2423,16 @@ class PackageManagerShellCommand extends ShellCommand { sessionParams.setStaged(); break; case "--enable-rollback": + if (params.installerPackageName == null) { + // com.android.shell has the TEST_MANAGE_ROLLBACKS + // permission needed to enable rollback for non-module + // packages, which is likely what the user wants when + // enabling rollback through the shell command. Set + // the installer to com.android.shell if no installer + // has been provided so that the user doesn't have to + // remember to set it themselves. + params.installerPackageName = "com.android.shell"; + } sessionParams.installFlags |= PackageManager.INSTALL_ENABLE_ROLLBACK; break; default: diff --git a/services/core/java/com/android/server/pm/Settings.java b/services/core/java/com/android/server/pm/Settings.java index 41a8a776e3f7..dae07bdc37d5 100644 --- a/services/core/java/com/android/server/pm/Settings.java +++ b/services/core/java/com/android/server/pm/Settings.java @@ -810,15 +810,20 @@ public final class Settings { /** * Registers a user ID with the system. Potentially allocates a new user ID. + * @return {@code true} if a new app ID was created in the process. {@code false} can be + * returned in the case that a shared user ID already exists or the explicit app ID is + * already registered. * @throws PackageManagerException If a user ID could not be allocated. */ - void registerAppIdLPw(PackageSetting p) throws PackageManagerException { + boolean registerAppIdLPw(PackageSetting p) throws PackageManagerException { + final boolean createdNew; if (p.appId == 0) { // Assign new user ID p.appId = acquireAndRegisterNewAppIdLPw(p); + createdNew = true; } else { // Add new setting to list of user IDs - registerExistingAppIdLPw(p.appId, p, p.name); + createdNew = registerExistingAppIdLPw(p.appId, p, p.name); } if (p.appId < 0) { PackageManagerService.reportSettingsProblem(Log.WARN, @@ -826,6 +831,7 @@ public final class Settings { throw new PackageManagerException(INSTALL_FAILED_INSUFFICIENT_STORAGE, "Package " + p.name + " could not be assigned a valid UID"); } + return createdNew; } /** diff --git a/services/core/java/com/android/server/power/ThermalManagerService.java b/services/core/java/com/android/server/power/ThermalManagerService.java index 03814453fc52..1552fd517d30 100644 --- a/services/core/java/com/android/server/power/ThermalManagerService.java +++ b/services/core/java/com/android/server/power/ThermalManagerService.java @@ -312,10 +312,8 @@ public class ThermalManagerService extends SystemService { if (!mThermalEventListeners.register(listener, null)) { return false; } - if (mHalReady.get()) { - // Notify its callback after new client registered. - postEventListenerCurrentTemperatures(listener, null); - } + // Notify its callback after new client registered. + postEventListenerCurrentTemperatures(listener, null); return true; } finally { Binder.restoreCallingIdentity(token); @@ -334,10 +332,8 @@ public class ThermalManagerService extends SystemService { if (!mThermalEventListeners.register(listener, new Integer(type))) { return false; } - if (mHalReady.get()) { - // Notify its callback after new client registered. - postEventListenerCurrentTemperatures(listener, new Integer(type)); - } + // Notify its callback after new client registered. + postEventListenerCurrentTemperatures(listener, new Integer(type)); return true; } finally { Binder.restoreCallingIdentity(token); @@ -398,10 +394,8 @@ public class ThermalManagerService extends SystemService { if (!mThermalStatusListeners.register(listener)) { return false; } - if (mHalReady.get()) { - // Notify its callback after new client registered. - postStatusListener(listener); - } + // Notify its callback after new client registered. + postStatusListener(listener); return true; } finally { Binder.restoreCallingIdentity(token); diff --git a/services/core/java/com/android/server/role/RoleManagerService.java b/services/core/java/com/android/server/role/RoleManagerService.java index b2ac1b884483..654c47780f4a 100644 --- a/services/core/java/com/android/server/role/RoleManagerService.java +++ b/services/core/java/com/android/server/role/RoleManagerService.java @@ -147,6 +147,8 @@ public class RoleManagerService extends SystemService implements RoleUserState.C mLegacyRoleResolver = legacyRoleResolver; + RoleControllerManager.initializeRemoteServiceComponentName(context); + mUserManagerInternal = LocalServices.getService(UserManagerInternal.class); mAppOpsManager = context.getSystemService(AppOpsManager.class); @@ -231,7 +233,7 @@ public class RoleManagerService extends SystemService implements RoleUserState.C // Run grants again Slog.i(LOG_TAG, "Granting default permissions..."); CompletableFuture<Void> result = new CompletableFuture<>(); - getOrCreateControllerService(userId).grantDefaultRoles(FgThread.getExecutor(), + getOrCreateController(userId).grantDefaultRoles(FgThread.getExecutor(), successful -> { if (successful) { userState.setPackagesHash(packagesHash); @@ -318,7 +320,7 @@ public class RoleManagerService extends SystemService implements RoleUserState.C } @NonNull - private RoleControllerManager getOrCreateControllerService(@UserIdInt int userId) { + private RoleControllerManager getOrCreateController(@UserIdInt int userId) { synchronized (mLock) { RoleControllerManager controller = mControllers.get(userId); if (controller == null) { @@ -330,7 +332,8 @@ public class RoleManagerService extends SystemService implements RoleUserState.C } catch (NameNotFoundException e) { throw new RuntimeException(e); } - controller = new RoleControllerManager(context, FgThread.getHandler()); + controller = RoleControllerManager.createWithInitializedRemoteServiceComponentName( + FgThread.getHandler(), context); mControllers.put(userId, controller); } return controller; @@ -474,7 +477,7 @@ public class RoleManagerService extends SystemService implements RoleUserState.C Preconditions.checkStringNotEmpty(packageName, "packageName cannot be null or empty"); Preconditions.checkNotNull(callback, "callback cannot be null"); - getOrCreateControllerService(userId).onAddRoleHolder(roleName, packageName, flags, + getOrCreateController(userId).onAddRoleHolder(roleName, packageName, flags, callback); } @@ -494,7 +497,7 @@ public class RoleManagerService extends SystemService implements RoleUserState.C Preconditions.checkStringNotEmpty(packageName, "packageName cannot be null or empty"); Preconditions.checkNotNull(callback, "callback cannot be null"); - getOrCreateControllerService(userId).onRemoveRoleHolder(roleName, packageName, flags, + getOrCreateController(userId).onRemoveRoleHolder(roleName, packageName, flags, callback); } @@ -513,7 +516,7 @@ public class RoleManagerService extends SystemService implements RoleUserState.C Preconditions.checkStringNotEmpty(roleName, "roleName cannot be null or empty"); Preconditions.checkNotNull(callback, "callback cannot be null"); - getOrCreateControllerService(userId).onClearRoleHolders(roleName, flags, callback); + getOrCreateController(userId).onClearRoleHolders(roleName, flags, callback); } @Override @@ -738,10 +741,10 @@ public class RoleManagerService extends SystemService implements RoleUserState.C } }); if (packageName != null) { - getOrCreateControllerService(userId).onAddRoleHolder(RoleManager.ROLE_BROWSER, + getOrCreateController(userId).onAddRoleHolder(RoleManager.ROLE_BROWSER, packageName, 0, callback); } else { - getOrCreateControllerService(userId).onClearRoleHolders(RoleManager.ROLE_BROWSER, 0, + getOrCreateController(userId).onClearRoleHolders(RoleManager.ROLE_BROWSER, 0, callback); } try { @@ -762,10 +765,10 @@ public class RoleManagerService extends SystemService implements RoleUserState.C } }); if (packageName != null) { - getOrCreateControllerService(userId).onAddRoleHolder(RoleManager.ROLE_BROWSER, + getOrCreateController(userId).onAddRoleHolder(RoleManager.ROLE_BROWSER, packageName, 0, callback); } else { - getOrCreateControllerService(userId).onClearRoleHolders(RoleManager.ROLE_BROWSER, 0, + getOrCreateController(userId).onClearRoleHolders(RoleManager.ROLE_BROWSER, 0, callback); } } @@ -791,10 +794,10 @@ public class RoleManagerService extends SystemService implements RoleUserState.C callback.accept(successful); }); if (packageName != null) { - getOrCreateControllerService(userId).onAddRoleHolder(RoleManager.ROLE_HOME, + getOrCreateController(userId).onAddRoleHolder(RoleManager.ROLE_HOME, packageName, 0, remoteCallback); } else { - getOrCreateControllerService(userId).onClearRoleHolders(RoleManager.ROLE_HOME, 0, + getOrCreateController(userId).onClearRoleHolders(RoleManager.ROLE_HOME, 0, remoteCallback); } } diff --git a/services/core/java/com/android/server/wm/BoundsAnimationController.java b/services/core/java/com/android/server/wm/BoundsAnimationController.java index c1d872f23f0f..b5e706710823 100644 --- a/services/core/java/com/android/server/wm/BoundsAnimationController.java +++ b/services/core/java/com/android/server/wm/BoundsAnimationController.java @@ -435,6 +435,18 @@ public class BoundsAnimationController { moveFromFullscreen, moveToFullscreen, animationType); } + /** + * Cancel existing animation if the destination was modified. + */ + void cancel(final BoundsAnimationTarget target) { + final BoundsAnimator existing = mRunningAnimations.get(target); + if (existing != null) { + // Cancel animation. Since its already started, send animation end to client. + if (DEBUG) Slog.d(TAG, "cancel: mTarget= " + target); + existing.cancelAndCallAnimationEnd(); + } + } + @VisibleForTesting BoundsAnimator animateBoundsImpl(final BoundsAnimationTarget target, Rect from, Rect to, int animationDuration, @SchedulePipModeChangedState int schedulePipModeChangedState, diff --git a/services/core/java/com/android/server/wm/TaskStack.java b/services/core/java/com/android/server/wm/TaskStack.java index bdb4d0474865..7515b3fa1249 100644 --- a/services/core/java/com/android/server/wm/TaskStack.java +++ b/services/core/java/com/android/server/wm/TaskStack.java @@ -388,7 +388,9 @@ public class TaskStack extends WindowContainer<Task> implements * @return true if bounds were updated to some non-empty value. */ boolean calculatePinnedBoundsForConfigChange(Rect inOutBounds) { + boolean animating = false; if ((mBoundsAnimatingRequested || mBoundsAnimating) && !mBoundsAnimationTarget.isEmpty()) { + animating = true; getFinalAnimationBounds(mTmpRect2); } else { mTmpRect2.set(inOutBounds); @@ -398,6 +400,13 @@ public class TaskStack extends WindowContainer<Task> implements if (updated) { inOutBounds.set(mTmpRect3); + // The final boundary is updated while there is an existing boundary animation. Let's + // cancel this animation to prevent the obsolete animation overwritten updated bounds. + if (animating && !inOutBounds.equals(mBoundsAnimationTarget)) { + final DisplayContent displayContent = getDisplayContent(); + displayContent.mBoundsAnimationController.getHandler().post(() -> + displayContent.mBoundsAnimationController.cancel(this)); + } // Once we've set the bounds based on the rotation of the old bounds in the new // orientation, clear the animation target bounds since they are obsolete, and // cancel any currently running animations @@ -1585,7 +1594,6 @@ public class TaskStack extends WindowContainer<Task> implements mBoundsAnimatingRequested = false; mBoundsAnimating = true; - mCancelCurrentBoundsAnimation = false; mAnimationType = animationType; // If we are changing UI mode, as in the PiP to fullscreen @@ -1645,7 +1653,7 @@ public class TaskStack extends WindowContainer<Task> implements mBoundsAnimationTarget, false /* forceUpdate */); } - if (finalStackSize != null) { + if (finalStackSize != null && !mCancelCurrentBoundsAnimation) { setPinnedStackSize(finalStackSize, null); } else { // We have been canceled, so the final stack size is null, still run the @@ -1758,6 +1766,7 @@ public class TaskStack extends WindowContainer<Task> implements } final @BoundsAnimationController.AnimationType int animationType = intendedAnimationType; + mCancelCurrentBoundsAnimation = false; displayContent.mBoundsAnimationController.getHandler().post(() -> { displayContent.mBoundsAnimationController.animateBounds(this, fromBounds, finalToBounds, animationDuration, finalSchedulePipModeChangedState, diff --git a/services/java/com/android/server/SystemServer.java b/services/java/com/android/server/SystemServer.java index 0cd730b68c72..2a2de7796424 100644 --- a/services/java/com/android/server/SystemServer.java +++ b/services/java/com/android/server/SystemServer.java @@ -32,7 +32,6 @@ import android.content.Context; import android.content.Intent; import android.content.pm.PackageItemInfo; import android.content.pm.PackageManager; -import android.content.pm.PackageManagerInternal; import android.content.res.Configuration; import android.content.res.Resources.Theme; import android.database.sqlite.SQLiteCompatibilityWalFlags; @@ -1248,11 +1247,6 @@ public final class SystemServer { mSystemServiceManager.startService(CONTENT_SUGGESTIONS_SERVICE_CLASS); traceEnd(); - // NOTE: ClipboardService indirectly depends on IntelligenceService - traceBeginAndSlog("StartClipboardService"); - mSystemServiceManager.startService(ClipboardService.class); - traceEnd(); - traceBeginAndSlog("InitNetworkStackClient"); try { NetworkStackClient.getInstance().init(); @@ -1887,6 +1881,11 @@ public final class SystemServer { traceEnd(); } + // NOTE: ClipboardService depends on ContentCapture and Autofill + traceBeginAndSlog("StartClipboardService"); + mSystemServiceManager.startService(ClipboardService.class); + traceEnd(); + traceBeginAndSlog("AppServiceManager"); mSystemServiceManager.startService(AppBindingService.Lifecycle.class); traceEnd(); diff --git a/services/net/java/android/net/ip/RouterAdvertisementDaemon.java b/services/net/java/android/net/ip/RouterAdvertisementDaemon.java index 339607bbc73d..59aea21f46c8 100644 --- a/services/net/java/android/net/ip/RouterAdvertisementDaemon.java +++ b/services/net/java/android/net/ip/RouterAdvertisementDaemon.java @@ -36,6 +36,7 @@ import android.system.StructTimeval; import android.util.Log; import com.android.internal.annotations.GuardedBy; +import com.android.internal.util.TrafficStatsConstants; import libcore.io.IoBridge; @@ -586,7 +587,8 @@ public class RouterAdvertisementDaemon { private boolean createSocket() { final int SEND_TIMEOUT_MS = 300; - final int oldTag = TrafficStats.getAndSetThreadStatsTag(TrafficStats.TAG_SYSTEM_NEIGHBOR); + final int oldTag = TrafficStats.getAndSetThreadStatsTag( + TrafficStatsConstants.TAG_SYSTEM_NEIGHBOR); try { mSocket = Os.socket(AF_INET6, SOCK_RAW, IPPROTO_ICMPV6); // Setting SNDTIMEO is purely for defensive purposes. diff --git a/services/robotests/backup/src/com/android/server/backup/UserBackupManagerServiceTest.java b/services/robotests/backup/src/com/android/server/backup/UserBackupManagerServiceTest.java index c8d1eb413ecf..74fe81c6f68e 100644 --- a/services/robotests/backup/src/com/android/server/backup/UserBackupManagerServiceTest.java +++ b/services/robotests/backup/src/com/android/server/backup/UserBackupManagerServiceTest.java @@ -30,6 +30,7 @@ import static org.mockito.ArgumentMatchers.anyInt; import static org.mockito.ArgumentMatchers.eq; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.never; +import static org.mockito.Mockito.times; import static org.mockito.Mockito.verify; import static org.mockito.Mockito.when; import static org.robolectric.Shadows.shadowOf; @@ -38,6 +39,7 @@ import static org.testng.Assert.expectThrows; import android.app.backup.BackupManager; import android.app.backup.IBackupObserver; import android.app.backup.ISelectBackupTransportCallback; +import android.content.BroadcastReceiver; import android.content.ComponentName; import android.content.Context; import android.content.ContextWrapper; @@ -48,6 +50,7 @@ import android.os.Binder; import android.os.HandlerThread; import android.os.PowerManager; import android.os.PowerSaveState; +import android.os.UserHandle; import android.platform.test.annotations.Presubmit; import android.provider.Settings; @@ -66,6 +69,7 @@ import org.junit.Before; import org.junit.Test; import org.junit.runner.RunWith; import org.mockito.Mock; +import org.mockito.Mockito; import org.mockito.MockitoAnnotations; import org.robolectric.RobolectricTestRunner; import org.robolectric.RuntimeEnvironment; @@ -1130,6 +1134,31 @@ public class UserBackupManagerServiceTest { /* transportManager */ null)); } + /** + * Test verifying that creating a new instance registers the broadcast receiver for package + * tracking + */ + @Test + public void testCreateAndInitializeService_registersPackageTrackingReceiver() throws Exception { + Context contextSpy = Mockito.spy(mContext); + + UserBackupManagerService service = UserBackupManagerService.createAndInitializeService( + USER_ID, + contextSpy, + new Trampoline(mContext), + mBackupThread, + mBaseStateDir, + mDataDir, + mTransportManager); + + BroadcastReceiver packageTrackingReceiver = service.getPackageTrackingReceiver(); + assertThat(packageTrackingReceiver).isNotNull(); + + // One call for package changes and one call for sd card events. + verify(contextSpy, times(2)).registerReceiverAsUser( + eq(packageTrackingReceiver), eq(UserHandle.of(USER_ID)), any(), any(), any()); + } + private UserBackupManagerService createUserBackupManagerServiceAndRunTasks() { return BackupManagerServiceTestUtils.createUserBackupManagerServiceAndRunTasks( USER_ID, mContext, mBackupThread, mBaseStateDir, mDataDir, mTransportManager); diff --git a/services/robotests/backup/src/com/android/server/backup/keyvalue/KeyValueBackupTaskTest.java b/services/robotests/backup/src/com/android/server/backup/keyvalue/KeyValueBackupTaskTest.java index 164570a84cb0..cc64323e51c7 100644 --- a/services/robotests/backup/src/com/android/server/backup/keyvalue/KeyValueBackupTaskTest.java +++ b/services/robotests/backup/src/com/android/server/backup/keyvalue/KeyValueBackupTaskTest.java @@ -86,6 +86,7 @@ import android.content.Intent; import android.content.pm.ApplicationInfo; import android.content.pm.PackageInfo; import android.content.pm.PackageManager; +import android.content.pm.PackageManagerInternal; import android.net.Uri; import android.os.ConditionVariable; import android.os.DeadObjectException; @@ -100,6 +101,7 @@ import android.util.Pair; import com.android.internal.backup.IBackupTransport; import com.android.server.EventLogTags; +import com.android.server.LocalServices; import com.android.server.backup.BackupRestoreTask; import com.android.server.backup.DataChangedJournal; import com.android.server.backup.KeyValueBackupJob; @@ -116,7 +118,6 @@ import com.android.server.backup.testing.TransportTestUtils; import com.android.server.backup.testing.TransportTestUtils.TransportMock; import com.android.server.testing.shadows.FrameworkShadowLooper; import com.android.server.testing.shadows.ShadowApplicationPackageManager; -import com.android.server.testing.shadows.ShadowBackupActivityThread; import com.android.server.testing.shadows.ShadowBackupDataInput; import com.android.server.testing.shadows.ShadowBackupDataOutput; import com.android.server.testing.shadows.ShadowEventLog; @@ -163,8 +164,7 @@ import java.util.stream.Stream; ShadowBackupDataInput.class, ShadowBackupDataOutput.class, ShadowEventLog.class, - ShadowQueuedWork.class, - ShadowBackupActivityThread.class, + ShadowQueuedWork.class }) @Presubmit public class KeyValueBackupTaskTest { @@ -179,6 +179,7 @@ public class KeyValueBackupTaskTest { @Mock private IBackupObserver mObserver; @Mock private IBackupManagerMonitor mMonitor; @Mock private OnTaskFinishedListener mListener; + @Mock private PackageManagerInternal mPackageManagerInternal; private UserBackupManagerService mBackupManagerService; private TransportData mTransport; private ShadowLooper mShadowBackupLooper; @@ -243,6 +244,11 @@ public class KeyValueBackupTaskTest { mShadowBackupLooper = shadowOf(mBackupHandler.getLooper()); ShadowEventLog.setUp(); mReporter = spy(new KeyValueBackupReporter(mBackupManagerService, mObserver, mMonitor)); + + when(mPackageManagerInternal.getApplicationEnabledState(any(), anyInt())) + .thenReturn(PackageManager.COMPONENT_ENABLED_STATE_ENABLED); + LocalServices.removeServiceForTest(PackageManagerInternal.class); + LocalServices.addService(PackageManagerInternal.class, mPackageManagerInternal); } @After @@ -471,7 +477,7 @@ public class KeyValueBackupTaskTest { TransportMock transportMock = setUpInitializedTransport(mTransport); setUpAgentWithData(PACKAGE_1); BackupAgent pmAgent = spy(createPmAgent()); - when(mBackupManagerService.makeMetadataAgent()).thenReturn(forward(pmAgent)); + doReturn(forward(pmAgent)).when(mBackupManagerService).makeMetadataAgent(); KeyValueBackupTask task = createKeyValueBackupTask(transportMock, true, PACKAGE_1); runTask(task); @@ -484,7 +490,7 @@ public class KeyValueBackupTaskTest { TransportMock transportMock = setUpInitializedTransport(mTransport); setUpAgentWithData(PACKAGE_1); BackupAgent pmAgent = spy(createPmAgent()); - when(mBackupManagerService.makeMetadataAgent()).thenReturn(forward(pmAgent)); + doReturn(forward(pmAgent)).when(mBackupManagerService).makeMetadataAgent(); KeyValueBackupTask task = createKeyValueBackupTask(transportMock, true, PACKAGE_1, PM_PACKAGE); @@ -498,7 +504,7 @@ public class KeyValueBackupTaskTest { TransportMock transportMock = setUpInitializedTransport(mTransport); setUpAgentWithData(PACKAGE_1); BackupAgent pmAgent = spy(createPmAgent()); - when(mBackupManagerService.makeMetadataAgent()).thenReturn(forward(pmAgent)); + doReturn(forward(pmAgent)).when(mBackupManagerService).makeMetadataAgent(); KeyValueBackupTask task = createKeyValueBackupTask(transportMock, false, PACKAGE_1); runTask(task); @@ -1307,7 +1313,7 @@ public class KeyValueBackupTaskTest { argThat(packageInfo(PM_PACKAGE)), any(), anyInt())) .then(copyBackupDataTo(backupDataPath)); BackupAgent pmAgent = spy(createPmAgent()); - when(mBackupManagerService.makeMetadataAgent()).thenReturn(forward(pmAgent)); + doReturn(forward(pmAgent)).when(mBackupManagerService).makeMetadataAgent(); agentOnBackupDo( pmAgent, (oldState, dataOutput, newState) -> { @@ -1371,7 +1377,7 @@ public class KeyValueBackupTaskTest { setUpAgent(PACKAGE_1); when(transportMock.transport.finishBackup()).thenReturn(BackupTransport.TRANSPORT_OK); BackupAgent pmAgent = spy(createPmAgent()); - when(mBackupManagerService.makeMetadataAgent()).thenReturn(forward(pmAgent)); + doReturn(forward(pmAgent)).when(mBackupManagerService).makeMetadataAgent(); agentOnBackupDo( pmAgent, (oldState, dataOutput, newState) -> { @@ -1395,7 +1401,7 @@ public class KeyValueBackupTaskTest { setUpAgent(PACKAGE_1); when(transportMock.transport.finishBackup()).thenReturn(BackupTransport.TRANSPORT_OK); BackupAgent pmAgent = spy(createPmAgent()); - when(mBackupManagerService.makeMetadataAgent()).thenReturn(forward(pmAgent)); + doReturn(forward(pmAgent)).when(mBackupManagerService).makeMetadataAgent(); agentOnBackupDo( pmAgent, (oldState, dataOutput, newState) -> { @@ -1957,7 +1963,7 @@ public class KeyValueBackupTaskTest { TransportMock transportMock = setUpInitializedTransport(mTransport); setUpAgent(PACKAGE_1); BackupAgent pmAgent = createThrowingPmAgent(new RuntimeException()); - when(mBackupManagerService.makeMetadataAgent()).thenReturn(pmAgent); + doReturn(pmAgent).when(mBackupManagerService).makeMetadataAgent(); KeyValueBackupTask task = createKeyValueBackupTask(transportMock, PACKAGE_1); runTask(task); @@ -1970,7 +1976,7 @@ public class KeyValueBackupTaskTest { TransportMock transportMock = setUpInitializedTransport(mTransport); setUpAgent(PACKAGE_1); BackupAgent pmAgent = createThrowingPmAgent(new RuntimeException()); - when(mBackupManagerService.makeMetadataAgent()).thenReturn(pmAgent); + doReturn(pmAgent).when(mBackupManagerService).makeMetadataAgent(); KeyValueBackupTask task = createKeyValueBackupTask(transportMock, PACKAGE_1); runTask(task); @@ -1983,7 +1989,7 @@ public class KeyValueBackupTaskTest { TransportMock transportMock = setUpInitializedTransport(mTransport); setUpAgent(PACKAGE_1); BackupAgent pmAgent = createThrowingPmAgent(new RuntimeException()); - when(mBackupManagerService.makeMetadataAgent()).thenReturn(pmAgent); + doReturn(pmAgent).when(mBackupManagerService).makeMetadataAgent(); KeyValueBackupTask task = createKeyValueBackupTask(transportMock, PACKAGE_1); runTask(task); @@ -1996,7 +2002,7 @@ public class KeyValueBackupTaskTest { TransportMock transportMock = setUpInitializedTransport(mTransport); setUpAgent(PACKAGE_1); BackupAgent pmAgent = spy(createPmAgent()); - when(mBackupManagerService.makeMetadataAgent()).thenReturn(forward(pmAgent)); + doReturn(forward(pmAgent)).when(mBackupManagerService).makeMetadataAgent(); KeyValueBackupTask task = createKeyValueBackupTask(transportMock, PACKAGE_1); agentOnBackupDo( pmAgent, (oldState, dataOutput, newState) -> runInWorkerThread(task::markCancel)); @@ -2011,7 +2017,7 @@ public class KeyValueBackupTaskTest { TransportMock transportMock = setUpInitializedTransport(mTransport); setUpAgent(PACKAGE_1); BackupAgent pmAgent = spy(createPmAgent()); - when(mBackupManagerService.makeMetadataAgent()).thenReturn(forward(pmAgent)); + doReturn(forward(pmAgent)).when(mBackupManagerService).makeMetadataAgent(); KeyValueBackupTask task = createKeyValueBackupTask(transportMock, PACKAGE_1); agentOnBackupDo( pmAgent, (oldState, dataOutput, newState) -> runInWorkerThread(task::markCancel)); diff --git a/services/robotests/src/com/android/server/testing/shadows/ShadowBackupActivityThread.java b/services/robotests/src/com/android/server/testing/shadows/ShadowBackupActivityThread.java deleted file mode 100644 index ca2e3b6dafef..000000000000 --- a/services/robotests/src/com/android/server/testing/shadows/ShadowBackupActivityThread.java +++ /dev/null @@ -1,79 +0,0 @@ -/* - * Copyright (C) 2019 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package com.android.server.testing.shadows; - -import android.app.ActivityThread; -import android.content.pm.ApplicationInfo; -import android.content.pm.IPackageManager; -import android.content.pm.PackageManager; -import android.os.RemoteException; - -import org.robolectric.RuntimeEnvironment; -import org.robolectric.annotation.Implementation; -import org.robolectric.annotation.Implements; -import org.robolectric.shadows.ShadowActivityThread; - -import java.lang.reflect.InvocationHandler; -import java.lang.reflect.Method; -import java.lang.reflect.Proxy; - -import javax.annotation.Nonnull; - -/** - * Extends the existing {@link ShadowActivityThread} to add support for - * {@link PackageManager#getApplicationEnabledSetting(String)} in the shadow {@link PackageManager} - * returned by {@link ShadowBackupActivityThread#getPackageManager()}. - */ -@Implements(value = ActivityThread.class, isInAndroidSdk = false, looseSignatures = true) -public class ShadowBackupActivityThread extends ShadowActivityThread { - @Implementation - public static Object getPackageManager() { - ClassLoader classLoader = ShadowActivityThread.class.getClassLoader(); - Class<?> iPackageManagerClass; - try { - iPackageManagerClass = classLoader.loadClass("android.content.pm.IPackageManager"); - } catch (ClassNotFoundException e) { - throw new RuntimeException(e); - } - - return Proxy.newProxyInstance( - classLoader, - new Class[] {iPackageManagerClass}, - new InvocationHandler() { - @Override - public Object invoke(Object proxy, @Nonnull Method method, Object[] args) - throws Exception { - if (method.getName().equals("getApplicationInfo")) { - String packageName = (String) args[0]; - int flags = (Integer) args[1]; - - try { - return RuntimeEnvironment.application - .getPackageManager() - .getApplicationInfo(packageName, flags); - } catch (PackageManager.NameNotFoundException e) { - throw new RemoteException(e.getMessage()); - } - } else if (method.getName().equals("getApplicationEnabledSetting")) { - return 0; - } else { - return null; - } - } - }); - } -} diff --git a/services/tests/servicestests/src/com/android/server/attention/AttentionManagerServiceTest.java b/services/tests/servicestests/src/com/android/server/attention/AttentionManagerServiceTest.java new file mode 100644 index 000000000000..8426a0bb2c11 --- /dev/null +++ b/services/tests/servicestests/src/com/android/server/attention/AttentionManagerServiceTest.java @@ -0,0 +1,145 @@ +/* + * 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.attention; + +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.anyLong; +import static org.mockito.Mockito.doNothing; +import static org.mockito.Mockito.doReturn; +import static org.mockito.Mockito.spy; +import static org.mockito.Mockito.verify; + +import android.attention.AttentionManagerInternal.AttentionCallbackInternal; +import android.content.ComponentName; +import android.content.Context; +import android.os.IBinder; +import android.os.IPowerManager; +import android.os.PowerManager; +import android.os.RemoteException; +import android.service.attention.IAttentionCallback; +import android.service.attention.IAttentionService; + +import androidx.test.filters.SmallTest; + +import com.android.server.attention.AttentionManagerService.AttentionCheck; +import com.android.server.attention.AttentionManagerService.AttentionHandler; +import com.android.server.attention.AttentionManagerService.UserState; + +import org.junit.Before; +import org.junit.Test; +import org.mockito.Mock; +import org.mockito.Mockito; +import org.mockito.MockitoAnnotations; + +/** + * Tests for {@link com.android.server.attention.AttentionManagerService} + */ +@SmallTest +public class AttentionManagerServiceTest { + private AttentionManagerService mSpyAttentionManager; + private UserState mSpyUserState; + private final int mTimeout = 1000; + @Mock private AttentionCallbackInternal mMockAttentionCallbackInternal; + @Mock private AttentionHandler mMockHandler; + @Mock private IAttentionCallback mMockIAttentionCallback; + @Mock private IPowerManager mMockIPowerManager; + @Mock Context mContext; + + @Before + public void setUp() throws RemoteException { + MockitoAnnotations.initMocks(this); + // setup context mock + doReturn(true).when(mContext).bindServiceAsUser(any(), any(), anyInt(), any()); + // setup power manager mock + PowerManager mPowerManager; + doReturn(true).when(mMockIPowerManager).isInteractive(); + mPowerManager = new PowerManager(mContext, mMockIPowerManager, null); + + Object mLock = new Object(); + // setup a spy on attention manager + AttentionManagerService mAttentionManager = new AttentionManagerService( + mContext, + mPowerManager, + mLock, + mMockHandler); + mSpyAttentionManager = Mockito.spy(mAttentionManager); + // setup a spy on user state + ComponentName componentName = new ComponentName("a", "b"); + mSpyAttentionManager.mComponentName = componentName; + UserState mUserState = new UserState(0, + mContext, + mLock, + componentName); + mUserState.mService = new MockIAttentionService(); + mSpyUserState = spy(mUserState); + } + + @Test + public void testCancelAttentionCheck_noCrashWhenNoUserStateLocked() { + mSpyAttentionManager.cancelAttentionCheck(null); + } + + @Test + public void testCancelAttentionCheck_noCrashWhenCallbackMismatched() { + mSpyUserState.mCurrentAttentionCheck = + new AttentionCheck(mMockAttentionCallbackInternal, mMockIAttentionCallback); + doReturn(mSpyUserState).when(mSpyAttentionManager).peekCurrentUserStateLocked(); + mSpyAttentionManager.cancelAttentionCheck(null); + } + + @Test + public void testCancelAttentionCheck_cancelCallbackWhenMatched() { + mSpyUserState.mCurrentAttentionCheck = + new AttentionCheck(mMockAttentionCallbackInternal, mMockIAttentionCallback); + doReturn(mSpyUserState).when(mSpyAttentionManager).peekCurrentUserStateLocked(); + mSpyAttentionManager.cancelAttentionCheck(mMockAttentionCallbackInternal); + verify(mSpyAttentionManager).cancel(any()); + } + + @Test + public void testCheckAttention_returnFalseWhenPowerManagerNotInteract() throws RemoteException { + doReturn(false).when(mMockIPowerManager).isInteractive(); + AttentionCallbackInternal callback = Mockito.mock(AttentionCallbackInternal.class); + assertThat(mSpyAttentionManager.checkAttention(mTimeout, callback)).isFalse(); + } + + @Test + public void testCheckAttention_callOnSuccess() throws RemoteException { + doReturn(true).when(mSpyAttentionManager).isServiceEnabled(); + doReturn(true).when(mMockIPowerManager).isInteractive(); + doReturn(mSpyUserState).when(mSpyAttentionManager).getOrCreateCurrentUserStateLocked(); + doNothing().when(mSpyAttentionManager).freeIfInactiveLocked(); + + AttentionCallbackInternal callback = Mockito.mock(AttentionCallbackInternal.class); + mSpyAttentionManager.checkAttention(mTimeout, callback); + verify(callback).onSuccess(anyInt(), anyLong()); + } + + private class MockIAttentionService implements IAttentionService { + public void checkAttention(IAttentionCallback callback) throws RemoteException { + callback.onSuccess(0, 0); + } + public void cancelAttentionCheck(IAttentionCallback callback) { + } + public IBinder asBinder() { + return null; + } + } +} diff --git a/services/tests/servicestests/src/com/android/server/backup/testutils/IPackageManagerStub.java b/services/tests/servicestests/src/com/android/server/backup/testutils/IPackageManagerStub.java deleted file mode 100644 index 97a6e6657b7a..000000000000 --- a/services/tests/servicestests/src/com/android/server/backup/testutils/IPackageManagerStub.java +++ /dev/null @@ -1,1217 +0,0 @@ -/* - * Copyright (C) 2019 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License - */ - -package com.android.server.backup.testutils; - -import android.content.ComponentName; -import android.content.Intent; -import android.content.IntentFilter; -import android.content.IntentSender; -import android.content.pm.ActivityInfo; -import android.content.pm.ApplicationInfo; -import android.content.pm.ChangedPackages; -import android.content.pm.IDexModuleRegisterCallback; -import android.content.pm.IOnPermissionsChangeListener; -import android.content.pm.IPackageDataObserver; -import android.content.pm.IPackageDeleteObserver; -import android.content.pm.IPackageDeleteObserver2; -import android.content.pm.IPackageInstaller; -import android.content.pm.IPackageManager; -import android.content.pm.IPackageMoveObserver; -import android.content.pm.IPackageStatsObserver; -import android.content.pm.InstrumentationInfo; -import android.content.pm.KeySet; -import android.content.pm.ModuleInfo; -import android.content.pm.PackageInfo; -import android.content.pm.PackageManager; -import android.content.pm.ParceledListSlice; -import android.content.pm.PermissionGroupInfo; -import android.content.pm.PermissionInfo; -import android.content.pm.ProviderInfo; -import android.content.pm.ResolveInfo; -import android.content.pm.ServiceInfo; -import android.content.pm.SuspendDialogInfo; -import android.content.pm.VerifierDeviceIdentity; -import android.content.pm.VersionedPackage; -import android.content.pm.dex.IArtManager; -import android.graphics.Bitmap; -import android.os.IBinder; -import android.os.PersistableBundle; -import android.os.RemoteException; - -import java.util.List; - -/** - * Stub for IPackageManager to use in tests. - */ -public class IPackageManagerStub implements IPackageManager { - public static PackageInfo sPackageInfo; - public static int sApplicationEnabledSetting = PackageManager.COMPONENT_ENABLED_STATE_DEFAULT; - - @Override - public PackageInfo getPackageInfo(String packageName, int flags, int userId) - throws RemoteException { - return sPackageInfo; - } - - @Override - public int getApplicationEnabledSetting(String packageName, int userId) throws RemoteException { - return sApplicationEnabledSetting; - } - - @Override - public void checkPackageStartable(String packageName, int userId) throws RemoteException { - - } - - @Override - public boolean isPackageAvailable(String packageName, int userId) throws RemoteException { - return false; - } - - @Override - public PackageInfo getPackageInfoVersioned(VersionedPackage versionedPackage, int flags, - int userId) throws RemoteException { - return null; - } - - @Override - public int getPackageUid(String packageName, int flags, int userId) throws RemoteException { - return 0; - } - - @Override - public int[] getPackageGids(String packageName, int flags, int userId) throws RemoteException { - return new int[0]; - } - - @Override - public String[] currentToCanonicalPackageNames(String[] names) throws RemoteException { - return new String[0]; - } - - @Override - public String[] canonicalToCurrentPackageNames(String[] names) throws RemoteException { - return new String[0]; - } - - @Override - public PermissionInfo getPermissionInfo(String name, String packageName, int flags) - throws RemoteException { - return null; - } - - @Override - public ParceledListSlice queryPermissionsByGroup(String group, int flags) - throws RemoteException { - return null; - } - - @Override - public PermissionGroupInfo getPermissionGroupInfo(String name, int flags) - throws RemoteException { - return null; - } - - @Override - public ParceledListSlice getAllPermissionGroups(int flags) throws RemoteException { - return null; - } - - @Override - public ApplicationInfo getApplicationInfo(String packageName, int flags, int userId) - throws RemoteException { - return null; - } - - @Override - public ActivityInfo getActivityInfo(ComponentName className, int flags, int userId) - throws RemoteException { - return null; - } - - @Override - public boolean activitySupportsIntent(ComponentName className, Intent intent, - String resolvedType) - throws RemoteException { - return false; - } - - @Override - public ActivityInfo getReceiverInfo(ComponentName className, int flags, int userId) - throws RemoteException { - return null; - } - - @Override - public ServiceInfo getServiceInfo(ComponentName className, int flags, int userId) - throws RemoteException { - return null; - } - - @Override - public ProviderInfo getProviderInfo(ComponentName className, int flags, int userId) - throws RemoteException { - return null; - } - - @Override - public int checkPermission(String permName, String pkgName, int userId) throws RemoteException { - return 0; - } - - @Override - public int checkUidPermission(String permName, int uid) throws RemoteException { - return 0; - } - - @Override - public boolean addPermission(PermissionInfo info) throws RemoteException { - return false; - } - - @Override - public void removePermission(String name) throws RemoteException { - - } - - @Override - public void grantRuntimePermission(String packageName, String permissionName, int userId) - throws RemoteException { - - } - - @Override - public void revokeRuntimePermission(String packageName, String permissionName, int userId) - throws RemoteException { - - } - - @Override - public void resetRuntimePermissions() throws RemoteException { - - } - - @Override - public int getPermissionFlags(String permissionName, String packageName, int userId) - throws RemoteException { - return 0; - } - - @Override - public void updatePermissionFlags(String permissionName, String packageName, int flagMask, - int flagValues, boolean checkAdjustPolicyFlagPermission, int userId) - throws RemoteException { - - } - - @Override - public void updatePermissionFlagsForAllApps(int flagMask, int flagValues, int userId) - throws RemoteException { - - } - - @Override - public List<String> getWhitelistedRestrictedPermissions(String packageName, int flags, - int userId) throws RemoteException { - return null; - } - - @Override - public boolean addWhitelistedRestrictedPermission(String packageName, String permission, - int whitelistFlags, int userId) throws RemoteException { - return false; - } - - @Override - public boolean removeWhitelistedRestrictedPermission(String packageName, String permission, - int whitelistFlags, int userId) throws RemoteException { - return false; - } - - @Override - public boolean shouldShowRequestPermissionRationale(String permissionName, String packageName, - int userId) throws RemoteException { - return false; - } - - @Override - public boolean isProtectedBroadcast(String actionName) throws RemoteException { - return false; - } - - @Override - public int checkSignatures(String pkg1, String pkg2) throws RemoteException { - return 0; - } - - @Override - public int checkUidSignatures(int uid1, int uid2) throws RemoteException { - return 0; - } - - @Override - public List<String> getAllPackages() throws RemoteException { - return null; - } - - @Override - public String[] getPackagesForUid(int uid) throws RemoteException { - return new String[0]; - } - - @Override - public String getNameForUid(int uid) throws RemoteException { - return null; - } - - @Override - public String[] getNamesForUids(int[] uids) throws RemoteException { - return new String[0]; - } - - @Override - public int getUidForSharedUser(String sharedUserName) throws RemoteException { - return 0; - } - - @Override - public int getFlagsForUid(int uid) throws RemoteException { - return 0; - } - - @Override - public int getPrivateFlagsForUid(int uid) throws RemoteException { - return 0; - } - - @Override - public boolean isUidPrivileged(int uid) throws RemoteException { - return false; - } - - @Override - public String[] getAppOpPermissionPackages(String permissionName) throws RemoteException { - return new String[0]; - } - - @Override - public ResolveInfo resolveIntent(Intent intent, String resolvedType, int flags, int userId) - throws RemoteException { - return null; - } - - @Override - public ResolveInfo findPersistentPreferredActivity(Intent intent, int userId) - throws RemoteException { - return null; - } - - @Override - public boolean canForwardTo(Intent intent, String resolvedType, int sourceUserId, - int targetUserId) throws RemoteException { - return false; - } - - @Override - public ParceledListSlice queryIntentActivities(Intent intent, String resolvedType, int flags, - int userId) throws RemoteException { - return null; - } - - @Override - public ParceledListSlice queryIntentActivityOptions(ComponentName caller, Intent[] specifics, - String[] specificTypes, Intent intent, String resolvedType, int flags, int userId) - throws RemoteException { - return null; - } - - @Override - public ParceledListSlice queryIntentReceivers(Intent intent, String resolvedType, int flags, - int userId) throws RemoteException { - return null; - } - - @Override - public ResolveInfo resolveService(Intent intent, String resolvedType, int flags, int userId) - throws RemoteException { - return null; - } - - @Override - public ParceledListSlice queryIntentServices(Intent intent, String resolvedType, int flags, - int userId) throws RemoteException { - return null; - } - - @Override - public ParceledListSlice queryIntentContentProviders(Intent intent, String resolvedType, - int flags, int userId) throws RemoteException { - return null; - } - - @Override - public ParceledListSlice getInstalledPackages(int flags, int userId) throws RemoteException { - return null; - } - - @Override - public ParceledListSlice getPackagesHoldingPermissions(String[] permissions, int flags, - int userId) throws RemoteException { - return null; - } - - @Override - public ParceledListSlice getInstalledApplications(int flags, int userId) - throws RemoteException { - return null; - } - - @Override - public ParceledListSlice getPersistentApplications(int flags) throws RemoteException { - return null; - } - - @Override - public ProviderInfo resolveContentProvider(String name, int flags, int userId) - throws RemoteException { - return null; - } - - @Override - public void querySyncProviders(List<String> outNames, List<ProviderInfo> outInfo) - throws RemoteException { - - } - - @Override - public ParceledListSlice queryContentProviders(String processName, int uid, int flags, - String metaDataKey) throws RemoteException { - return null; - } - - @Override - public InstrumentationInfo getInstrumentationInfo(ComponentName className, int flags) - throws RemoteException { - return null; - } - - @Override - public ParceledListSlice queryInstrumentation(String targetPackage, int flags) - throws RemoteException { - return null; - } - - @Override - public void finishPackageInstall(int token, boolean didLaunch) throws RemoteException { - - } - - @Override - public void setInstallerPackageName(String targetPackage, String installerPackageName) - throws RemoteException { - - } - - @Override - public void setApplicationCategoryHint(String packageName, int categoryHint, - String callerPackageName) throws RemoteException { - - } - - @Override - public void deletePackageAsUser(String packageName, int versionCode, - IPackageDeleteObserver observer, int userId, int flags) throws RemoteException { - - } - - @Override - public void deletePackageVersioned(VersionedPackage versionedPackage, - IPackageDeleteObserver2 observer, int userId, int flags) throws RemoteException { - - } - - @Override - public String getInstallerPackageName(String packageName) throws RemoteException { - return null; - } - - @Override - public void resetApplicationPreferences(int userId) throws RemoteException { - - } - - @Override - public ResolveInfo getLastChosenActivity(Intent intent, String resolvedType, int flags) - throws RemoteException { - return null; - } - - @Override - public void setLastChosenActivity(Intent intent, String resolvedType, int flags, - IntentFilter filter, int match, ComponentName activity) throws RemoteException { - - } - - @Override - public void addPreferredActivity(IntentFilter filter, int match, ComponentName[] set, - ComponentName activity, int userId) throws RemoteException { - - } - - @Override - public void replacePreferredActivity(IntentFilter filter, int match, ComponentName[] set, - ComponentName activity, int userId) throws RemoteException { - - } - - @Override - public void clearPackagePreferredActivities(String packageName) throws RemoteException { - - } - - @Override - public int getPreferredActivities(List<IntentFilter> outFilters, - List<ComponentName> outActivities, String packageName) throws RemoteException { - return 0; - } - - @Override - public void addPersistentPreferredActivity(IntentFilter filter, ComponentName activity, - int userId) throws RemoteException { - - } - - @Override - public void clearPackagePersistentPreferredActivities(String packageName, int userId) - throws RemoteException { - - } - - @Override - public void addCrossProfileIntentFilter(IntentFilter intentFilter, String ownerPackage, - int sourceUserId, int targetUserId, int flags) throws RemoteException { - - } - - @Override - public void clearCrossProfileIntentFilters(int sourceUserId, String ownerPackage) - throws RemoteException { - - } - - @Override - public String[] setDistractingPackageRestrictionsAsUser(String[] packageNames, - int restrictionFlags, int userId) throws RemoteException { - return new String[0]; - } - - @Override - public String[] setPackagesSuspendedAsUser(String[] packageNames, boolean suspended, - PersistableBundle appExtras, PersistableBundle launcherExtras, SuspendDialogInfo dialogInfo, - String callingPackage, int userId) throws RemoteException { - return new String[0]; - } - - @Override - public String[] getUnsuspendablePackagesForUser(String[] packageNames, int userId) - throws RemoteException { - return new String[0]; - } - - @Override - public boolean isPackageSuspendedForUser(String packageName, int userId) - throws RemoteException { - return false; - } - - @Override - public PersistableBundle getSuspendedPackageAppExtras(String packageName, int userId) - throws RemoteException { - return null; - } - - @Override - public byte[] getPreferredActivityBackup(int userId) throws RemoteException { - return new byte[0]; - } - - @Override - public void restorePreferredActivities(byte[] backup, int userId) throws RemoteException { - - } - - @Override - public byte[] getDefaultAppsBackup(int userId) throws RemoteException { - return new byte[0]; - } - - @Override - public void restoreDefaultApps(byte[] backup, int userId) throws RemoteException { - - } - - @Override - public byte[] getIntentFilterVerificationBackup(int userId) throws RemoteException { - return new byte[0]; - } - - @Override - public void restoreIntentFilterVerification(byte[] backup, int userId) throws RemoteException { - - } - - @Override - public ComponentName getHomeActivities(List<ResolveInfo> outHomeCandidates) - throws RemoteException { - return null; - } - - @Override - public void setHomeActivity(ComponentName className, int userId) throws RemoteException { - - } - - @Override - public void setComponentEnabledSetting(ComponentName componentName, int newState, int flags, - int userId) throws RemoteException { - - } - - @Override - public int getComponentEnabledSetting(ComponentName componentName, int userId) - throws RemoteException { - return 0; - } - - @Override - public void setApplicationEnabledSetting(String packageName, int newState, int flags, - int userId, - String callingPackage) throws RemoteException { - - } - - @Override - public void logAppProcessStartIfNeeded(String processName, int uid, String seinfo, - String apkFile, - int pid) throws RemoteException { - - } - - @Override - public void flushPackageRestrictionsAsUser(int userId) throws RemoteException { - - } - - @Override - public void setPackageStoppedState(String packageName, boolean stopped, int userId) - throws RemoteException { - - } - - @Override - public void freeStorageAndNotify(String volumeUuid, long freeStorageSize, int storageFlags, - IPackageDataObserver observer) throws RemoteException { - - } - - @Override - public void freeStorage(String volumeUuid, long freeStorageSize, int storageFlags, - IntentSender pi) throws RemoteException { - - } - - @Override - public void deleteApplicationCacheFiles(String packageName, IPackageDataObserver observer) - throws RemoteException { - - } - - @Override - public void deleteApplicationCacheFilesAsUser(String packageName, int userId, - IPackageDataObserver observer) throws RemoteException { - - } - - @Override - public void clearApplicationUserData(String packageName, IPackageDataObserver observer, - int userId) throws RemoteException { - - } - - @Override - public void clearApplicationProfileData(String packageName) throws RemoteException { - - } - - @Override - public void getPackageSizeInfo(String packageName, int userHandle, - IPackageStatsObserver observer) - throws RemoteException { - - } - - @Override - public String[] getSystemSharedLibraryNames() throws RemoteException { - return new String[0]; - } - - @Override - public ParceledListSlice getSystemAvailableFeatures() throws RemoteException { - return null; - } - - @Override - public boolean hasSystemFeature(String name, int version) throws RemoteException { - return false; - } - - @Override - public void enterSafeMode() throws RemoteException { - - } - - @Override - public boolean isSafeMode() throws RemoteException { - return false; - } - - @Override - public void systemReady() throws RemoteException { - - } - - @Override - public boolean hasSystemUidErrors() throws RemoteException { - return false; - } - - @Override - public void performFstrimIfNeeded() throws RemoteException { - - } - - @Override - public void updatePackagesIfNeeded() throws RemoteException { - - } - - @Override - public void notifyPackageUse(String packageName, int reason) throws RemoteException { - - } - - @Override - public void notifyDexLoad(String loadingPackageName, List<String> classLoadersNames, - List<String> classPaths, String loaderIsa) throws RemoteException { - - } - - @Override - public void registerDexModule(String packageName, String dexModulePath, boolean isSharedModule, - IDexModuleRegisterCallback callback) throws RemoteException { - - } - - @Override - public boolean performDexOptMode(String packageName, boolean checkProfiles, - String targetCompilerFilter, boolean force, boolean bootComplete, String splitName) - throws RemoteException { - return false; - } - - @Override - public boolean performDexOptSecondary(String packageName, String targetCompilerFilter, - boolean force) throws RemoteException { - return false; - } - - @Override - public boolean compileLayouts(String packageName) throws RemoteException { - return false; - } - - @Override - public void dumpProfiles(String packageName) throws RemoteException { - - } - - @Override - public void forceDexOpt(String packageName) throws RemoteException { - - } - - @Override - public boolean runBackgroundDexoptJob(List<String> packageNames) throws RemoteException { - return false; - } - - @Override - public void reconcileSecondaryDexFiles(String packageName) throws RemoteException { - - } - - @Override - public int getMoveStatus(int moveId) throws RemoteException { - return 0; - } - - @Override - public void registerMoveCallback(IPackageMoveObserver callback) throws RemoteException { - - } - - @Override - public void unregisterMoveCallback(IPackageMoveObserver callback) throws RemoteException { - - } - - @Override - public int movePackage(String packageName, String volumeUuid) throws RemoteException { - return 0; - } - - @Override - public int movePrimaryStorage(String volumeUuid) throws RemoteException { - return 0; - } - - @Override - public boolean addPermissionAsync(PermissionInfo info) throws RemoteException { - return false; - } - - @Override - public boolean setInstallLocation(int loc) throws RemoteException { - return false; - } - - @Override - public int getInstallLocation() throws RemoteException { - return 0; - } - - @Override - public int installExistingPackageAsUser(String packageName, int userId, int installFlags, - int installReason) throws RemoteException { - return 0; - } - - @Override - public void verifyPendingInstall(int id, int verificationCode) throws RemoteException { - - } - - @Override - public void extendVerificationTimeout(int id, int verificationCodeAtTimeout, - long millisecondsToDelay) throws RemoteException { - - } - - @Override - public void verifyIntentFilter(int id, int verificationCode, List<String> failedDomains) - throws RemoteException { - - } - - @Override - public int getIntentVerificationStatus(String packageName, int userId) throws RemoteException { - return 0; - } - - @Override - public boolean updateIntentVerificationStatus(String packageName, int status, int userId) - throws RemoteException { - return false; - } - - @Override - public ParceledListSlice getIntentFilterVerifications(String packageName) - throws RemoteException { - return null; - } - - @Override - public ParceledListSlice getAllIntentFilters(String packageName) throws RemoteException { - return null; - } - - @Override - public boolean setDefaultBrowserPackageName(String packageName, int userId) - throws RemoteException { - return false; - } - - @Override - public String getDefaultBrowserPackageName(int userId) throws RemoteException { - return null; - } - - @Override - public VerifierDeviceIdentity getVerifierDeviceIdentity() throws RemoteException { - return null; - } - - @Override - public boolean isFirstBoot() throws RemoteException { - return false; - } - - @Override - public boolean isOnlyCoreApps() throws RemoteException { - return false; - } - - @Override - public boolean isDeviceUpgrading() throws RemoteException { - return false; - } - - @Override - public void setPermissionEnforced(String permission, boolean enforced) throws RemoteException { - - } - - @Override - public boolean isPermissionEnforced(String permission) throws RemoteException { - return false; - } - - @Override - public boolean isStorageLow() throws RemoteException { - return false; - } - - @Override - public boolean setApplicationHiddenSettingAsUser(String packageName, boolean hidden, int userId) - throws RemoteException { - return false; - } - - @Override - public boolean getApplicationHiddenSettingAsUser(String packageName, int userId) - throws RemoteException { - return false; - } - - @Override - public void setSystemAppHiddenUntilInstalled(String packageName, boolean hidden) - throws RemoteException { - - } - - @Override - public boolean setSystemAppInstallState(String packageName, boolean installed, int userId) - throws RemoteException { - return false; - } - - @Override - public IPackageInstaller getPackageInstaller() throws RemoteException { - return null; - } - - @Override - public boolean setBlockUninstallForUser(String packageName, boolean blockUninstall, int userId) - throws RemoteException { - return false; - } - - @Override - public boolean getBlockUninstallForUser(String packageName, int userId) throws RemoteException { - return false; - } - - @Override - public KeySet getKeySetByAlias(String packageName, String alias) throws RemoteException { - return null; - } - - @Override - public KeySet getSigningKeySet(String packageName) throws RemoteException { - return null; - } - - @Override - public boolean isPackageSignedByKeySet(String packageName, KeySet ks) throws RemoteException { - return false; - } - - @Override - public boolean isPackageSignedByKeySetExactly(String packageName, KeySet ks) - throws RemoteException { - return false; - } - - @Override - public void addOnPermissionsChangeListener(IOnPermissionsChangeListener listener) - throws RemoteException { - - } - - @Override - public void removeOnPermissionsChangeListener(IOnPermissionsChangeListener listener) - throws RemoteException { - - } - - @Override - public void grantDefaultPermissionsToEnabledCarrierApps(String[] packageNames, int userId) - throws RemoteException { - - } - - @Override - public void grantDefaultPermissionsToEnabledImsServices(String[] packageNames, int userId) - throws RemoteException { - - } - - @Override - public void grantDefaultPermissionsToEnabledTelephonyDataServices(String[] packageNames, - int userId) throws RemoteException { - - } - - @Override - public void revokeDefaultPermissionsFromDisabledTelephonyDataServices(String[] packageNames, - int userId) throws RemoteException { - - } - - @Override - public void grantDefaultPermissionsToActiveLuiApp(String packageName, int userId) - throws RemoteException { - - } - - @Override - public void revokeDefaultPermissionsFromLuiApps(String[] packageNames, int userId) - throws RemoteException { - - } - - @Override - public boolean isPermissionRevokedByPolicy(String permission, String packageName, int userId) - throws RemoteException { - return false; - } - - @Override - public String getPermissionControllerPackageName() throws RemoteException { - return null; - } - - @Override - public ParceledListSlice getInstantApps(int userId) throws RemoteException { - return null; - } - - @Override - public byte[] getInstantAppCookie(String packageName, int userId) throws RemoteException { - return new byte[0]; - } - - @Override - public boolean setInstantAppCookie(String packageName, byte[] cookie, int userId) - throws RemoteException { - return false; - } - - @Override - public Bitmap getInstantAppIcon(String packageName, int userId) throws RemoteException { - return null; - } - - @Override - public boolean isInstantApp(String packageName, int userId) throws RemoteException { - return false; - } - - @Override - public boolean setRequiredForSystemUser(String packageName, boolean systemUserApp) - throws RemoteException { - return false; - } - - @Override - public void setUpdateAvailable(String packageName, boolean updateAvaialble) - throws RemoteException { - - } - - @Override - public String getServicesSystemSharedLibraryPackageName() throws RemoteException { - return null; - } - - @Override - public String getSharedSystemSharedLibraryPackageName() throws RemoteException { - return null; - } - - @Override - public ChangedPackages getChangedPackages(int sequenceNumber, int userId) - throws RemoteException { - return null; - } - - @Override - public boolean isPackageDeviceAdminOnAnyUser(String packageName) throws RemoteException { - return false; - } - - @Override - public int getInstallReason(String packageName, int userId) throws RemoteException { - return 0; - } - - @Override - public ParceledListSlice getSharedLibraries(String packageName, int flags, int userId) - throws RemoteException { - return null; - } - - @Override - public ParceledListSlice getDeclaredSharedLibraries(String packageName, int flags, int userId) - throws RemoteException { - return null; - } - - @Override - public boolean canRequestPackageInstalls(String packageName, int userId) - throws RemoteException { - return false; - } - - @Override - public void deletePreloadsFileCache() throws RemoteException { - - } - - @Override - public ComponentName getInstantAppResolverComponent() throws RemoteException { - return null; - } - - @Override - public ComponentName getInstantAppResolverSettingsComponent() throws RemoteException { - return null; - } - - @Override - public ComponentName getInstantAppInstallerComponent() throws RemoteException { - return null; - } - - @Override - public String getInstantAppAndroidId(String packageName, int userId) throws RemoteException { - return null; - } - - @Override - public IArtManager getArtManager() throws RemoteException { - return null; - } - - @Override - public void setHarmfulAppWarning(String packageName, CharSequence warning, int userId) - throws RemoteException { - - } - - @Override - public CharSequence getHarmfulAppWarning(String packageName, int userId) - throws RemoteException { - return null; - } - - @Override - public boolean hasSigningCertificate(String packageName, byte[] signingCertificate, int flags) - throws RemoteException { - return false; - } - - @Override - public boolean hasUidSigningCertificate(int uid, byte[] signingCertificate, int flags) - throws RemoteException { - return false; - } - - @Override - public String getSystemTextClassifierPackageName() throws RemoteException { - return null; - } - - @Override - public String getWellbeingPackageName() throws RemoteException { - return null; - } - - @Override - public String getSystemCaptionsServicePackageName() throws RemoteException { - return null; - } - - @Override - public String getAttentionServicePackageName() throws RemoteException { - return null; - } - - public String getIncidentReportApproverPackageName() throws RemoteException { - return null; - } - - @Override - public String getAppPredictionServicePackageName() { - return null; - } - - @Override - public boolean isPackageStateProtected(String packageName, int userId) throws RemoteException { - return false; - } - - @Override - public void sendDeviceCustomizationReadyBroadcast() throws RemoteException { - - } - - @Override - public List<ModuleInfo> getInstalledModules(int flags) throws RemoteException { - return null; - } - - @Override - public ModuleInfo getModuleInfo(String packageName, int flags) throws RemoteException { - return null; - } - - @Override - public int getRuntimePermissionsVersion(int userId) throws RemoteException { - return 0; - } - - @Override - public void setRuntimePermissionsVersion(int version, int userId) throws RemoteException { - - } - - @Override - public IBinder asBinder() { - return null; - } -} diff --git a/services/tests/servicestests/src/com/android/server/backup/utils/AppBackupUtilsTest.java b/services/tests/servicestests/src/com/android/server/backup/utils/AppBackupUtilsTest.java index a92b576e5d0f..a9011756240d 100644 --- a/services/tests/servicestests/src/com/android/server/backup/utils/AppBackupUtilsTest.java +++ b/services/tests/servicestests/src/com/android/server/backup/utils/AppBackupUtilsTest.java @@ -20,6 +20,7 @@ import static com.google.common.truth.Truth.assertThat; import static org.mockito.Mockito.doReturn; import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.when; import android.content.pm.ApplicationInfo; import android.content.pm.PackageInfo; @@ -36,7 +37,6 @@ import androidx.test.filters.SmallTest; import androidx.test.runner.AndroidJUnit4; import com.android.server.backup.UserBackupManagerService; -import com.android.server.backup.testutils.IPackageManagerStub; import org.junit.Before; import org.junit.Test; @@ -54,14 +54,12 @@ public class AppBackupUtilsTest { private static final Signature SIGNATURE_3 = generateSignature((byte) 3); private static final Signature SIGNATURE_4 = generateSignature((byte) 4); - private IPackageManagerStub mPackageManagerStub; private PackageManagerInternal mMockPackageManagerInternal; private int mUserId; @Before public void setUp() throws Exception { - mPackageManagerStub = new IPackageManagerStub(); mMockPackageManagerInternal = mock(PackageManagerInternal.class); mUserId = UserHandle.USER_SYSTEM; @@ -76,7 +74,7 @@ public class AppBackupUtilsTest { applicationInfo.packageName = TEST_PACKAGE_NAME; boolean isEligible = AppBackupUtils.appIsEligibleForBackup(applicationInfo, - mPackageManagerStub, mUserId); + mMockPackageManagerInternal, mUserId); assertThat(isEligible).isFalse(); } @@ -91,7 +89,7 @@ public class AppBackupUtilsTest { applicationInfo.packageName = TEST_PACKAGE_NAME; boolean isEligible = AppBackupUtils.appIsEligibleForBackup(applicationInfo, - mPackageManagerStub, mUserId); + mMockPackageManagerInternal, mUserId); assertThat(isEligible).isFalse(); } @@ -105,7 +103,7 @@ public class AppBackupUtilsTest { applicationInfo.packageName = UserBackupManagerService.SHARED_BACKUP_AGENT_PACKAGE; boolean isEligible = AppBackupUtils.appIsEligibleForBackup(applicationInfo, - mPackageManagerStub, mUserId); + mMockPackageManagerInternal, mUserId); assertThat(isEligible).isFalse(); } @@ -118,12 +116,11 @@ public class AppBackupUtilsTest { applicationInfo.uid = Process.SYSTEM_UID; applicationInfo.backupAgentName = CUSTOM_BACKUP_AGENT_NAME; applicationInfo.packageName = TEST_PACKAGE_NAME; - - IPackageManagerStub.sApplicationEnabledSetting = - PackageManager.COMPONENT_ENABLED_STATE_ENABLED; + when(mMockPackageManagerInternal.getApplicationEnabledState(TEST_PACKAGE_NAME, mUserId)) + .thenReturn(PackageManager.COMPONENT_ENABLED_STATE_ENABLED); boolean isEligible = AppBackupUtils.appIsEligibleForBackup(applicationInfo, - mPackageManagerStub, mUserId); + mMockPackageManagerInternal, mUserId); assertThat(isEligible).isTrue(); } @@ -136,12 +133,11 @@ public class AppBackupUtilsTest { applicationInfo.uid = Process.FIRST_APPLICATION_UID; applicationInfo.backupAgentName = null; applicationInfo.packageName = TEST_PACKAGE_NAME; - - IPackageManagerStub.sApplicationEnabledSetting = - PackageManager.COMPONENT_ENABLED_STATE_ENABLED; + when(mMockPackageManagerInternal.getApplicationEnabledState(TEST_PACKAGE_NAME, mUserId)) + .thenReturn(PackageManager.COMPONENT_ENABLED_STATE_ENABLED); boolean isEligible = AppBackupUtils.appIsEligibleForBackup(applicationInfo, - mPackageManagerStub, mUserId); + mMockPackageManagerInternal, mUserId); assertThat(isEligible).isTrue(); } @@ -154,12 +150,11 @@ public class AppBackupUtilsTest { applicationInfo.uid = Process.FIRST_APPLICATION_UID; applicationInfo.backupAgentName = CUSTOM_BACKUP_AGENT_NAME; applicationInfo.packageName = TEST_PACKAGE_NAME; - - IPackageManagerStub.sApplicationEnabledSetting = - PackageManager.COMPONENT_ENABLED_STATE_ENABLED; + when(mMockPackageManagerInternal.getApplicationEnabledState(TEST_PACKAGE_NAME, mUserId)) + .thenReturn(PackageManager.COMPONENT_ENABLED_STATE_ENABLED); boolean isEligible = AppBackupUtils.appIsEligibleForBackup(applicationInfo, - mPackageManagerStub, mUserId); + mMockPackageManagerInternal, mUserId); assertThat(isEligible).isTrue(); } @@ -172,12 +167,11 @@ public class AppBackupUtilsTest { applicationInfo.uid = Process.SYSTEM_UID; applicationInfo.backupAgentName = CUSTOM_BACKUP_AGENT_NAME; applicationInfo.packageName = TEST_PACKAGE_NAME; - - IPackageManagerStub.sApplicationEnabledSetting = - PackageManager.COMPONENT_ENABLED_STATE_DISABLED; + when(mMockPackageManagerInternal.getApplicationEnabledState(TEST_PACKAGE_NAME, mUserId)) + .thenReturn(PackageManager.COMPONENT_ENABLED_STATE_DISABLED); boolean isEligible = AppBackupUtils.appIsEligibleForBackup(applicationInfo, - mPackageManagerStub, mUserId); + mMockPackageManagerInternal, mUserId); assertThat(isEligible).isFalse(); } @@ -190,12 +184,11 @@ public class AppBackupUtilsTest { applicationInfo.uid = Process.FIRST_APPLICATION_UID; applicationInfo.backupAgentName = null; applicationInfo.packageName = TEST_PACKAGE_NAME; - - IPackageManagerStub.sApplicationEnabledSetting = - PackageManager.COMPONENT_ENABLED_STATE_DISABLED; + when(mMockPackageManagerInternal.getApplicationEnabledState(TEST_PACKAGE_NAME, mUserId)) + .thenReturn(PackageManager.COMPONENT_ENABLED_STATE_DISABLED); boolean isEligible = AppBackupUtils.appIsEligibleForBackup(applicationInfo, - mPackageManagerStub, mUserId); + mMockPackageManagerInternal, mUserId); assertThat(isEligible).isFalse(); } @@ -208,12 +201,11 @@ public class AppBackupUtilsTest { applicationInfo.uid = Process.FIRST_APPLICATION_UID; applicationInfo.backupAgentName = CUSTOM_BACKUP_AGENT_NAME; applicationInfo.packageName = TEST_PACKAGE_NAME; - - IPackageManagerStub.sApplicationEnabledSetting = - PackageManager.COMPONENT_ENABLED_STATE_DISABLED; + when(mMockPackageManagerInternal.getApplicationEnabledState(TEST_PACKAGE_NAME, mUserId)) + .thenReturn(PackageManager.COMPONENT_ENABLED_STATE_DISABLED); boolean isEligible = AppBackupUtils.appIsEligibleForBackup(applicationInfo, - mPackageManagerStub, mUserId); + mMockPackageManagerInternal, mUserId); assertThat(isEligible).isFalse(); } @@ -226,12 +218,11 @@ public class AppBackupUtilsTest { applicationInfo.backupAgentName = CUSTOM_BACKUP_AGENT_NAME; applicationInfo.packageName = TEST_PACKAGE_NAME; applicationInfo.enabled = true; + when(mMockPackageManagerInternal.getApplicationEnabledState(TEST_PACKAGE_NAME, mUserId)) + .thenReturn(PackageManager.COMPONENT_ENABLED_STATE_DEFAULT); - IPackageManagerStub.sApplicationEnabledSetting = - PackageManager.COMPONENT_ENABLED_STATE_DEFAULT; - - boolean isDisabled = AppBackupUtils.appIsDisabled(applicationInfo, mPackageManagerStub, - mUserId); + boolean isDisabled = + AppBackupUtils.appIsDisabled(applicationInfo, mMockPackageManagerInternal, mUserId); assertThat(isDisabled).isFalse(); } @@ -244,12 +235,12 @@ public class AppBackupUtilsTest { applicationInfo.backupAgentName = CUSTOM_BACKUP_AGENT_NAME; applicationInfo.packageName = TEST_PACKAGE_NAME; applicationInfo.enabled = false; + when(mMockPackageManagerInternal.getApplicationEnabledState(TEST_PACKAGE_NAME, mUserId)) + .thenReturn(PackageManager.COMPONENT_ENABLED_STATE_DEFAULT); - IPackageManagerStub.sApplicationEnabledSetting = - PackageManager.COMPONENT_ENABLED_STATE_DEFAULT; - boolean isDisabled = AppBackupUtils.appIsDisabled(applicationInfo, mPackageManagerStub, - mUserId); + boolean isDisabled = + AppBackupUtils.appIsDisabled(applicationInfo, mMockPackageManagerInternal, mUserId); assertThat(isDisabled).isTrue(); } @@ -261,12 +252,12 @@ public class AppBackupUtilsTest { applicationInfo.uid = Process.FIRST_APPLICATION_UID; applicationInfo.backupAgentName = CUSTOM_BACKUP_AGENT_NAME; applicationInfo.packageName = TEST_PACKAGE_NAME; + when(mMockPackageManagerInternal.getApplicationEnabledState(TEST_PACKAGE_NAME, mUserId)) + .thenReturn(PackageManager.COMPONENT_ENABLED_STATE_ENABLED); - IPackageManagerStub.sApplicationEnabledSetting = - PackageManager.COMPONENT_ENABLED_STATE_ENABLED; - boolean isDisabled = AppBackupUtils.appIsDisabled(applicationInfo, mPackageManagerStub, - mUserId); + boolean isDisabled = + AppBackupUtils.appIsDisabled(applicationInfo, mMockPackageManagerInternal, mUserId); assertThat(isDisabled).isFalse(); } @@ -278,12 +269,12 @@ public class AppBackupUtilsTest { applicationInfo.uid = Process.FIRST_APPLICATION_UID; applicationInfo.backupAgentName = CUSTOM_BACKUP_AGENT_NAME; applicationInfo.packageName = TEST_PACKAGE_NAME; + when(mMockPackageManagerInternal.getApplicationEnabledState(TEST_PACKAGE_NAME, mUserId)) + .thenReturn(PackageManager.COMPONENT_ENABLED_STATE_DISABLED); - IPackageManagerStub.sApplicationEnabledSetting = - PackageManager.COMPONENT_ENABLED_STATE_DISABLED; - boolean isDisabled = AppBackupUtils.appIsDisabled(applicationInfo, mPackageManagerStub, - mUserId); + boolean isDisabled = + AppBackupUtils.appIsDisabled(applicationInfo, mMockPackageManagerInternal, mUserId); assertThat(isDisabled).isTrue(); } @@ -295,12 +286,11 @@ public class AppBackupUtilsTest { applicationInfo.uid = Process.FIRST_APPLICATION_UID; applicationInfo.backupAgentName = CUSTOM_BACKUP_AGENT_NAME; applicationInfo.packageName = TEST_PACKAGE_NAME; + when(mMockPackageManagerInternal.getApplicationEnabledState(TEST_PACKAGE_NAME, mUserId)) + .thenReturn(PackageManager.COMPONENT_ENABLED_STATE_DISABLED_USER); - IPackageManagerStub.sApplicationEnabledSetting = - PackageManager.COMPONENT_ENABLED_STATE_DISABLED_USER; - - boolean isDisabled = AppBackupUtils.appIsDisabled(applicationInfo, mPackageManagerStub, - mUserId); + boolean isDisabled = + AppBackupUtils.appIsDisabled(applicationInfo, mMockPackageManagerInternal, mUserId); assertThat(isDisabled).isTrue(); } @@ -312,12 +302,11 @@ public class AppBackupUtilsTest { applicationInfo.uid = Process.FIRST_APPLICATION_UID; applicationInfo.backupAgentName = CUSTOM_BACKUP_AGENT_NAME; applicationInfo.packageName = TEST_PACKAGE_NAME; + when(mMockPackageManagerInternal.getApplicationEnabledState(TEST_PACKAGE_NAME, mUserId)) + .thenReturn(PackageManager.COMPONENT_ENABLED_STATE_DISABLED_UNTIL_USED); - IPackageManagerStub.sApplicationEnabledSetting = - PackageManager.COMPONENT_ENABLED_STATE_DISABLED_UNTIL_USED; - - boolean isDisabled = AppBackupUtils.appIsDisabled(applicationInfo, mPackageManagerStub, - mUserId); + boolean isDisabled = + AppBackupUtils.appIsDisabled(applicationInfo, mMockPackageManagerInternal, mUserId); assertThat(isDisabled).isTrue(); } 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 355ff63d18f7..2d101dd87a0f 100644 --- a/services/tests/uiservicestests/src/com/android/server/notification/NotificationManagerServiceTest.java +++ b/services/tests/uiservicestests/src/com/android/server/notification/NotificationManagerServiceTest.java @@ -349,6 +349,7 @@ public class NotificationManagerServiceTest extends UiServiceTestCase { when(mUgmInternal.newUriPermissionOwner(anyString())).thenReturn(mPermOwner); when(mPackageManager.getPackagesForUid(mUid)).thenReturn(new String[]{PKG}); when(mPackageManagerClient.getPackagesForUid(anyInt())).thenReturn(new String[]{PKG}); + mContext.addMockSystemService(AppOpsManager.class, mock(AppOpsManager.class)); // write to a test file; the system file isn't readable from tests mFile = new File(mContext.getCacheDir(), "test.xml"); diff --git a/services/tests/uiservicestests/src/com/android/server/notification/RoleObserverTest.java b/services/tests/uiservicestests/src/com/android/server/notification/RoleObserverTest.java index 91d3e5e4d7f6..7e3d4b47bbdf 100644 --- a/services/tests/uiservicestests/src/com/android/server/notification/RoleObserverTest.java +++ b/services/tests/uiservicestests/src/com/android/server/notification/RoleObserverTest.java @@ -206,6 +206,7 @@ public class RoleObserverTest extends UiServiceTestCase { LocalServices.removeServiceForTest(WindowManagerInternal.class); LocalServices.addService(WindowManagerInternal.class, mock(WindowManagerInternal.class)); + mContext.addMockSystemService(AppOpsManager.class, mock(AppOpsManager.class)); mUsers = new ArrayList<>(); mUsers.add(new UserInfo(0, "system", 0)); diff --git a/services/tests/uiservicestests/src/com/android/server/notification/ZenModeHelperTest.java b/services/tests/uiservicestests/src/com/android/server/notification/ZenModeHelperTest.java index 08d83333e91b..89364500fd80 100644 --- a/services/tests/uiservicestests/src/com/android/server/notification/ZenModeHelperTest.java +++ b/services/tests/uiservicestests/src/com/android/server/notification/ZenModeHelperTest.java @@ -110,6 +110,7 @@ public class ZenModeHelperTest extends UiServiceTestCase { private ZenModeHelper mZenModeHelperSpy; private Context mContext; private ContentResolver mContentResolver; + @Mock AppOpsManager mAppOps; @Before public void setUp() { @@ -127,6 +128,7 @@ public class ZenModeHelperTest extends UiServiceTestCase { e.toString()); } + when(mContext.getSystemService(AppOpsManager.class)).thenReturn(mAppOps); when(mContext.getSystemService(NotificationManager.class)).thenReturn(mNotificationManager); mConditionProviders = new ConditionProviders(mContext, new UserProfiles(), AppGlobals.getPackageManager()); @@ -219,10 +221,10 @@ public class ZenModeHelperTest extends UiServiceTestCase { Policy.PRIORITY_CATEGORY_MEDIA, 0, 0, 0, 0); mZenModeHelperSpy.applyRestrictions(); - doNothing().when(mZenModeHelperSpy).applyRestrictions(anyBoolean(), anyInt()); - verify(mZenModeHelperSpy, atLeastOnce()).applyRestrictions(false, + doNothing().when(mZenModeHelperSpy).applyRestrictions(eq(false), anyBoolean(), anyInt()); + verify(mZenModeHelperSpy, atLeastOnce()).applyRestrictions(false, false, AudioAttributes.USAGE_ALARM); - verify(mZenModeHelperSpy, atLeastOnce()).applyRestrictions(false, + verify(mZenModeHelperSpy, atLeastOnce()).applyRestrictions(false, false, AudioAttributes.USAGE_MEDIA); } @@ -233,9 +235,9 @@ public class ZenModeHelperTest extends UiServiceTestCase { Policy.PRIORITY_CATEGORY_MEDIA, 0, 0, 0, 0); mZenModeHelperSpy.applyRestrictions(); - verify(mZenModeHelperSpy, atLeastOnce()).applyRestrictions(false, + verify(mZenModeHelperSpy, atLeastOnce()).applyRestrictions(true, false, AudioAttributes.USAGE_ALARM); - verify(mZenModeHelperSpy, atLeastOnce()).applyRestrictions(false, + verify(mZenModeHelperSpy, atLeastOnce()).applyRestrictions(true, false, AudioAttributes.USAGE_MEDIA); } @@ -244,13 +246,13 @@ public class ZenModeHelperTest extends UiServiceTestCase { mZenModeHelperSpy.mZenMode = Settings.Global.ZEN_MODE_IMPORTANT_INTERRUPTIONS; mZenModeHelperSpy.mConsolidatedPolicy = new Policy(0, 0, 0, 0, 0); mZenModeHelperSpy.applyRestrictions(); - verify(mZenModeHelperSpy, atLeastOnce()).applyRestrictions(true, + verify(mZenModeHelperSpy, atLeastOnce()).applyRestrictions(true, true, AudioAttributes.USAGE_ALARM); // Media is a catch-all that includes games - verify(mZenModeHelperSpy, atLeastOnce()).applyRestrictions(true, + verify(mZenModeHelperSpy, atLeastOnce()).applyRestrictions(true, true, AudioAttributes.USAGE_MEDIA); - verify(mZenModeHelperSpy, atLeastOnce()).applyRestrictions(true, + verify(mZenModeHelperSpy, atLeastOnce()).applyRestrictions(true, true, AudioAttributes.USAGE_GAME); } @@ -262,17 +264,17 @@ public class ZenModeHelperTest extends UiServiceTestCase { mZenModeHelperSpy.applyRestrictions(); // Total silence will silence alarms, media and system noises (but not vibrations) - verify(mZenModeHelperSpy, atLeastOnce()).applyRestrictions(true, + verify(mZenModeHelperSpy, atLeastOnce()).applyRestrictions(false, true, AudioAttributes.USAGE_ALARM); - verify(mZenModeHelperSpy, atLeastOnce()).applyRestrictions(true, + verify(mZenModeHelperSpy, atLeastOnce()).applyRestrictions(false, true, AudioAttributes.USAGE_MEDIA); - verify(mZenModeHelperSpy, atLeastOnce()).applyRestrictions(true, + verify(mZenModeHelperSpy, atLeastOnce()).applyRestrictions(false, true, AudioAttributes.USAGE_GAME); - verify(mZenModeHelperSpy, atLeastOnce()).applyRestrictions(true, + verify(mZenModeHelperSpy, atLeastOnce()).applyRestrictions(false, true, AudioAttributes.USAGE_ASSISTANCE_SONIFICATION, AppOpsManager.OP_PLAY_AUDIO); - verify(mZenModeHelperSpy, atLeastOnce()).applyRestrictions(false, + verify(mZenModeHelperSpy, atLeastOnce()).applyRestrictions(false, false, AudioAttributes.USAGE_ASSISTANCE_SONIFICATION, AppOpsManager.OP_VIBRATE); - verify(mZenModeHelperSpy, atLeastOnce()).applyRestrictions(true, + verify(mZenModeHelperSpy, atLeastOnce()).applyRestrictions(false, true, AudioAttributes.USAGE_UNKNOWN); } @@ -283,19 +285,19 @@ public class ZenModeHelperTest extends UiServiceTestCase { mZenModeHelperSpy.applyRestrictions(); // Alarms only mode will not silence alarms - verify(mZenModeHelperSpy, atLeastOnce()).applyRestrictions(false, + verify(mZenModeHelperSpy, atLeastOnce()).applyRestrictions(false, false, AudioAttributes.USAGE_ALARM); // Alarms only mode will not silence media - verify(mZenModeHelperSpy, atLeastOnce()).applyRestrictions(false, + verify(mZenModeHelperSpy, atLeastOnce()).applyRestrictions(false, false, AudioAttributes.USAGE_MEDIA); - verify(mZenModeHelperSpy, atLeastOnce()).applyRestrictions(false, + verify(mZenModeHelperSpy, atLeastOnce()).applyRestrictions(false, false, AudioAttributes.USAGE_GAME); - verify(mZenModeHelperSpy, atLeastOnce()).applyRestrictions(false, + verify(mZenModeHelperSpy, atLeastOnce()).applyRestrictions(false, false, AudioAttributes.USAGE_UNKNOWN); // Alarms only will silence system noises (but not vibrations) - verify(mZenModeHelperSpy, atLeastOnce()).applyRestrictions(true, + verify(mZenModeHelperSpy, atLeastOnce()).applyRestrictions(false, true, AudioAttributes.USAGE_ASSISTANCE_SONIFICATION, AppOpsManager.OP_PLAY_AUDIO); } @@ -306,9 +308,9 @@ public class ZenModeHelperTest extends UiServiceTestCase { mZenModeHelperSpy.applyRestrictions(); // Alarms only mode will silence calls despite priority-mode config - verify(mZenModeHelperSpy, atLeastOnce()).applyRestrictions(true, + verify(mZenModeHelperSpy, atLeastOnce()).applyRestrictions(false, true, AudioAttributes.USAGE_NOTIFICATION_RINGTONE); - verify(mZenModeHelperSpy, atLeastOnce()).applyRestrictions(true, + verify(mZenModeHelperSpy, atLeastOnce()).applyRestrictions(false, true, AudioAttributes.USAGE_NOTIFICATION_COMMUNICATION_REQUEST); } @@ -319,7 +321,7 @@ public class ZenModeHelperTest extends UiServiceTestCase { mZenModeHelperSpy.mConsolidatedPolicy = new Policy(0, 0, 0, 0, 0); mZenModeHelperSpy.applyRestrictions(); - verify(mZenModeHelperSpy, atLeastOnce()).applyRestrictions(false, + verify(mZenModeHelperSpy, atLeastOnce()).applyRestrictions(false, false, AudioAttributes.USAGE_ALARM); } @@ -334,19 +336,64 @@ public class ZenModeHelperTest extends UiServiceTestCase { for (int usage : AudioAttributes.SDK_USAGES) { if (usage == AudioAttributes.USAGE_ASSISTANCE_SONIFICATION) { // only mute audio, not vibrations - verify(mZenModeHelperSpy, atLeastOnce()).applyRestrictions(true, usage, + verify(mZenModeHelperSpy, atLeastOnce()).applyRestrictions(true, true, usage, AppOpsManager.OP_PLAY_AUDIO); - verify(mZenModeHelperSpy, atLeastOnce()).applyRestrictions(false, usage, + verify(mZenModeHelperSpy, atLeastOnce()).applyRestrictions(true, false, usage, AppOpsManager.OP_VIBRATE); } else { boolean shouldMute = AudioAttributes.SUPPRESSIBLE_USAGES.get(usage) != AudioAttributes.SUPPRESSIBLE_NEVER; - verify(mZenModeHelperSpy, atLeastOnce()).applyRestrictions(shouldMute, usage); + verify(mZenModeHelperSpy, atLeastOnce()).applyRestrictions(true, shouldMute, usage); } } } @Test + public void testApplyRestrictions_whitelist_priorityOnlyMode() { + mZenModeHelperSpy.setPriorityOnlyDndExemptPackages(new String[] {PKG_O}); + mZenModeHelperSpy.mZenMode = Global.ZEN_MODE_IMPORTANT_INTERRUPTIONS; + mZenModeHelperSpy.mConsolidatedPolicy = new Policy(0, 0, 0, 0, 0); + mZenModeHelperSpy.applyRestrictions(); + + for (int usage : AudioAttributes.SDK_USAGES) { + verify(mAppOps).setRestriction( + eq(AppOpsManager.OP_PLAY_AUDIO), eq(usage), anyInt(), eq(new String[]{PKG_O})); + verify(mAppOps).setRestriction( + eq(AppOpsManager.OP_VIBRATE), eq(usage), anyInt(), eq(new String[]{PKG_O})); + } + } + + @Test + public void testApplyRestrictions_whitelist_alarmsOnlyMode() { + mZenModeHelperSpy.setPriorityOnlyDndExemptPackages(new String[] {PKG_O}); + mZenModeHelperSpy.mZenMode = Global.ZEN_MODE_ALARMS; + mZenModeHelperSpy.mConsolidatedPolicy = new Policy(0, 0, 0, 0, 0); + mZenModeHelperSpy.applyRestrictions(); + + for (int usage : AudioAttributes.SDK_USAGES) { + verify(mAppOps).setRestriction( + eq(AppOpsManager.OP_PLAY_AUDIO), eq(usage), anyInt(), eq(null)); + verify(mAppOps).setRestriction( + eq(AppOpsManager.OP_VIBRATE), eq(usage), anyInt(), eq(null)); + } + } + + @Test + public void testApplyRestrictions_whitelist_totalSilenceMode() { + mZenModeHelperSpy.setPriorityOnlyDndExemptPackages(new String[] {PKG_O}); + mZenModeHelperSpy.mZenMode = Global.ZEN_MODE_NO_INTERRUPTIONS; + mZenModeHelperSpy.mConsolidatedPolicy = new Policy(0, 0, 0, 0, 0); + mZenModeHelperSpy.applyRestrictions(); + + for (int usage : AudioAttributes.SDK_USAGES) { + verify(mAppOps).setRestriction( + eq(AppOpsManager.OP_PLAY_AUDIO), eq(usage), anyInt(), eq(null)); + verify(mAppOps).setRestriction( + eq(AppOpsManager.OP_VIBRATE), eq(usage), anyInt(), eq(null)); + } + } + + @Test public void testZenUpgradeNotification() { // shows zen upgrade notification if stored settings says to shows, // zen has not been updated, boot is completed diff --git a/telephony/java/android/telephony/SubscriptionManager.java b/telephony/java/android/telephony/SubscriptionManager.java index 0808adf26aef..d4f98743089e 100644 --- a/telephony/java/android/telephony/SubscriptionManager.java +++ b/telephony/java/android/telephony/SubscriptionManager.java @@ -294,6 +294,19 @@ public class SubscriptionManager { public static final String SUBSCRIPTION_TYPE = "subscription_type"; /** + * TelephonyProvider column name white_listed_apn_data. + * It's a bitmask of APN types that will be allowed on this subscription even if it's metered + * and mobile data is turned off by the user. + * <P>Type: INTEGER (int)</P> For example, if TYPE_MMS is is true, Telephony will allow MMS + * data connection to setup even if MMS is metered and mobile_data is turned off on that + * subscription. + * + * Default value is 0. + */ + /** @hide */ + public static final String WHITE_LISTED_APN_DATA = "white_listed_apn_data"; + + /** * This constant is to designate a subscription as a Local-SIM Subscription. * <p> A Local-SIM can be a physical SIM inserted into a sim-slot in the device, or eSIM on the * device. @@ -3086,6 +3099,31 @@ public class SubscriptionManager { return subId; } + /** + * Set whether a subscription always allows MMS connection. If true, MMS network + * request will be accepted by telephony even if user turns "mobile data" off + * on this subscription. + * + * @param subId which subscription it's setting to. + * @param alwaysAllow whether Mms data is always allowed. + * @return whether operation is successful. + * + * @hide + */ + public boolean setAlwaysAllowMmsData(int subId, boolean alwaysAllow) { + try { + ISub iSub = ISub.Stub.asInterface(ServiceManager.getService("isub")); + if (iSub != null) { + return iSub.setAlwaysAllowMmsData(subId, alwaysAllow); + } + } catch (RemoteException ex) { + if (!isSystemProcess()) { + ex.rethrowAsRuntimeException(); + } + } + return false; + } + private interface CallISubMethodHelper { int callMethod(ISub iSub) throws RemoteException; } diff --git a/telephony/java/android/telephony/TelephonyManager.java b/telephony/java/android/telephony/TelephonyManager.java index 2bd4f8f336fd..18b9fbbf414e 100644 --- a/telephony/java/android/telephony/TelephonyManager.java +++ b/telephony/java/android/telephony/TelephonyManager.java @@ -10948,4 +10948,33 @@ public class TelephonyManager { } return new Pair<Integer, Integer>(-1, -1); } + + + /** + * Return whether MMS data is enabled. This will tell if framework will accept a MMS network + * request on a subId. + * + * Mms is enabled if: + * 1) user data is turned on, or + * 2) MMS is un-metered for this subscription, or + * 3) alwaysAllowMms setting {@link SubscriptionManager#setAlwaysAllowMmsData} is turned on. + * + * @return whether MMS data is allowed. + * + * @hide + */ + public boolean isMmsDataEnabled() { + String pkgForDebug = mContext != null ? mContext.getOpPackageName() : "<unknown>"; + try { + ITelephony service = getITelephony(); + if (service != null) { + return service.isMmsDataEnabled(getSubId(), pkgForDebug); + } + } catch (RemoteException ex) { + if (!isSystemProcess()) { + ex.rethrowAsRuntimeException(); + } + } + return false; + } } diff --git a/telephony/java/com/android/internal/telephony/DctConstants.java b/telephony/java/com/android/internal/telephony/DctConstants.java index 6e8d038eda4f..bb5c251b69e1 100644 --- a/telephony/java/com/android/internal/telephony/DctConstants.java +++ b/telephony/java/com/android/internal/telephony/DctConstants.java @@ -94,6 +94,7 @@ public class DctConstants { public static final int EVENT_ROAMING_SETTING_CHANGE = BASE + 48; public static final int EVENT_DATA_SERVICE_BINDING_CHANGED = BASE + 49; public static final int EVENT_DEVICE_PROVISIONED_CHANGE = BASE + 50; + public static final int EVENT_APN_WHITE_LIST_CHANGE = BASE + 51; /***** Constants *****/ diff --git a/telephony/java/com/android/internal/telephony/ISub.aidl b/telephony/java/com/android/internal/telephony/ISub.aidl index cfba0529e664..b081228d9616 100755 --- a/telephony/java/com/android/internal/telephony/ISub.aidl +++ b/telephony/java/com/android/internal/telephony/ISub.aidl @@ -279,4 +279,6 @@ interface ISub { int getSimStateForSlotIndex(int slotIndex); boolean isActiveSubId(int subId, String callingPackage); + + boolean setAlwaysAllowMmsData(int subId, boolean alwaysAllow); } diff --git a/telephony/java/com/android/internal/telephony/ITelephony.aidl b/telephony/java/com/android/internal/telephony/ITelephony.aidl index ecbbd6a6c786..536c514356cb 100644 --- a/telephony/java/com/android/internal/telephony/ITelephony.aidl +++ b/telephony/java/com/android/internal/telephony/ITelephony.aidl @@ -1966,4 +1966,6 @@ interface ITelephony { int getRadioHalVersion(); boolean isModemEnabledForSlot(int slotIndex, String callingPackage); + + boolean isMmsDataEnabled(int subId, String callingPackage); } diff --git a/telephony/java/com/android/internal/telephony/TelephonyPermissions.java b/telephony/java/com/android/internal/telephony/TelephonyPermissions.java index 80fb58d45078..12b20efcb0b3 100644 --- a/telephony/java/com/android/internal/telephony/TelephonyPermissions.java +++ b/telephony/java/com/android/internal/telephony/TelephonyPermissions.java @@ -344,10 +344,22 @@ public final class TelephonyPermissions { return true; } // if the calling package is null then return now as there's no way to perform the - // DevicePolicyManager device / profile owner checks. + // DevicePolicyManager device / profile owner and AppOp checks if (callingPackage == null) { return false; } + // Allow access to an app that has been granted the READ_DEVICE_IDENTIFIERS app op. + long token = Binder.clearCallingIdentity(); + AppOpsManager appOpsManager = (AppOpsManager) context.getSystemService( + Context.APP_OPS_SERVICE); + try { + if (appOpsManager.noteOpNoThrow(AppOpsManager.OPSTR_READ_DEVICE_IDENTIFIERS, uid, + callingPackage) == AppOpsManager.MODE_ALLOWED) { + return true; + } + } finally { + Binder.restoreCallingIdentity(token); + } // Allow access to a device / profile owner app. DevicePolicyManager devicePolicyManager = (DevicePolicyManager) context.getSystemService( Context.DEVICE_POLICY_SERVICE); diff --git a/tests/FlickerTests/lib/Android.bp b/tests/FlickerTests/lib/Android.bp index 982fcbadddf7..5d8ed2c205e9 100644 --- a/tests/FlickerTests/lib/Android.bp +++ b/tests/FlickerTests/lib/Android.bp @@ -20,7 +20,7 @@ java_test { srcs: ["src/**/*.java"], static_libs: [ "androidx.test.janktesthelper", - "cts-amwm-util", + "cts-wm-util", "platformprotosnano", "layersprotosnano", "truth-prebuilt", diff --git a/tests/utils/testutils/java/com/android/server/wm/test/filters/FrameworksTestsFilter.java b/tests/utils/testutils/java/com/android/server/wm/test/filters/FrameworksTestsFilter.java index e0d74e0908ee..c9e3404e0f1a 100644 --- a/tests/utils/testutils/java/com/android/server/wm/test/filters/FrameworksTestsFilter.java +++ b/tests/utils/testutils/java/com/android/server/wm/test/filters/FrameworksTestsFilter.java @@ -34,6 +34,8 @@ import com.android.test.filters.SelectTest; public final class FrameworksTestsFilter extends SelectTest { private static final String[] SELECTED_TESTS = { + // Test specifications for FrameworksMockingCoreTests. + "android.app.activity.ActivityThreadClientTest", // Test specifications for FrameworksCoreTests. "android.app.servertransaction.", // all tests under the package. "android.view.DisplayCutoutTest", diff --git a/tools/aapt2/Debug.cpp b/tools/aapt2/Debug.cpp index 98324850f3f5..3da22b4fb9fa 100644 --- a/tools/aapt2/Debug.cpp +++ b/tools/aapt2/Debug.cpp @@ -129,12 +129,20 @@ class ValueBodyPrinter : public ConstValueVisitor { constexpr uint32_t kMask = android::ResTable_map::TYPE_ENUM | android::ResTable_map::TYPE_FLAGS; if (attr->type_mask & kMask) { for (const auto& symbol : attr->symbols) { - printer_->Print(symbol.symbol.name.value().entry); - if (symbol.symbol.id) { - printer_->Print("("); + if (symbol.symbol.name) { + printer_->Print(symbol.symbol.name.value().entry); + + if (symbol.symbol.id) { + printer_->Print("("); + printer_->Print(symbol.symbol.id.value().to_string()); + printer_->Print(")"); + } + } else if (symbol.symbol.id) { printer_->Print(symbol.symbol.id.value().to_string()); - printer_->Print(")"); + } else { + printer_->Print("???"); } + printer_->Println(StringPrintf("=0x%08x", symbol.value)); } } diff --git a/wifi/java/android/net/wifi/WifiEnterpriseConfig.java b/wifi/java/android/net/wifi/WifiEnterpriseConfig.java index 950c6f801461..9d4b837ce2d1 100644 --- a/wifi/java/android/net/wifi/WifiEnterpriseConfig.java +++ b/wifi/java/android/net/wifi/WifiEnterpriseConfig.java @@ -967,6 +967,10 @@ public class WifiEnterpriseConfig implements Parcelable { * from the top-level domain and all the labels in domain_suffix_match shall be included in the * certificate. The certificate may include additional sub-level labels in addition to the * required labels. + * <p>More than one match string can be provided by using semicolons to separate the strings + * (e.g., example.org;example.com). When multiple strings are specified, a match with any one of + * the values is considered a sufficient match for the certificate, i.e., the conditions are + * ORed ogether. * <p>For example, domain_suffix_match=example.com would match test.example.com but would not * match test-example.com. * @param domain The domain value |