diff options
108 files changed, 4512 insertions, 1136 deletions
diff --git a/Android.bp b/Android.bp index 543e65601517..b8d8bc865077 100644 --- a/Android.bp +++ b/Android.bp @@ -520,24 +520,22 @@ gensrcs { name: "framework-javastream-protos", depfile: true, - tool_files: ["tools/genprotos.sh"], tools: [ "aprotoc", "protoc-gen-javastream", "soong_zip", ], - // TODO This should not be needed. If you set a custom OUT_DIR or OUT_DIR_COMMON_BASE you can - // end up with a command that is extremely long, potentially going passed MAX_ARG_STRLEN due to - // the way sbox rewrites the command. See b/70221552. - cmd: "$(location tools/genprotos.sh) " + - " $(location aprotoc) " + - " $(location protoc-gen-javastream) " + - " $(location soong_zip) " + - " $(genDir) " + - " $(depfile) " + - " $(in) " + - " $(out)", + cmd: "mkdir -p $(genDir)/$(in) " + + "&& $(location aprotoc) " + + " --plugin=$(location protoc-gen-javastream) " + + " --dependency_out=$(depfile) " + + " --javastream_out=$(genDir)/$(in) " + + " -Iexternal/protobuf/src " + + " -I . " + + " $(in) " + + "&& $(location soong_zip) -jar -o $(out) -C $(genDir)/$(in) -D $(genDir)/$(in)", + srcs: [ "core/proto/**/*.proto", "libs/incident/**/*.proto", @@ -897,7 +895,10 @@ framework_docs_only_args = " -android -manifest $(location core/res/AndroidManif "-overview $(location core/java/overview.html) " + // Federate Support Library references against local API file. "-federate SupportLib https://developer.android.com " + - "-federationapi SupportLib $(location :current-support-api) " + "-federationapi SupportLib $(location :current-support-api) " + + // Federate Support Library references against local API file. + "-federate AndroidX https://developer.android.com " + + "-federationapi AndroidX $(location :current-androidx-api) " framework_docs_only_libs = [ "voip-common", @@ -1015,6 +1016,7 @@ doc_defaults { "core/res/AndroidManifest.xml", "core/java/overview.html", ":current-support-api", + ":current-androidx-api", ], create_stubs: false, } diff --git a/api/current.txt b/api/current.txt index 80a3f332a4b0..4ba4fbee7b15 100644 --- a/api/current.txt +++ b/api/current.txt @@ -3021,9 +3021,9 @@ package android.accounts { field public final String type; } - public class AccountAuthenticatorActivity extends android.app.Activity { - ctor public AccountAuthenticatorActivity(); - method public final void setAccountAuthenticatorResult(android.os.Bundle); + @Deprecated public class AccountAuthenticatorActivity extends android.app.Activity { + ctor @Deprecated public AccountAuthenticatorActivity(); + method @Deprecated public final void setAccountAuthenticatorResult(android.os.Bundle); } public class AccountAuthenticatorResponse implements android.os.Parcelable { @@ -34548,8 +34548,8 @@ package android.os { } public class Handler { - ctor public Handler(); - ctor public Handler(@Nullable android.os.Handler.Callback); + ctor @Deprecated public Handler(); + ctor @Deprecated public Handler(@Nullable android.os.Handler.Callback); ctor public Handler(@NonNull android.os.Looper); ctor public Handler(@NonNull android.os.Looper, @Nullable android.os.Handler.Callback); method @NonNull public static android.os.Handler createAsync(@NonNull android.os.Looper); @@ -43520,6 +43520,7 @@ package android.telecom { field public static final int LOCAL = 2; // 0x2 field public static final int MISSED = 5; // 0x5 field public static final int OTHER = 9; // 0x9 + field public static final String REASON_EMERGENCY_CALL_PLACED = "REASON_EMERGENCY_CALL_PLACED"; field public static final int REJECTED = 6; // 0x6 field public static final int REMOTE = 3; // 0x3 field public static final int RESTRICTED = 8; // 0x8 @@ -44048,6 +44049,7 @@ package android.telephony { field public static final String KEY_ALLOW_ADD_CALL_DURING_VIDEO_CALL_BOOL = "allow_add_call_during_video_call"; field public static final String KEY_ALLOW_EMERGENCY_NUMBERS_IN_CALL_LOG_BOOL = "allow_emergency_numbers_in_call_log_bool"; field public static final String KEY_ALLOW_EMERGENCY_VIDEO_CALLS_BOOL = "allow_emergency_video_calls_bool"; + field public static final String KEY_ALLOW_HOLD_CALL_DURING_EMERGENCY_BOOL = "allow_hold_call_during_emergency_bool"; field public static final String KEY_ALLOW_LOCAL_DTMF_TONES_BOOL = "allow_local_dtmf_tones_bool"; field public static final String KEY_ALLOW_MERGE_WIFI_CALLS_WHEN_VOWIFI_OFF_BOOL = "allow_merge_wifi_calls_when_vowifi_off_bool"; field public static final String KEY_ALLOW_NON_EMERGENCY_CALLS_IN_ECM_BOOL = "allow_non_emergency_calls_in_ecm_bool"; @@ -44064,7 +44066,7 @@ package android.telephony { field public static final String KEY_CARRIER_DATA_CALL_PERMANENT_FAILURE_STRINGS = "carrier_data_call_permanent_failure_strings"; field public static final String KEY_CARRIER_DEFAULT_WFC_IMS_MODE_INT = "carrier_default_wfc_ims_mode_int"; field public static final String KEY_CARRIER_DEFAULT_WFC_IMS_ROAMING_MODE_INT = "carrier_default_wfc_ims_roaming_mode_int"; - field public static final String KEY_CARRIER_FORCE_DISABLE_ETWS_CMAS_TEST_BOOL = "carrier_force_disable_etws_cmas_test_bool"; + field @Deprecated public static final String KEY_CARRIER_FORCE_DISABLE_ETWS_CMAS_TEST_BOOL = "carrier_force_disable_etws_cmas_test_bool"; field public static final String KEY_CARRIER_IMS_GBA_REQUIRED_BOOL = "carrier_ims_gba_required_bool"; field public static final String KEY_CARRIER_INSTANT_LETTERING_AVAILABLE_BOOL = "carrier_instant_lettering_available_bool"; field public static final String KEY_CARRIER_INSTANT_LETTERING_ENCODING_STRING = "carrier_instant_lettering_encoding_string"; diff --git a/api/system-current.txt b/api/system-current.txt index 867f0209dab8..341dd6faeb6f 100644 --- a/api/system-current.txt +++ b/api/system-current.txt @@ -66,6 +66,7 @@ package android { field public static final String CRYPT_KEEPER = "android.permission.CRYPT_KEEPER"; field public static final String DEVICE_POWER = "android.permission.DEVICE_POWER"; field public static final String DISPATCH_PROVISIONING_MESSAGE = "android.permission.DISPATCH_PROVISIONING_MESSAGE"; + field public static final String ENTER_CAR_MODE_PRIORITIZED = "android.permission.ENTER_CAR_MODE_PRIORITIZED"; field public static final String FORCE_BACK = "android.permission.FORCE_BACK"; field public static final String FORCE_STOP_PACKAGES = "android.permission.FORCE_STOP_PACKAGES"; field public static final String GET_APP_OPS_STATS = "android.permission.GET_APP_OPS_STATS"; @@ -74,6 +75,7 @@ package android { field public static final String GET_TOP_ACTIVITY_INFO = "android.permission.GET_TOP_ACTIVITY_INFO"; field public static final String GRANT_PROFILE_OWNER_DEVICE_IDS_ACCESS = "android.permission.GRANT_PROFILE_OWNER_DEVICE_IDS_ACCESS"; field public static final String GRANT_RUNTIME_PERMISSIONS = "android.permission.GRANT_RUNTIME_PERMISSIONS"; + field public static final String HANDLE_CAR_MODE_CHANGES = "android.permission.HANDLE_CAR_MODE_CHANGES"; field public static final String HARDWARE_TEST = "android.permission.HARDWARE_TEST"; field public static final String HDMI_CEC = "android.permission.HDMI_CEC"; field public static final String HIDE_NON_SYSTEM_OVERLAY_WINDOWS = "android.permission.HIDE_NON_SYSTEM_OVERLAY_WINDOWS"; @@ -602,6 +604,15 @@ package android.app { method public boolean isStatusBarExpansionDisabled(); } + public class UiModeManager { + method @RequiresPermission(android.Manifest.permission.ENTER_CAR_MODE_PRIORITIZED) public void enableCarMode(@IntRange(from=0) int, int); + field public static final String ACTION_ENTER_CAR_MODE_PRIORITIZED = "android.app.action.ENTER_CAR_MODE_PRIORITIZED"; + field public static final String ACTION_EXIT_CAR_MODE_PRIORITIZED = "android.app.action.EXIT_CAR_MODE_PRIORITIZED"; + field public static final int DEFAULT_PRIORITY = 0; // 0x0 + field public static final String EXTRA_CALLING_PACKAGE = "android.app.extra.CALLING_PACKAGE"; + field public static final String EXTRA_PRIORITY = "android.app.extra.PRIORITY"; + } + public final class Vr2dDisplayProperties implements android.os.Parcelable { ctor public Vr2dDisplayProperties(int, int, int); method public int describeContents(); @@ -1239,7 +1250,9 @@ package android.bluetooth { public final class BluetoothAdapter { method @RequiresPermission(android.Manifest.permission.BLUETOOTH_PRIVILEGED) public boolean addOnMetadataChangedListener(@NonNull android.bluetooth.BluetoothDevice, @NonNull java.util.concurrent.Executor, @NonNull android.bluetooth.BluetoothAdapter.OnMetadataChangedListener); + method @RequiresPermission(android.Manifest.permission.BLUETOOTH_ADMIN) public boolean connectAllEnabledProfiles(@NonNull android.bluetooth.BluetoothDevice); method public boolean disableBLE(); + method @RequiresPermission(android.Manifest.permission.BLUETOOTH_ADMIN) public boolean disconnectAllEnabledProfiles(@NonNull android.bluetooth.BluetoothDevice); method public boolean enableBLE(); method @RequiresPermission(android.Manifest.permission.BLUETOOTH_ADMIN) public boolean enableNoAutoConnect(); method @RequiresPermission(android.Manifest.permission.BLUETOOTH_PRIVILEGED) public boolean factoryReset(); @@ -5669,14 +5682,6 @@ package android.os.storage { } -package android.os.telephony { - - public class TelephonyRegistryManager { - method public void notifyCarrierNetworkChange(boolean); - } - -} - package android.permission { public final class PermissionControllerManager { @@ -7320,6 +7325,21 @@ package android.telephony { method @NonNull public android.telephony.CarrierRestrictionRules.Builder setMultiSimPolicy(int); } + public class CbGeoUtils { + } + + public static interface CbGeoUtils.Geometry { + method public boolean contains(@NonNull android.telephony.CbGeoUtils.LatLng); + } + + public static class CbGeoUtils.LatLng { + ctor public CbGeoUtils.LatLng(double, double); + method public double distance(@NonNull android.telephony.CbGeoUtils.LatLng); + method @NonNull public android.telephony.CbGeoUtils.LatLng subtract(@NonNull android.telephony.CbGeoUtils.LatLng); + field public final double lat; + field public final double lng; + } + public abstract class CellBroadcastService extends android.app.Service { ctor public CellBroadcastService(); method @CallSuper @NonNull public android.os.IBinder onBind(@Nullable android.content.Intent); @@ -7743,6 +7763,7 @@ package android.telephony { field public static final int NUMBER_UNREACHABLE = 8; // 0x8 field public static final int OTASP_PROVISIONING_IN_PROCESS = 76; // 0x4c field public static final int OUTGOING_CANCELED = 44; // 0x2c + field public static final int OUTGOING_EMERGENCY_CALL_PLACED = 80; // 0x50 field public static final int OUTGOING_FAILURE = 43; // 0x2b field public static final int OUT_OF_NETWORK = 11; // 0xb field public static final int OUT_OF_SERVICE = 18; // 0x12 @@ -8132,6 +8153,7 @@ package android.telephony { method public int getGeographicalScope(); method @Nullable public String getLanguageCode(); method @NonNull public android.telephony.SmsCbLocation getLocation(); + method public int getMaximumWaitingDuration(); method @Nullable public String getMessageBody(); method public int getMessageFormat(); method public int getMessagePriority(); @@ -8283,7 +8305,7 @@ package android.telephony { method @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) public boolean isPotentialEmergencyNumber(@NonNull String); method @Deprecated @RequiresPermission(anyOf={android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE, android.Manifest.permission.READ_PHONE_STATE}) public boolean isRadioOn(); method @Deprecated @RequiresPermission(anyOf={android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE, android.Manifest.permission.READ_PHONE_STATE}) public boolean isRinging(); - method @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) public boolean isTetherApnRequired(); + method @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) public boolean isTetheringApnRequired(); method @Deprecated @RequiresPermission(anyOf={android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE, android.Manifest.permission.READ_PHONE_STATE}) public boolean isVideoCallingEnabled(); method @Deprecated @RequiresPermission(android.Manifest.permission.READ_PHONE_STATE) public boolean isVisualVoicemailEnabled(android.telecom.PhoneAccountHandle); method public boolean needsOtaServiceProvisioning(); @@ -8367,6 +8389,15 @@ package android.telephony { field public static final int SRVCC_STATE_HANDOVER_STARTED = 0; // 0x0 } + public class TelephonyRegistryManager { + method public void addOnOpportunisticSubscriptionsChangedListener(@NonNull android.telephony.SubscriptionManager.OnOpportunisticSubscriptionsChangedListener, @NonNull java.util.concurrent.Executor); + method public void addOnSubscriptionsChangedListener(@NonNull android.telephony.SubscriptionManager.OnSubscriptionsChangedListener, @NonNull java.util.concurrent.Executor); + method @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public void notifyCallStateChangedForAllSubscriptions(int, @Nullable String); + method public void notifyCarrierNetworkChange(boolean); + method public void removeOnOpportunisticSubscriptionsChangedListener(@NonNull android.telephony.SubscriptionManager.OnOpportunisticSubscriptionsChangedListener); + method public void removeOnSubscriptionsChangedListener(@NonNull android.telephony.SubscriptionManager.OnSubscriptionsChangedListener); + } + public final class UiccAccessRule implements android.os.Parcelable { ctor public UiccAccessRule(byte[], @Nullable String, long); method public int describeContents(); @@ -8899,7 +8930,7 @@ package android.telephony.ims { public class ImsMmTelManager implements android.telephony.ims.RegistrationManager { method @NonNull public static android.telephony.ims.ImsMmTelManager createForSubscriptionId(int); - method @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) public void getFeatureState(@NonNull java.util.function.Consumer<java.lang.Integer>, @NonNull java.util.concurrent.Executor) throws android.telephony.ims.ImsException; + method @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) public void getFeatureState(@NonNull java.util.concurrent.Executor, @NonNull java.util.function.Consumer<java.lang.Integer>) throws android.telephony.ims.ImsException; method @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) public void getRegistrationState(@NonNull java.util.concurrent.Executor, @NonNull java.util.function.Consumer<java.lang.Integer>); method @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) public void getRegistrationTransportType(@NonNull java.util.concurrent.Executor, @NonNull java.util.function.Consumer<java.lang.Integer>); method @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) public int getVoWiFiModeSetting(); diff --git a/api/test-current.txt b/api/test-current.txt index ca0b48bdd8a5..d88e27c949da 100644 --- a/api/test-current.txt +++ b/api/test-current.txt @@ -376,6 +376,7 @@ package android.app { } public class UiModeManager { + method @RequiresPermission("android.permission.ENTER_CAR_MODE_PRIORITIZED") public void enableCarMode(@IntRange(from=0) int, int); method public boolean isNightModeLocked(); method public boolean isUiModeLocked(); } @@ -3231,7 +3232,7 @@ package android.telephony.ims { public class ImsMmTelManager implements android.telephony.ims.RegistrationManager { method @NonNull public static android.telephony.ims.ImsMmTelManager createForSubscriptionId(int); - method @RequiresPermission("android.permission.READ_PRIVILEGED_PHONE_STATE") public void getFeatureState(@NonNull java.util.function.Consumer<java.lang.Integer>, @NonNull java.util.concurrent.Executor) throws android.telephony.ims.ImsException; + method @RequiresPermission("android.permission.READ_PRIVILEGED_PHONE_STATE") public void getFeatureState(@NonNull java.util.concurrent.Executor, @NonNull java.util.function.Consumer<java.lang.Integer>) throws android.telephony.ims.ImsException; method @RequiresPermission("android.permission.READ_PRIVILEGED_PHONE_STATE") public void getRegistrationState(@NonNull java.util.concurrent.Executor, @NonNull java.util.function.Consumer<java.lang.Integer>); method @RequiresPermission("android.permission.READ_PRIVILEGED_PHONE_STATE") public void getRegistrationTransportType(@NonNull java.util.concurrent.Executor, @NonNull java.util.function.Consumer<java.lang.Integer>); method @RequiresPermission("android.permission.READ_PRIVILEGED_PHONE_STATE") public int getVoWiFiModeSetting(); diff --git a/core/java/android/accounts/AbstractAccountAuthenticator.java b/core/java/android/accounts/AbstractAccountAuthenticator.java index 25cd342cb5e9..6470a04aefd6 100644 --- a/core/java/android/accounts/AbstractAccountAuthenticator.java +++ b/core/java/android/accounts/AbstractAccountAuthenticator.java @@ -103,8 +103,6 @@ import java.util.Arrays; * When writing an activity to satisfy these requests one must pass in the AccountManagerResponse * and return the result via that response when the activity finishes (or whenever else the * activity author deems it is the correct time to respond). - * The {@link AccountAuthenticatorActivity} handles this, so one may wish to extend that when - * writing activities to handle these requests. */ public abstract class AbstractAccountAuthenticator { private static final String TAG = "AccountAuthenticator"; diff --git a/core/java/android/accounts/AccountAuthenticatorActivity.java b/core/java/android/accounts/AccountAuthenticatorActivity.java index 967aa0424b1d..65ba35ff9e8d 100644 --- a/core/java/android/accounts/AccountAuthenticatorActivity.java +++ b/core/java/android/accounts/AccountAuthenticatorActivity.java @@ -32,7 +32,11 @@ import android.os.Bundle; * This result will be sent as the result of the request when the activity finishes. If this * is never set or if it is set to null then error {@link AccountManager#ERROR_CODE_CANCELED} * will be called on the response. + * + * @deprecated Applications should extend Activity themselves. This class is not compatible with + * AppCompat, and the functionality it provides is not complex. */ +@Deprecated public class AccountAuthenticatorActivity extends Activity { private AccountAuthenticatorResponse mAccountAuthenticatorResponse = null; private Bundle mResultBundle = null; diff --git a/core/java/android/app/IUiModeManager.aidl b/core/java/android/app/IUiModeManager.aidl index f2c9f615c03f..a3e0845af0ce 100644 --- a/core/java/android/app/IUiModeManager.aidl +++ b/core/java/android/app/IUiModeManager.aidl @@ -25,7 +25,7 @@ interface IUiModeManager { * Enables the car mode. Only the system can do this. * @hide */ - void enableCarMode(int flags); + void enableCarMode(int flags, int priority, String callingPackage); /** * Disables the car mode. @@ -34,6 +34,12 @@ interface IUiModeManager { void disableCarMode(int flags); /** + * Disables car mode (the original version is marked unsupported app usage so cannot be changed + * for the time being). + */ + void disableCarModeByCallingPackage(int flags, String callingPackage); + + /** * Return the current running mode. */ int getCurrentModeType(); diff --git a/core/java/android/app/SystemServiceRegistry.java b/core/java/android/app/SystemServiceRegistry.java index 0f5d6a30bc31..a70e874c3511 100644 --- a/core/java/android/app/SystemServiceRegistry.java +++ b/core/java/android/app/SystemServiceRegistry.java @@ -157,7 +157,7 @@ import android.os.health.SystemHealthManager; import android.os.image.DynamicSystemManager; import android.os.image.IDynamicSystemService; import android.os.storage.StorageManager; -import android.os.telephony.TelephonyRegistryManager; +import android.telephony.TelephonyRegistryManager; import android.permission.PermissionControllerManager; import android.permission.PermissionManager; import android.print.IPrintManager; @@ -611,7 +611,7 @@ final class SystemServiceRegistry { new CachedServiceFetcher<TelephonyRegistryManager>() { @Override public TelephonyRegistryManager createService(ContextImpl ctx) { - return new TelephonyRegistryManager(); + return new TelephonyRegistryManager(ctx); }}); registerService(Context.TELEPHONY_SUBSCRIPTION_SERVICE, SubscriptionManager.class, @@ -653,7 +653,7 @@ final class SystemServiceRegistry { new CachedServiceFetcher<UiModeManager>() { @Override public UiModeManager createService(ContextImpl ctx) throws ServiceNotFoundException { - return new UiModeManager(); + return new UiModeManager(ctx.getOuterContext()); }}); registerService(Context.USB_SERVICE, UsbManager.class, diff --git a/core/java/android/app/UiModeManager.java b/core/java/android/app/UiModeManager.java index 46316e1a254b..83247875c8a5 100644 --- a/core/java/android/app/UiModeManager.java +++ b/core/java/android/app/UiModeManager.java @@ -17,6 +17,10 @@ package android.app; import android.annotation.IntDef; +import android.annotation.IntRange; +import android.annotation.Nullable; +import android.annotation.RequiresPermission; +import android.annotation.SystemApi; import android.annotation.SystemService; import android.annotation.TestApi; import android.annotation.UnsupportedAppUsage; @@ -68,6 +72,25 @@ public class UiModeManager { * of the broadcast to {@link Activity#RESULT_CANCELED}. */ public static String ACTION_ENTER_CAR_MODE = "android.app.action.ENTER_CAR_MODE"; + + /** + * Broadcast sent when the device's UI has switched to car mode, either by being placed in a car + * dock or explicit action of the user. + * <p> + * In addition to the behavior for {@link #ACTION_ENTER_CAR_MODE}, this broadcast includes the + * package name of the app which requested to enter car mode in the + * {@link #EXTRA_CALLING_PACKAGE}. If an app requested to enter car mode using + * {@link #enableCarMode(int, int)} and specified a priority this will be specified in the + * {@link #EXTRA_PRIORITY}. + * + * This is primarily intended to be received by other components of the Android OS. + * <p> + * Receiver requires permission: {@link android.Manifest.permission.HANDLE_CAR_MODE_CHANGES} + * @hide + */ + @SystemApi + public static final String ACTION_ENTER_CAR_MODE_PRIORITIZED = + "android.app.action.ENTER_CAR_MODE_PRIORITIZED"; /** * Broadcast sent when the device's UI has switch away from car mode back @@ -75,6 +98,28 @@ public class UiModeManager { * when the user exits car mode. */ public static String ACTION_EXIT_CAR_MODE = "android.app.action.EXIT_CAR_MODE"; + + /** + * Broadcast sent when the device's UI has switched away from car mode back to normal mode. + * Typically used by a car mode app, to dismiss itself when the user exits car mode. + * <p> + * In addition to the behavior for {@link #ACTION_EXIT_CAR_MODE}, this broadcast includes the + * package name of the app which requested to exit car mode in {@link #EXTRA_CALLING_PACKAGE}. + * If an app requested to enter car mode using {@link #enableCarMode(int, int)} and specified a + * priority this will be specified in the {@link #EXTRA_PRIORITY} when exiting car mode. + * <p> + * If {@link #DISABLE_CAR_MODE_ALL_PRIORITIES} is used when disabling car mode (i.e. this is + * initiated by the user via the persistent car mode notification), this broadcast is sent once + * for each priority level for which car mode is being disabled. + * <p> + * This is primarily intended to be received by other components of the Android OS. + * <p> + * Receiver requires permission: {@link android.Manifest.permission.HANDLE_CAR_MODE_CHANGES} + * @hide + */ + @SystemApi + public static final String ACTION_EXIT_CAR_MODE_PRIORITIZED = + "android.app.action.EXIT_CAR_MODE_PRIORITIZED"; /** * Broadcast sent when the device's UI has switched to desk mode, @@ -97,6 +142,24 @@ public class UiModeManager { */ public static String ACTION_EXIT_DESK_MODE = "android.app.action.EXIT_DESK_MODE"; + /** + * String extra used with {@link #ACTION_ENTER_CAR_MODE_PRIORITIZED} and + * {@link #ACTION_EXIT_CAR_MODE_PRIORITIZED} to indicate the package name of the app which + * requested to enter or exit car mode. + * @hide + */ + @SystemApi + public static final String EXTRA_CALLING_PACKAGE = "android.app.extra.CALLING_PACKAGE"; + + /** + * Integer extra used with {@link #ACTION_ENTER_CAR_MODE_PRIORITIZED} and + * {@link #ACTION_EXIT_CAR_MODE_PRIORITIZED} to indicate the priority level at which car mode + * is being disabled. + * @hide + */ + @SystemApi + public static final String EXTRA_PRIORITY = "android.app.extra.PRIORITY"; + /** @hide */ @IntDef(prefix = { "MODE_" }, value = { MODE_NIGHT_AUTO, @@ -126,10 +189,21 @@ public class UiModeManager { private IUiModeManager mService; + /** + * Context required for getting the opPackageName of API caller; maybe be {@code null} if the + * old constructor marked with UnSupportedAppUsage is used. + */ + private @Nullable Context mContext; + @UnsupportedAppUsage /*package*/ UiModeManager() throws ServiceNotFoundException { + this(null /* context */); + } + + /*package*/ UiModeManager(Context context) throws ServiceNotFoundException { mService = IUiModeManager.Stub.asInterface( ServiceManager.getServiceOrThrow(Context.UI_MODE_SERVICE)); + mContext = context; } /** @@ -152,6 +226,14 @@ public class UiModeManager { */ public static final int ENABLE_CAR_MODE_ALLOW_SLEEP = 0x0002; + /** @hide */ + @IntDef(prefix = { "ENABLE_CAR_MODE_" }, value = { + ENABLE_CAR_MODE_GO_CAR_HOME, + ENABLE_CAR_MODE_ALLOW_SLEEP + }) + @Retention(RetentionPolicy.SOURCE) + public @interface EnableCarMode {} + /** * Force device into car mode, like it had been placed in the car dock. * This will cause the device to switch to the car home UI as part of @@ -159,9 +241,54 @@ public class UiModeManager { * @param flags Must be 0. */ public void enableCarMode(int flags) { + enableCarMode(DEFAULT_PRIORITY, flags); + } + + /** + * Force device into car mode, like it had been placed in the car dock. This will cause the + * device to switch to the car home UI as part of the mode switch. + * <p> + * An app may request to enter car mode when the system is already in car mode. The app may + * specify a "priority" when entering car mode. The device will remain in car mode + * (i.e. {@link #getCurrentModeType()} is {@link Configuration#UI_MODE_TYPE_CAR}) as long as + * there is a priority level at which car mode have been enabled. For example assume app A + * enters car mode at priority level 100, and then app B enters car mode at the default priority + * (0). If app A exits car mode, the device will remain in car mode until app B exits car mode. + * <p> + * Specifying a priority level when entering car mode is important in cases where multiple apps + * on a device implement a car-mode {@link android.telecom.InCallService} (see + * {@link android.telecom.TelecomManager#METADATA_IN_CALL_SERVICE_CAR_MODE_UI}). The + * {@link android.telecom.InCallService} associated with the highest priority app which entered + * car mode will be bound to by Telecom and provided with information about ongoing calls on + * the device. + * <p> + * System apps holding the required permission can enable car mode when the app determines the + * correct conditions exist for that app to be in car mode. The device maker should ensure that + * where multiple apps exist on the device which can potentially enter car mode, appropriate + * priorities are used to ensure that calls delivered by the + * {@link android.telecom.InCallService} API are delivered to the highest priority app. + * If app A and app B can both potentially enable car mode, and it is desired that app B is the + * one which should receive call information, the priority for app B should be higher than the + * one for app A. + * <p> + * When an app uses a priority to enable car mode, they can disable car mode at the specified + * priority level using {@link #disableCarMode(int)}. An app may only enable car mode at a + * single priority. + * <p> + * Public apps are assumed to enter/exit car mode at {@link #DEFAULT_PRIORITY}. + * + * @param priority The declared priority for the caller. + * @param flags Car mode flags. + * @hide + */ + @SystemApi + @TestApi + @RequiresPermission(android.Manifest.permission.ENTER_CAR_MODE_PRIORITIZED) + public void enableCarMode(@IntRange(from = 0) int priority, @EnableCarMode int flags) { if (mService != null) { try { - mService.enableCarMode(flags); + mService.enableCarMode(flags, priority, + mContext == null ? null : mContext.getOpPackageName()); } catch (RemoteException e) { throw e.rethrowFromSystemServer(); } @@ -176,15 +303,44 @@ public class UiModeManager { * being in car mode). */ public static final int DISABLE_CAR_MODE_GO_HOME = 0x0001; + + /** + * Flag for use with {@link #disableCarMode(int)}: Disables car mode at ALL priority levels. + * Primarily intended for use from {@link com.android.internal.app.DisableCarModeActivity} to + * provide the user with a means to exit car mode at all priority levels. + * @hide + */ + public static final int DISABLE_CAR_MODE_ALL_PRIORITIES = 0x0002; + + /** @hide */ + @IntDef(prefix = { "DISABLE_CAR_MODE_" }, value = { + DISABLE_CAR_MODE_GO_HOME + }) + @Retention(RetentionPolicy.SOURCE) + public @interface DisableCarMode {} + + /** + * The default priority used for entering car mode. + * <p> + * Callers of the {@link UiModeManager#enableCarMode(int)} priority will be assigned the + * default priority. + * <p> + * System apps can specify a priority other than the default priority when using + * {@link UiModeManager#enableCarMode(int, int)} to enable car mode. + * @hide + */ + @SystemApi + public static final int DEFAULT_PRIORITY = 0; /** * Turn off special mode if currently in car mode. - * @param flags May be 0 or {@link #DISABLE_CAR_MODE_GO_HOME}. + * @param flags One of the disable car mode flags. */ - public void disableCarMode(int flags) { + public void disableCarMode(@DisableCarMode int flags) { if (mService != null) { try { - mService.disableCarMode(flags); + mService.disableCarModeByCallingPackage(flags, + mContext == null ? null : mContext.getOpPackageName()); } catch (RemoteException e) { throw e.rethrowFromSystemServer(); } diff --git a/core/java/android/app/contentsuggestions/ContentSuggestionsManager.java b/core/java/android/app/contentsuggestions/ContentSuggestionsManager.java index 1bb81b1487af..1e6ab4136187 100644 --- a/core/java/android/app/contentsuggestions/ContentSuggestionsManager.java +++ b/core/java/android/app/contentsuggestions/ContentSuggestionsManager.java @@ -45,6 +45,17 @@ import java.util.concurrent.Executor; */ @SystemApi public final class ContentSuggestionsManager { + /** + * Key into the extras Bundle passed to {@link #provideContextImage(int, Bundle)}. + * This can be used to provide the bitmap to + * {@link android.service.contentsuggestions.ContentSuggestionsService}. + * The value must be a {@link android.graphics.Bitmap} with the + * config {@link android.graphics.Bitmap.Config.HARDWARE}. + * + * @hide + */ + public static final String EXTRA_BITMAP = "android.contentsuggestions.extra.BITMAP"; + private static final String TAG = ContentSuggestionsManager.class.getSimpleName(); /** @@ -70,7 +81,7 @@ public final class ContentSuggestionsManager { * system content suggestions service. * * @param taskId of the task to snapshot. - * @param imageContextRequestExtras sent with with request to provide implementation specific + * @param imageContextRequestExtras sent with request to provide implementation specific * extra information. */ public void provideContextImage( diff --git a/core/java/android/bluetooth/BluetoothAdapter.java b/core/java/android/bluetooth/BluetoothAdapter.java index 566b38738dc1..9d152a7faf44 100644 --- a/core/java/android/bluetooth/BluetoothAdapter.java +++ b/core/java/android/bluetooth/BluetoothAdapter.java @@ -1734,6 +1734,56 @@ public final class BluetoothAdapter { } /** + * Connects all enabled and supported bluetooth profiles between the local and remote device + * + * @param device is the remote device with which to connect these profiles + * @return true if all profiles successfully connected, false if an error occurred + * + * @hide + */ + @SystemApi + @RequiresPermission(Manifest.permission.BLUETOOTH_ADMIN) + public boolean connectAllEnabledProfiles(@NonNull BluetoothDevice device) { + try { + mServiceLock.readLock().lock(); + if (mService != null) { + return mService.connectAllEnabledProfiles(device); + } + } catch (RemoteException e) { + Log.e(TAG, "", e); + } finally { + mServiceLock.readLock().unlock(); + } + + return false; + } + + /** + * Disconnects all enabled and supported bluetooth profiles between the local and remote device + * + * @param device is the remote device with which to disconnect these profiles + * @return true if all profiles successfully disconnected, false if an error occurred + * + * @hide + */ + @SystemApi + @RequiresPermission(Manifest.permission.BLUETOOTH_ADMIN) + public boolean disconnectAllEnabledProfiles(@NonNull BluetoothDevice device) { + try { + mServiceLock.readLock().lock(); + if (mService != null) { + return mService.disconnectAllEnabledProfiles(device); + } + } catch (RemoteException e) { + Log.e(TAG, "", e); + } finally { + mServiceLock.readLock().unlock(); + } + + return false; + } + + /** * Return true if the multi advertisement is supported by the chipset * * @return true if Multiple Advertisement feature is supported diff --git a/core/java/android/bluetooth/BluetoothDevice.java b/core/java/android/bluetooth/BluetoothDevice.java index 19f42b6a4c9e..0be3eca8239e 100644 --- a/core/java/android/bluetooth/BluetoothDevice.java +++ b/core/java/android/bluetooth/BluetoothDevice.java @@ -1095,24 +1095,6 @@ public final class BluetoothDevice implements Parcelable { } /** - * Get the Bluetooth alias of the remote device. - * If Alias is null, get the Bluetooth name instead. - * - * @return the Bluetooth alias, or null if no alias or there was a problem - * @hide - * @see #getAlias() - * @see #getName() - */ - @UnsupportedAppUsage(publicAlternatives = "Use {@link #getName()} instead.") - public String getAliasName() { - String name = getAlias(); - if (name == null) { - name = getName(); - } - return name; - } - - /** * Get the most recent identified battery level of this Bluetooth device * <p>Requires {@link android.Manifest.permission#BLUETOOTH} * diff --git a/core/java/android/bluetooth/BluetoothProfile.java b/core/java/android/bluetooth/BluetoothProfile.java index dabe0fdac39a..f5aa01458481 100644 --- a/core/java/android/bluetooth/BluetoothProfile.java +++ b/core/java/android/bluetooth/BluetoothProfile.java @@ -324,4 +324,54 @@ public interface BluetoothProfile { return "STATE_UNKNOWN"; } } + + /** + * Convert an integer value of profile ID into human readable string + * + * @param profile profile ID + * @return profile name as String, UNKOWN_PROFILE if the profile ID is not defined. + * @hide + */ + static String getProfileName(int profile) { + switch(profile) { + case HEADSET: + return "HEADSET"; + case A2DP: + return "A2DP"; + case HID_HOST: + return "HID_HOST"; + case PAN: + return "PAN"; + case PBAP: + return "PBAP"; + case GATT: + return "GATT"; + case GATT_SERVER: + return "GATT_SERVER"; + case MAP: + return "MAP"; + case SAP: + return "SAP"; + case A2DP_SINK: + return "A2DP_SINK"; + case AVRCP_CONTROLLER: + return "AVRCP_CONTROLLER"; + case AVRCP: + return "AVRCP"; + case HEADSET_CLIENT: + return "HEADSET_CLIENT"; + case PBAP_CLIENT: + return "PBAP_CLIENT"; + case MAP_CLIENT: + return "MAP_CLIENT"; + case HID_DEVICE: + return "HID_DEVICE"; + case OPP: + return "OPP"; + case HEARING_AID: + return "HEARING_AID"; + default: + return "UNKNOWN_PROFILE"; + } + } } diff --git a/core/java/android/content/Context.java b/core/java/android/content/Context.java index bb96ed265e6b..aca8f5d8a4e2 100644 --- a/core/java/android/content/Context.java +++ b/core/java/android/content/Context.java @@ -63,6 +63,7 @@ import android.os.UserHandle; import android.os.UserManager; import android.os.storage.StorageManager; import android.provider.MediaStore; +import android.telephony.TelephonyRegistryManager; import android.util.AttributeSet; import android.view.Display; import android.view.DisplayAdjustments; @@ -4755,7 +4756,7 @@ public abstract class Context { /** * Use with {@link #getSystemService(String)} to retrieve an - * {@link android.os.telephony.TelephonyRegistryManager}. + * {@link TelephonyRegistryManager}. * @hide */ @SystemApi diff --git a/core/java/android/content/pm/PackageManagerInternal.java b/core/java/android/content/pm/PackageManagerInternal.java index 28d9152b3707..30a3bd4d0738 100644 --- a/core/java/android/content/pm/PackageManagerInternal.java +++ b/core/java/android/content/pm/PackageManagerInternal.java @@ -415,6 +415,17 @@ public abstract class PackageManagerInternal { @ResolveInfoFlags int flags, int filterCallingUid, int userId); /** + * Retrieve all activities that can be performed for the given intent. + * @param filterCallingUid The results will be filtered in the context of this UID instead + * of the calling UID. + * @see PackageManager#queryIntentActivities(Intent, int) + */ + public abstract List<ResolveInfo> queryIntentActivities( + Intent intent, @Nullable String resolvedType, @ResolveInfoFlags int flags, + int filterCallingUid, int userId); + + + /** * Retrieve all services that can be performed for the given intent. * @see PackageManager#queryIntentServices(Intent, int) */ diff --git a/core/java/android/os/Handler.java b/core/java/android/os/Handler.java index 9af9edae9a3f..a99bdabee137 100644 --- a/core/java/android/os/Handler.java +++ b/core/java/android/os/Handler.java @@ -28,15 +28,14 @@ import java.lang.reflect.Modifier; * A Handler allows you to send and process {@link Message} and Runnable * objects associated with a thread's {@link MessageQueue}. Each Handler * instance is associated with a single thread and that thread's message - * queue. When you create a new Handler, it is bound to the thread / - * message queue of the thread that is creating it -- from that point on, - * it will deliver messages and runnables to that message queue and execute - * them as they come out of the message queue. - * + * queue. When you create a new Handler it is bound to a {@link Looper}. + * It will deliver messages and runnables to that Looper's message + * queue and execute them on that Looper's thread. + * * <p>There are two main uses for a Handler: (1) to schedule messages and * runnables to be executed at some point in the future; and (2) to enqueue * an action to be performed on a different thread than your own. - * + * * <p>Scheduling messages is accomplished with the * {@link #post}, {@link #postAtTime(Runnable, long)}, * {@link #postDelayed}, {@link #sendEmptyMessage}, @@ -114,7 +113,18 @@ public class Handler { * * If this thread does not have a looper, this handler won't be able to receive messages * so an exception is thrown. + * + * @deprecated Implicitly choosing a Looper during Handler construction can lead to bugs + * where operations are silently lost (if the Handler is not expecting new tasks and quits), + * crashes (if a handler is sometimes created on a thread without a Looper active), or race + * conditions, where the thread a handler is associated with is not what the author + * anticipated. Instead, use an {@link java.util.concurrent.Executor} or specify the Looper + * explicitly, using {@link Looper#getMainLooper}, {link android.view.View#getHandler}, or + * similar. If the implicit thread local behavior is required for compatibility, use + * {@code new Handler(Looper.myLooper())} to make it clear to readers. + * */ + @Deprecated public Handler() { this(null, false); } @@ -128,7 +138,17 @@ public class Handler { * so an exception is thrown. * * @param callback The callback interface in which to handle messages, or null. - */ + * + * @deprecated Implicitly choosing a Looper during Handler construction can lead to bugs + * where operations are silently lost (if the Handler is not expecting new tasks and quits), + * crashes (if a handler is sometimes created on a thread without a Looper active), or race + * conditions, where the thread a handler is associated with is not what the author + * anticipated. Instead, use an {@link java.util.concurrent.Executor} or specify the Looper + * explicitly, using {@link Looper#getMainLooper}, {link android.view.View#getHandler}, or + * similar. If the implicit thread local behavior is required for compatibility, use + * {@code new Handler(Looper.myLooper(), callback)} to make it clear to readers. + */ + @Deprecated public Handler(@Nullable Callback callback) { this(callback, false); } diff --git a/core/java/android/provider/CallLog.java b/core/java/android/provider/CallLog.java index 82a3c4015f70..391f3a2fd135 100644 --- a/core/java/android/provider/CallLog.java +++ b/core/java/android/provider/CallLog.java @@ -43,8 +43,6 @@ import android.telephony.PhoneNumberUtils; import android.text.TextUtils; import android.util.Log; -import com.android.internal.telephony.PhoneConstants; - import java.util.List; /** @@ -610,7 +608,7 @@ public class CallLog { * if the contact is unknown. * @param context the context used to get the ContentResolver * @param number the phone number to be added to the calls db - * @param presentation enum value from PhoneConstants.PRESENTATION_xxx, which + * @param presentation enum value from TelecomManager.PRESENTATION_xxx, which * is set by the network and denotes the number presenting rules for * "allowed", "payphone", "restricted" or "unknown" * @param callType enumerated values for "incoming", "outgoing", or "missed" @@ -645,7 +643,7 @@ public class CallLog { * @param number the phone number to be added to the calls db * @param viaNumber the secondary number that the incoming call received with. If the * call was received with the SIM assigned number, then this field must be ''. - * @param presentation enum value from PhoneConstants.PRESENTATION_xxx, which + * @param presentation enum value from TelecomManager.PRESENTATION_xxx, which * is set by the network and denotes the number presenting rules for * "allowed", "payphone", "restricted" or "unknown" * @param callType enumerated values for "incoming", "outgoing", or "missed" @@ -686,7 +684,7 @@ public class CallLog { * if it was outgoing. Otherwise it is ''. * @param viaNumber the secondary number that the incoming call received with. If the * call was received with the SIM assigned number, then this field must be ''. - * @param presentation enum value from PhoneConstants.PRESENTATION_xxx, which + * @param presentation enum value from TelecomManager.PRESENTATION_xxx, which * is set by the network and denotes the number presenting rules for * "allowed", "payphone", "restricted" or "unknown" * @param callType enumerated values for "incoming", "outgoing", or "missed" @@ -1048,22 +1046,22 @@ public class CallLog { /** * Remap network specified number presentation types - * PhoneConstants.PRESENTATION_xxx to calllog number presentation types + * TelecomManager.PRESENTATION_xxx to calllog number presentation types * Calls.PRESENTATION_xxx, in order to insulate the persistent calllog * from any future radio changes. * If the number field is empty set the presentation type to Unknown. */ private static int getLogNumberPresentation(String number, int presentation) { - if (presentation == PhoneConstants.PRESENTATION_RESTRICTED) { + if (presentation == TelecomManager.PRESENTATION_RESTRICTED) { return presentation; } - if (presentation == PhoneConstants.PRESENTATION_PAYPHONE) { + if (presentation == TelecomManager.PRESENTATION_PAYPHONE) { return presentation; } if (TextUtils.isEmpty(number) - || presentation == PhoneConstants.PRESENTATION_UNKNOWN) { + || presentation == TelecomManager.PRESENTATION_UNKNOWN) { return PRESENTATION_UNKNOWN; } diff --git a/core/java/android/service/carrier/CarrierService.java b/core/java/android/service/carrier/CarrierService.java index 9184d6d51f44..eefc1b70bac9 100644 --- a/core/java/android/service/carrier/CarrierService.java +++ b/core/java/android/service/carrier/CarrierService.java @@ -22,7 +22,7 @@ import android.os.Bundle; import android.os.IBinder; import android.os.PersistableBundle; import android.os.ResultReceiver; -import android.os.telephony.TelephonyRegistryManager; +import android.telephony.TelephonyRegistryManager; import android.util.Log; /** diff --git a/core/java/android/service/contentsuggestions/ContentSuggestionsService.java b/core/java/android/service/contentsuggestions/ContentSuggestionsService.java index efc8e877f3c3..306b4830932e 100644 --- a/core/java/android/service/contentsuggestions/ContentSuggestionsService.java +++ b/core/java/android/service/contentsuggestions/ContentSuggestionsService.java @@ -64,14 +64,23 @@ public abstract class ContentSuggestionsService extends Service { @Override public void provideContextImage(int taskId, GraphicBuffer contextImage, int colorSpaceId, Bundle imageContextRequestExtras) { + if (imageContextRequestExtras.containsKey(ContentSuggestionsManager.EXTRA_BITMAP) + && contextImage != null) { + throw new IllegalArgumentException("Two bitmaps provided; expected one."); + } Bitmap wrappedBuffer = null; - if (contextImage != null) { - ColorSpace colorSpace = null; - if (colorSpaceId >= 0 && colorSpaceId < ColorSpace.Named.values().length) { - colorSpace = ColorSpace.get(ColorSpace.Named.values()[colorSpaceId]); + if (imageContextRequestExtras.containsKey(ContentSuggestionsManager.EXTRA_BITMAP)) { + wrappedBuffer = imageContextRequestExtras.getParcelable( + ContentSuggestionsManager.EXTRA_BITMAP); + } else { + if (contextImage != null) { + ColorSpace colorSpace = null; + if (colorSpaceId >= 0 && colorSpaceId < ColorSpace.Named.values().length) { + colorSpace = ColorSpace.get(ColorSpace.Named.values()[colorSpaceId]); + } + wrappedBuffer = Bitmap.wrapHardwareBuffer(contextImage, colorSpace); } - wrappedBuffer = Bitmap.wrapHardwareBuffer(contextImage, colorSpace); } mHandler.sendMessage( diff --git a/telephony/java/android/telephony/PhoneStateListener.java b/core/java/android/telephony/PhoneStateListener.java index e5bfb4dfb7b4..2af827e36696 100644 --- a/telephony/java/android/telephony/PhoneStateListener.java +++ b/core/java/android/telephony/PhoneStateListener.java @@ -35,8 +35,8 @@ import android.telephony.Annotation.SrvccState; import android.telephony.emergency.EmergencyNumber; import android.telephony.ims.ImsReasonInfo; -import com.android.internal.annotations.VisibleForTesting; import com.android.internal.telephony.IPhoneStateListener; +import com.android.internal.annotations.VisibleForTesting; import dalvik.system.VMRuntime; diff --git a/telephony/java/android/telephony/Rlog.java b/core/java/android/telephony/Rlog.java index cdab2dc54dd2..cdab2dc54dd2 100644 --- a/telephony/java/android/telephony/Rlog.java +++ b/core/java/android/telephony/Rlog.java diff --git a/core/java/android/os/telephony/TelephonyRegistryManager.java b/core/java/android/telephony/TelephonyRegistryManager.java index b67409988da0..456789fb77bc 100644 --- a/core/java/android/os/telephony/TelephonyRegistryManager.java +++ b/core/java/android/telephony/TelephonyRegistryManager.java @@ -13,12 +13,20 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package android.os.telephony; +package android.telephony; +import android.annotation.NonNull; +import android.annotation.Nullable; +import android.annotation.RequiresPermission; import android.annotation.SystemApi; +import android.annotation.TestApi; +import android.content.Context; import android.net.LinkProperties; import android.net.NetworkCapabilities; +import android.os.Binder; import android.os.Bundle; +import android.os.Handler; +import android.os.HandlerExecutor; import android.os.RemoteException; import android.os.ServiceManager; import android.telephony.Annotation; @@ -41,8 +49,15 @@ import android.telephony.SignalStrength; import android.telephony.TelephonyManager; import android.telephony.data.ApnSetting; import android.telephony.ims.ImsReasonInfo; +import android.util.Log; + +import com.android.internal.telephony.IOnSubscriptionsChangedListener; import com.android.internal.telephony.ITelephonyRegistry; + +import java.util.HashMap; import java.util.List; +import java.util.Map; +import java.util.concurrent.Executor; /** * A centralized place to notify telephony related status changes, e.g, {@link ServiceState} update @@ -59,9 +74,26 @@ public class TelephonyRegistryManager { private static final String TAG = "TelephonyRegistryManager"; private static ITelephonyRegistry sRegistry; + private final Context mContext; + + /** + * A mapping between {@link SubscriptionManager.OnSubscriptionsChangedListener} and + * its callback IOnSubscriptionsChangedListener. + */ + private final Map<SubscriptionManager.OnSubscriptionsChangedListener, + IOnSubscriptionsChangedListener> mSubscriptionChangedListenerMap = new HashMap<>(); + /** + * A mapping between {@link SubscriptionManager.OnOpportunisticSubscriptionsChangedListener} and + * its callback IOnSubscriptionsChangedListener. + */ + private final Map<SubscriptionManager.OnOpportunisticSubscriptionsChangedListener, + IOnSubscriptionsChangedListener> mOpportunisticSubscriptionChangedListenerMap + = new HashMap<>(); + /** @hide **/ - public TelephonyRegistryManager() { + public TelephonyRegistryManager(@NonNull Context context) { + mContext = context; if (sRegistry == null) { sRegistry = ITelephonyRegistry.Stub.asInterface( ServiceManager.getService("telephony.registry")); @@ -69,6 +101,113 @@ public class TelephonyRegistryManager { } /** + * Register for changes to the list of active {@link SubscriptionInfo} records or to the + * individual records themselves. When a change occurs the onSubscriptionsChanged method of + * the listener will be invoked immediately if there has been a notification. The + * onSubscriptionChanged method will also be triggered once initially when calling this + * function. + * + * @param listener an instance of {@link SubscriptionManager.OnSubscriptionsChangedListener} + * with onSubscriptionsChanged overridden. + * @param executor the executor that will execute callbacks. + */ + public void addOnSubscriptionsChangedListener( + @NonNull SubscriptionManager.OnSubscriptionsChangedListener listener, + @NonNull Executor executor) { + IOnSubscriptionsChangedListener callback = new IOnSubscriptionsChangedListener.Stub() { + @Override + public void onSubscriptionsChanged () { + Log.d(TAG, "onSubscriptionsChangedListener callback received."); + executor.execute(() -> listener.onSubscriptionsChanged()); + } + }; + mSubscriptionChangedListenerMap.put(listener, callback); + try { + sRegistry.addOnSubscriptionsChangedListener(mContext.getOpPackageName(), callback); + } catch (RemoteException ex) { + // system server crash + } + } + + /** + * Unregister the {@link SubscriptionManager.OnSubscriptionsChangedListener}. This is not + * strictly necessary as the listener will automatically be unregistered if an attempt to + * invoke the listener fails. + * + * @param listener that is to be unregistered. + */ + public void removeOnSubscriptionsChangedListener( + @NonNull SubscriptionManager.OnSubscriptionsChangedListener listener) { + if (mSubscriptionChangedListenerMap.get(listener) == null) { + return; + } + try { + sRegistry.removeOnSubscriptionsChangedListener(mContext.getOpPackageName(), + mSubscriptionChangedListenerMap.get(listener)); + mSubscriptionChangedListenerMap.remove(listener); + } catch (RemoteException ex) { + // system server crash + } + } + + /** + * Register for changes to the list of opportunistic subscription records or to the + * individual records themselves. When a change occurs the onOpportunisticSubscriptionsChanged + * method of the listener will be invoked immediately if there has been a notification. + * + * @param listener an instance of + * {@link SubscriptionManager.OnOpportunisticSubscriptionsChangedListener} with + * onOpportunisticSubscriptionsChanged overridden. + * @param executor an Executor that will execute callbacks. + */ + public void addOnOpportunisticSubscriptionsChangedListener( + @NonNull SubscriptionManager.OnOpportunisticSubscriptionsChangedListener listener, + @NonNull Executor executor) { + /** + * The callback methods need to be called on the executor thread where + * this object was created. If the binder did that for us it'd be nice. + */ + IOnSubscriptionsChangedListener callback = new IOnSubscriptionsChangedListener.Stub() { + @Override + public void onSubscriptionsChanged() { + final long identity = Binder.clearCallingIdentity(); + try { + Log.d(TAG, "onOpportunisticSubscriptionsChanged callback received."); + executor.execute(() -> listener.onOpportunisticSubscriptionsChanged()); + } finally { + Binder.restoreCallingIdentity(identity); + } + } + }; + mOpportunisticSubscriptionChangedListenerMap.put(listener, callback); + try { + sRegistry.addOnOpportunisticSubscriptionsChangedListener(mContext.getOpPackageName(), + callback); + } catch (RemoteException ex) { + // system server crash + } + } + + /** + * Unregister the {@link SubscriptionManager.OnOpportunisticSubscriptionsChangedListener} + * that is currently listening opportunistic subscriptions change. This is not strictly + * necessary as the listener will automatically be unregistered if an attempt to invoke the + * listener fails. + * + * @param listener that is to be unregistered. + */ + public void removeOnOpportunisticSubscriptionsChangedListener( + @NonNull SubscriptionManager.OnOpportunisticSubscriptionsChangedListener listener) { + try { + sRegistry.removeOnSubscriptionsChangedListener(mContext.getOpPackageName(), + mOpportunisticSubscriptionChangedListenerMap.get(listener)); + mOpportunisticSubscriptionChangedListenerMap.remove(listener); + } catch (RemoteException ex) { + // system server crash + } + } + + /** * Informs the system of an intentional upcoming carrier network change by a carrier app. * This call only used to allow the system to provide alternative UI while telephony is * performing an action that may result in intentional, temporary network lack of connectivity. @@ -98,14 +237,33 @@ public class TelephonyRegistryManager { * @param slotIndex for which call state changed. Can be derived from subId except when subId is * invalid. * @param state latest call state. e.g, offhook, ringing - * @param incomingNumer incoming phone number. + * @param incomingNumber incoming phone number. * * @hide */ public void notifyCallStateChanged(int subId, int slotIndex, @CallState int state, - String incomingNumer) { + String incomingNumber) { try { - sRegistry.notifyCallState(slotIndex, subId, state, incomingNumer); + sRegistry.notifyCallState(slotIndex, subId, state, incomingNumber); + } catch (RemoteException ex) { + // system server crash + } + } + + /** + * Notify call state changed on all subscriptions. + * + * @param state latest call state. e.g, offhook, ringing + * @param incomingNumber incoming phone number. + * @hide + */ + @SystemApi + @TestApi + @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) + public void notifyCallStateChangedForAllSubscriptions(@CallState int state, + @Nullable String incomingNumber) { + try { + sRegistry.notifyCallStateForAllSubs(state, incomingNumber); } catch (RemoteException ex) { // system server crash } @@ -545,4 +703,15 @@ public class TelephonyRegistryManager { } } + /** + * @param activeDataSubId + * @hide + */ + public void notifyActiveDataSubIdChanged(int activeDataSubId) { + try { + sRegistry.notifyActiveDataSubIdChanged(activeDataSubId); + } catch (RemoteException ex) { + + } + } } diff --git a/core/java/com/android/internal/app/AbstractResolverComparator.java b/core/java/com/android/internal/app/AbstractResolverComparator.java index 9ac979b54716..3a6a71d8eab6 100644 --- a/core/java/com/android/internal/app/AbstractResolverComparator.java +++ b/core/java/com/android/internal/app/AbstractResolverComparator.java @@ -30,6 +30,7 @@ import android.util.Log; import com.android.internal.app.ResolverActivity.ResolvedComponentInfo; +import java.text.Collator; import java.util.ArrayList; import java.util.Comparator; import java.util.List; @@ -37,7 +38,7 @@ import java.util.List; /** * Used to sort resolved activities in {@link ResolverListController}. */ -abstract class AbstractResolverComparator implements Comparator<ResolvedComponentInfo> { +public abstract class AbstractResolverComparator implements Comparator<ResolvedComponentInfo> { private static final int NUM_OF_TOP_ANNOTATIONS_TO_USE = 3; private static final boolean DEBUG = false; @@ -62,6 +63,8 @@ abstract class AbstractResolverComparator implements Comparator<ResolvedComponen // predicting ranking scores. private static final int WATCHDOG_TIMEOUT_MILLIS = 500; + private final Comparator<ResolveInfo> mAzComparator; + protected final Handler mHandler = new Handler(Looper.getMainLooper()) { public void handleMessage(Message msg) { switch (msg.what) { @@ -90,7 +93,7 @@ abstract class AbstractResolverComparator implements Comparator<ResolvedComponen } }; - AbstractResolverComparator(Context context, Intent intent) { + public AbstractResolverComparator(Context context, Intent intent) { String scheme = intent.getScheme(); mHttp = "http".equals(scheme) || "https".equals(scheme); mContentType = intent.getType(); @@ -100,6 +103,7 @@ abstract class AbstractResolverComparator implements Comparator<ResolvedComponen mDefaultBrowserPackageName = mHttp ? mPm.getDefaultBrowserPackageNameAsUser(UserHandle.myUserId()) : null; + mAzComparator = new AzInfoComparator(context); } // get annotations of content from intent. @@ -168,6 +172,20 @@ abstract class AbstractResolverComparator implements Comparator<ResolvedComponen return lhsSpecific ? -1 : 1; } } + + final boolean lPinned = lhsp.isPinned(); + final boolean rPinned = rhsp.isPinned(); + + // Pinned items always receive priority. + if (lPinned && !rPinned) { + return -1; + } else if (!lPinned && rPinned) { + return 1; + } else if (lPinned && rPinned) { + // If both items are pinned, resolve the tie alphabetically. + return mAzComparator.compare(lhsp.getResolveInfoAt(0), rhsp.getResolveInfoAt(0)); + } + return compare(lhs, rhs); } @@ -258,4 +276,25 @@ abstract class AbstractResolverComparator implements Comparator<ResolvedComponen } return false; } + + /** + * Sort intents alphabetically based on package name. + */ + class AzInfoComparator implements Comparator<ResolveInfo> { + Collator mCollator; + AzInfoComparator(Context context) { + mCollator = Collator.getInstance(context.getResources().getConfiguration().locale); + } + + @Override + public int compare(ResolveInfo lhsp, ResolveInfo rhsp) { + if (lhsp == null) { + return -1; + } else if (rhsp == null) { + return 1; + } + return mCollator.compare(lhsp.activityInfo.packageName, rhsp.activityInfo.packageName); + } + } + } diff --git a/core/java/com/android/internal/app/ChooserActivity.java b/core/java/com/android/internal/app/ChooserActivity.java index 102ba5c2ec5e..292e6d07dbb5 100644 --- a/core/java/com/android/internal/app/ChooserActivity.java +++ b/core/java/com/android/internal/app/ChooserActivity.java @@ -44,6 +44,7 @@ import android.content.IntentFilter; import android.content.IntentSender; import android.content.IntentSender.SendIntentException; import android.content.ServiceConnection; +import android.content.SharedPreferences; import android.content.pm.ActivityInfo; import android.content.pm.ApplicationInfo; import android.content.pm.LabeledIntent; @@ -69,6 +70,7 @@ import android.metrics.LogMaker; import android.net.Uri; import android.os.AsyncTask; import android.os.Bundle; +import android.os.Environment; import android.os.Handler; import android.os.IBinder; import android.os.Message; @@ -78,6 +80,7 @@ import android.os.RemoteException; import android.os.ResultReceiver; import android.os.UserHandle; import android.os.UserManager; +import android.os.storage.StorageManager; import android.provider.DeviceConfig; import android.provider.DocumentsContract; import android.provider.Downloads; @@ -120,6 +123,7 @@ import com.android.internal.widget.ResolverDrawerLayout; import com.google.android.collect.Lists; +import java.io.File; import java.io.IOException; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; @@ -256,6 +260,9 @@ public class ChooserActivity extends ResolverActivity { private static final int MAX_EXTRA_INITIAL_INTENTS = 2; private static final int MAX_EXTRA_CHOOSER_TARGETS = 2; + private SharedPreferences mPinnedSharedPrefs; + private static final String PINNED_SHARED_PREFS_NAME = "chooser_pin_settings"; + private boolean mListViewDataChanged = false; @Retention(SOURCE) @@ -600,6 +607,8 @@ public class ChooserActivity extends ResolverActivity { Intent.EXTRA_CHOOSER_REFINEMENT_INTENT_SENDER); setSafeForwardingMode(true); + mPinnedSharedPrefs = getPinnedSharedPrefs(this); + pa = intent.getParcelableArrayExtra(Intent.EXTRA_EXCLUDE_COMPONENTS); if (pa != null) { ComponentName[] names = new ComponentName[pa.length]; @@ -731,6 +740,23 @@ public class ChooserActivity extends ResolverActivity { } } + + static SharedPreferences getPinnedSharedPrefs(Context context) { + // The code below is because in the android:ui process, no one can hear you scream. + // The package info in the context isn't initialized in the way it is for normal apps, + // so the standard, name-based context.getSharedPreferences doesn't work. Instead, we + // build the path manually below using the same policy that appears in ContextImpl. + // This fails silently under the hood if there's a problem, so if we find ourselves in + // the case where we don't have access to credential encrypted storage we just won't + // have our pinned target info. + final File prefsFile = new File(new File( + Environment.getDataUserCePackageDirectory(StorageManager.UUID_PRIVATE_INTERNAL, + context.getUserId(), context.getPackageName()), + "shared_prefs"), + PINNED_SHARED_PREFS_NAME + ".xml"); + return context.getSharedPreferences(prefsFile, MODE_PRIVATE); + } + /** * Returns true if app prediction service is defined and the component exists on device. */ @@ -1277,9 +1303,10 @@ public class ChooserActivity extends ResolverActivity { } ComponentName name = ri.activityInfo.getComponentName(); + boolean pinned = mPinnedSharedPrefs.getBoolean(name.flattenToString(), false); ResolverTargetActionsDialogFragment f = new ResolverTargetActionsDialogFragment(ri.loadLabel(getPackageManager()), - name); + name, pinned); f.show(getFragmentManager(), TARGET_DETAILS_FRAGMENT_TAG); } @@ -1956,6 +1983,12 @@ public class ChooserActivity extends ResolverActivity { } return false; } + + @Override + public boolean isComponentPinned(ComponentName name) { + return mPinnedSharedPrefs.getBoolean(name.flattenToString(), false); + } + } @Override @@ -2089,6 +2122,10 @@ public class ChooserActivity extends ResolverActivity { public boolean isSuspended() { return false; } + + public boolean isPinned() { + return false; + } } final class PlaceHolderTargetInfo extends NotSelectableTargetInfo { @@ -2177,6 +2214,10 @@ public class ChooserActivity extends ResolverActivity { return mIsSuspended; } + public boolean isPinned() { + return mSourceInfo != null && mSourceInfo.isPinned(); + } + /** * Since ShortcutInfos are returned by ShortcutManager, we can cache the shortcuts and skip * the call to LauncherApps#getShortcuts(ShortcutQuery). diff --git a/core/java/com/android/internal/app/DisableCarModeActivity.java b/core/java/com/android/internal/app/DisableCarModeActivity.java index 7943c613b357..d44312b716ba 100644 --- a/core/java/com/android/internal/app/DisableCarModeActivity.java +++ b/core/java/com/android/internal/app/DisableCarModeActivity.java @@ -33,7 +33,9 @@ public class DisableCarModeActivity extends Activity { try { IUiModeManager uiModeManager = IUiModeManager.Stub.asInterface( ServiceManager.getService("uimode")); - uiModeManager.disableCarMode(UiModeManager.DISABLE_CAR_MODE_GO_HOME); + uiModeManager.disableCarModeByCallingPackage(UiModeManager.DISABLE_CAR_MODE_GO_HOME + | UiModeManager.DISABLE_CAR_MODE_ALL_PRIORITIES, + getOpPackageName()); } catch (RemoteException e) { Log.e(TAG, "Failed to disable car mode", e); } diff --git a/core/java/com/android/internal/app/ResolverActivity.java b/core/java/com/android/internal/app/ResolverActivity.java index c7d2547a24f4..77f37e31a087 100644 --- a/core/java/com/android/internal/app/ResolverActivity.java +++ b/core/java/com/android/internal/app/ResolverActivity.java @@ -1417,6 +1417,7 @@ public class ResolverActivity extends Activity { private final Intent mResolvedIntent; private final List<Intent> mSourceIntents = new ArrayList<>(); private boolean mIsSuspended; + private boolean mPinned = false; public DisplayResolveInfo(Intent originalIntent, ResolveInfo pri, CharSequence pLabel, CharSequence pInfo, Intent pOrigIntent) { @@ -1520,6 +1521,15 @@ public class ResolverActivity extends Activity { public boolean isSuspended() { return mIsSuspended; } + + @Override + public boolean isPinned() { + return mPinned; + } + + public void setPinned(boolean pinned) { + mPinned = pinned; + } } List<DisplayResolveInfo> getDisplayList() { @@ -1620,6 +1630,11 @@ public class ResolverActivity extends Activity { * @return true if this target can be selected by the user */ boolean isSuspended(); + + /** + * @return true if this target should be pinned to the front by the request of the user + */ + boolean isPinned(); } public class ResolveListAdapter extends BaseAdapter { @@ -1926,6 +1941,10 @@ public class ResolverActivity extends Activity { final Intent replaceIntent = getReplacementIntent(add.activityInfo, intent); final DisplayResolveInfo dri = new DisplayResolveInfo(intent, add, roLabel, extraInfo, replaceIntent); + dri.setPinned(rci.isPinned()); + if (rci.isPinned()) { + Log.i(TAG, "Pinned item: " + rci.name); + } addResolveInfo(dri); if (replaceIntent == intent) { // Only add alternates if we didn't get a specific replacement from @@ -2094,6 +2113,7 @@ public class ResolverActivity extends Activity { public final ComponentName name; private final List<Intent> mIntents = new ArrayList<>(); private final List<ResolveInfo> mResolveInfos = new ArrayList<>(); + private boolean mPinned; public ResolvedComponentInfo(ComponentName name, Intent intent, ResolveInfo info) { this.name = name; @@ -2134,6 +2154,15 @@ public class ResolverActivity extends Activity { } return -1; } + + public boolean isPinned() { + return mPinned; + } + + public void setPinned(boolean pinned) { + mPinned = pinned; + } + } static class ViewHolder { diff --git a/core/java/com/android/internal/app/ResolverListController.java b/core/java/com/android/internal/app/ResolverListController.java index 5f92cddbaa38..7efd5e16392c 100644 --- a/core/java/com/android/internal/app/ResolverListController.java +++ b/core/java/com/android/internal/app/ResolverListController.java @@ -156,11 +156,22 @@ public class ResolverListController { newInfo.activityInfo.packageName, newInfo.activityInfo.name); final ResolverActivity.ResolvedComponentInfo rci = new ResolverActivity.ResolvedComponentInfo(name, intent, newInfo); + rci.setPinned(isComponentPinned(name)); into.add(rci); } } } + + /** + * Whether this component is pinned by the user. Always false for resolver; overridden in + * Chooser. + */ + public boolean isComponentPinned(ComponentName name) { + return false; + } + + // Filter out any activities that the launched uid does not have permission for. // To preserve the inputList, optionally will return the original list if any modification has // been made. diff --git a/core/java/com/android/internal/app/ResolverTargetActionsDialogFragment.java b/core/java/com/android/internal/app/ResolverTargetActionsDialogFragment.java index a49240cd0019..df91c4a1f88d 100644 --- a/core/java/com/android/internal/app/ResolverTargetActionsDialogFragment.java +++ b/core/java/com/android/internal/app/ResolverTargetActionsDialogFragment.java @@ -23,6 +23,7 @@ import android.app.DialogFragment; import android.content.ComponentName; import android.content.DialogInterface; import android.content.Intent; +import android.content.SharedPreferences; import android.net.Uri; import android.os.Bundle; import android.provider.Settings; @@ -36,26 +37,33 @@ public class ResolverTargetActionsDialogFragment extends DialogFragment implements DialogInterface.OnClickListener { private static final String NAME_KEY = "componentName"; private static final String TITLE_KEY = "title"; + private static final String PINNED_KEY = "pinned"; // Sync with R.array.resolver_target_actions_* resources - private static final int APP_INFO_INDEX = 0; + private static final int TOGGLE_PIN_INDEX = 0; + private static final int APP_INFO_INDEX = 1; public ResolverTargetActionsDialogFragment() { } - public ResolverTargetActionsDialogFragment(CharSequence title, ComponentName name) { + public ResolverTargetActionsDialogFragment(CharSequence title, ComponentName name, + boolean pinned) { Bundle args = new Bundle(); args.putCharSequence(TITLE_KEY, title); args.putParcelable(NAME_KEY, name); + args.putBoolean(PINNED_KEY, pinned); setArguments(args); } @Override public Dialog onCreateDialog(Bundle savedInstanceState) { final Bundle args = getArguments(); + final int itemRes = args.getBoolean(PINNED_KEY, false) + ? R.array.resolver_target_actions_unpin + : R.array.resolver_target_actions_pin; return new Builder(getContext()) .setCancelable(true) - .setItems(R.array.resolver_target_actions, this) + .setItems(itemRes, this) .setTitle(args.getCharSequence(TITLE_KEY)) .create(); } @@ -65,6 +73,19 @@ public class ResolverTargetActionsDialogFragment extends DialogFragment final Bundle args = getArguments(); ComponentName name = args.getParcelable(NAME_KEY); switch (which) { + case TOGGLE_PIN_INDEX: + SharedPreferences sp = ChooserActivity.getPinnedSharedPrefs(getContext()); + final String key = name.flattenToString(); + boolean currentVal = sp.getBoolean(name.flattenToString(), false); + if (currentVal) { + sp.edit().remove(key).apply(); + } else { + sp.edit().putBoolean(key, true).apply(); + } + + // Force the chooser to requery and resort things + getActivity().recreate(); + break; case APP_INFO_INDEX: Intent in = new Intent().setAction(Settings.ACTION_APPLICATION_DETAILS_SETTINGS) .setData(Uri.fromParts("package", name.getPackageName(), null)) diff --git a/core/java/com/android/internal/compat/IPlatformCompat.aidl b/core/java/com/android/internal/compat/IPlatformCompat.aidl index 045a6800bb13..7dcb12c9e72b 100644 --- a/core/java/com/android/internal/compat/IPlatformCompat.aidl +++ b/core/java/com/android/internal/compat/IPlatformCompat.aidl @@ -171,6 +171,15 @@ interface IPlatformCompat void clearOverrides(in String packageName); /** + * Revert overrides to compatibility changes. Doesn't kill the app, to be only used in tests. + * + * @param packageName The package name of the app whose overrides will be cleared. + * + */ + void clearOverridesForTest(in String packageName); + + + /** * Get configs for an application. * * @param appInfo The application whose config will be returned. diff --git a/core/java/com/android/internal/config/sysui/SystemUiDeviceConfigFlags.java b/core/java/com/android/internal/config/sysui/SystemUiDeviceConfigFlags.java index 33b532537686..334e61a24193 100644 --- a/core/java/com/android/internal/config/sysui/SystemUiDeviceConfigFlags.java +++ b/core/java/com/android/internal/config/sysui/SystemUiDeviceConfigFlags.java @@ -47,6 +47,20 @@ public final class SystemUiDeviceConfigFlags { */ public static final String NAS_MAX_SUGGESTIONS = "nas_max_suggestions"; + // Flags related to screenshot intelligence + + /** + * (bool) Whether to enable smart actions in screenshot notifications. + */ + public static final String ENABLE_SCREENSHOT_NOTIFICATION_SMART_ACTIONS = + "enable_screenshot_notification_smart_actions"; + + /** + * (int) Timeout value in ms to get smart actions for screenshot notification. + */ + public static final String SCREENSHOT_NOTIFICATION_SMART_ACTIONS_TIMEOUT_MS = + "screenshot_notification_smart_actions_timeout_ms"; + // Flags related to Smart Suggestions - these are read in SmartReplyConstants. /** (boolean) Whether to enable smart suggestions in notifications. */ diff --git a/telephony/java/com/android/internal/telephony/IOnSubscriptionsChangedListener.aidl b/core/java/com/android/internal/telephony/IOnSubscriptionsChangedListener.aidl index 493b1ff6aba7..493b1ff6aba7 100644 --- a/telephony/java/com/android/internal/telephony/IOnSubscriptionsChangedListener.aidl +++ b/core/java/com/android/internal/telephony/IOnSubscriptionsChangedListener.aidl diff --git a/telephony/java/com/android/internal/telephony/IPhoneStateListener.aidl b/core/java/com/android/internal/telephony/IPhoneStateListener.aidl index 90019eef62fd..084a3cc64a35 100644 --- a/telephony/java/com/android/internal/telephony/IPhoneStateListener.aidl +++ b/core/java/com/android/internal/telephony/IPhoneStateListener.aidl @@ -29,6 +29,9 @@ import android.telephony.SignalStrength; import android.telephony.emergency.EmergencyNumber; import android.telephony.ims.ImsReasonInfo; +/** + * {@hide} + */ oneway interface IPhoneStateListener { void onServiceStateChanged(in ServiceState serviceState); void onSignalStrengthChanged(int asu); diff --git a/telephony/java/com/android/internal/telephony/ITelephonyRegistry.aidl b/core/java/com/android/internal/telephony/ITelephonyRegistry.aidl index d7a7af1d530f..d7a7af1d530f 100644 --- a/telephony/java/com/android/internal/telephony/ITelephonyRegistry.aidl +++ b/core/java/com/android/internal/telephony/ITelephonyRegistry.aidl diff --git a/core/jni/AndroidRuntime.cpp b/core/jni/AndroidRuntime.cpp index 9a90555041d7..9e4c2220576c 100644 --- a/core/jni/AndroidRuntime.cpp +++ b/core/jni/AndroidRuntime.cpp @@ -667,6 +667,8 @@ int AndroidRuntime::startVm(JavaVM** pJavaVM, JNIEnv** pEnv, bool zygote, bool p char dex2oatImageCompilerFilterBuf[sizeof("--compiler-filter=")-1 + PROPERTY_VALUE_MAX]; char dex2oatThreadsBuf[sizeof("-j")-1 + PROPERTY_VALUE_MAX]; char dex2oatThreadsImageBuf[sizeof("-j")-1 + PROPERTY_VALUE_MAX]; + char dex2oatCpuSetBuf[sizeof("--cpu-set=")-1 + PROPERTY_VALUE_MAX]; + char dex2oatCpuSetImageBuf[sizeof("--cpu-set=")-1 + PROPERTY_VALUE_MAX]; char dex2oat_isa_variant_key[PROPERTY_KEY_MAX]; char dex2oat_isa_variant[sizeof("--instruction-set-variant=") -1 + PROPERTY_VALUE_MAX]; char dex2oat_isa_features_key[PROPERTY_KEY_MAX]; @@ -956,6 +958,10 @@ int AndroidRuntime::startVm(JavaVM** pJavaVM, JNIEnv** pEnv, bool zygote, bool p parseCompilerOption("dalvik.vm.dex2oat-threads", dex2oatThreadsBuf, "-j", "-Xcompiler-option"); parseCompilerOption("dalvik.vm.image-dex2oat-threads", dex2oatThreadsImageBuf, "-j", "-Ximage-compiler-option"); + parseCompilerOption("dalvik.vm.dex2oat-cpu-set", dex2oatCpuSetBuf, "--cpu-set=", + "-Xcompiler-option"); + parseCompilerOption("dalvik.vm.image-dex2oat-cpu-set", dex2oatCpuSetImageBuf, "--cpu-set=", + "-Ximage-compiler-option"); // The runtime will compile a boot image, when necessary, not using installd. Thus, we need to // pass the instruction-set-features/variant as an image-compiler-option. diff --git a/core/jni/core_jni_helpers.h b/core/jni/core_jni_helpers.h index 16ef753c0cd0..f03f42737134 100644 --- a/core/jni/core_jni_helpers.h +++ b/core/jni/core_jni_helpers.h @@ -22,6 +22,18 @@ #include <nativehelper/scoped_utf_chars.h> #include <android_runtime/AndroidRuntime.h> +// Host targets (layoutlib) do not differentiate between regular and critical native methods, +// and they need all the JNI methods to have JNIEnv* and jclass/jobject as their first two arguments. +// The following macro allows to have those arguments when compiling for host while omitting them when +// compiling for Android. +#ifdef __ANDROID__ +#define CRITICAL_JNI_PARAMS +#define CRITICAL_JNI_PARAMS_COMMA +#else +#define CRITICAL_JNI_PARAMS JNIEnv*, jclass +#define CRITICAL_JNI_PARAMS_COMMA JNIEnv*, jclass, +#endif + namespace android { // Defines some helpful functions. diff --git a/core/res/AndroidManifest.xml b/core/res/AndroidManifest.xml index 74b4f34ebbf4..216b87f89b90 100644 --- a/core/res/AndroidManifest.xml +++ b/core/res/AndroidManifest.xml @@ -108,6 +108,8 @@ <protected-broadcast android:name="android.app.action.ENTER_CAR_MODE" /> <protected-broadcast android:name="android.app.action.EXIT_CAR_MODE" /> + <protected-broadcast android:name="android.app.action.ENTER_CAR_MODE_PRIVILEGED" /> + <protected-broadcast android:name="android.app.action.EXIT_CAR_MODE_PRIVILEGED" /> <protected-broadcast android:name="android.app.action.ENTER_DESK_MODE" /> <protected-broadcast android:name="android.app.action.EXIT_DESK_MODE" /> <protected-broadcast android:name="android.app.action.NEXT_ALARM_CLOCK_CHANGED" /> @@ -4339,6 +4341,21 @@ it will be ignored. @hide --> <permission android:name="android.permission.MODIFY_DAY_NIGHT_MODE" + android:protectionLevel="signature|privileged" /> + + <!-- @SystemApi Allows entering or exiting car mode using a specified priority. + This permission is required to use UiModeManager while specifying a priority for the calling + app. A device manufacturer uses this permission to prioritize the apps which can + potentially request to enter car-mode on a device to help establish the correct behavior + where multiple such apps are active at the same time. + @hide --> + <permission android:name="android.permission.ENTER_CAR_MODE_PRIORITIZED" + android:protectionLevel="signature|privileged" /> + + <!-- @SystemApi Required to receive ACTION_ENTER_CAR_MODE_PRIVILEGED or + ACTION_EXIT_CAR_MODE_PRIVILEGED. + @hide --> + <permission android:name="android.permission.HANDLE_CAR_MODE_CHANGES" android:protectionLevel="signature|privileged" /> <!-- The system process is explicitly the only one allowed to launch the diff --git a/core/res/res/values/arrays.xml b/core/res/res/values/arrays.xml index f05898561b8a..dca9c7234e13 100644 --- a/core/res/res/values/arrays.xml +++ b/core/res/res/values/arrays.xml @@ -175,7 +175,15 @@ </array> <!-- Used in ResolverTargetActionsDialogFragment --> - <string-array name="resolver_target_actions"> + + <!-- Used in ResolverTargetActionsDialogFragment --> + <string-array name="resolver_target_actions_pin"> + <item>@string/pin_target</item> + <item>@string/app_info</item> + </string-array> + + <string-array name="resolver_target_actions_unpin"> + <item>@string/unpin_target</item> <item>@string/app_info</item> </string-array> diff --git a/core/res/res/values/config.xml b/core/res/res/values/config.xml index 302b6f32f712..f867594b7c2a 100644 --- a/core/res/res/values/config.xml +++ b/core/res/res/values/config.xml @@ -529,7 +529,7 @@ - TYPE_ETHERNET (9) is prepended to this list, and - - the return value of TelephonyManager.isTetherApnRequired() + - the return value of TelephonyManager.isTetheringApnRequired() determines how the array is further modified: * TRUE (DUN REQUIRED). diff --git a/core/res/res/values/strings.xml b/core/res/res/values/strings.xml index d8ed39fa924a..75ae13586e97 100644 --- a/core/res/res/values/strings.xml +++ b/core/res/res/values/strings.xml @@ -5091,6 +5091,10 @@ <string name="usb_mtp_launch_notification_description">Tap to view files</string> <!-- Resolver target actions strings --> + <!-- Pin this app to the top of the Sharesheet app list. [CHAR LIMIT=60]--> + <string name="pin_target">Pin</string> + <!-- Un-pin this app in the Sharesheet, so that it is sorted normally. [CHAR LIMIT=60]--> + <string name="unpin_target">Unpin</string> <!-- View application info for a target. --> <string name="app_info">App info</string> diff --git a/core/res/res/values/symbols.xml b/core/res/res/values/symbols.xml index 0e57a26c618f..c331ca932767 100644 --- a/core/res/res/values/symbols.xml +++ b/core/res/res/values/symbols.xml @@ -3060,7 +3060,8 @@ <java-symbol type="color" name="notification_material_background_color" /> <!-- Resolver target actions --> - <java-symbol type="array" name="resolver_target_actions" /> + <java-symbol type="array" name="resolver_target_actions_pin" /> + <java-symbol type="array" name="resolver_target_actions_unpin" /> <java-symbol type="array" name="non_removable_euicc_slots" /> diff --git a/core/tests/coretests/src/com/android/internal/app/AbstractResolverComparatorTest.java b/core/tests/coretests/src/com/android/internal/app/AbstractResolverComparatorTest.java new file mode 100644 index 000000000000..36dd3e4e72b9 --- /dev/null +++ b/core/tests/coretests/src/com/android/internal/app/AbstractResolverComparatorTest.java @@ -0,0 +1,105 @@ +/* + * 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.app; + +import static junit.framework.Assert.assertEquals; + +import android.content.ComponentName; +import android.content.Context; +import android.content.Intent; +import android.content.pm.ActivityInfo; +import android.content.pm.ResolveInfo; +import android.os.Message; + +import androidx.test.InstrumentationRegistry; + +import org.junit.Test; + +import java.util.List; + +public class AbstractResolverComparatorTest { + + @Test + public void testPinned() { + ResolverActivity.ResolvedComponentInfo r1 = new ResolverActivity.ResolvedComponentInfo( + new ComponentName("package", "class"), new Intent(), new ResolveInfo() + ); + r1.setPinned(true); + + ResolverActivity.ResolvedComponentInfo r2 = new ResolverActivity.ResolvedComponentInfo( + new ComponentName("zackage", "zlass"), new Intent(), new ResolveInfo() + ); + + Context context = InstrumentationRegistry.getTargetContext(); + AbstractResolverComparator comparator = getTestComparator(context); + + assertEquals("Pinned ranks over unpinned", -1, comparator.compare(r1, r2)); + assertEquals("Unpinned ranks under pinned", 1, comparator.compare(r2, r1)); + } + + + @Test + public void testBothPinned() { + ResolveInfo pmInfo1 = new ResolveInfo(); + pmInfo1.activityInfo = new ActivityInfo(); + pmInfo1.activityInfo.packageName = "aaa"; + + ResolverActivity.ResolvedComponentInfo r1 = new ResolverActivity.ResolvedComponentInfo( + new ComponentName("package", "class"), new Intent(), pmInfo1); + r1.setPinned(true); + + ResolveInfo pmInfo2 = new ResolveInfo(); + pmInfo2.activityInfo = new ActivityInfo(); + pmInfo2.activityInfo.packageName = "zzz"; + ResolverActivity.ResolvedComponentInfo r2 = new ResolverActivity.ResolvedComponentInfo( + new ComponentName("zackage", "zlass"), new Intent(), pmInfo2); + r2.setPinned(true); + + Context context = InstrumentationRegistry.getTargetContext(); + AbstractResolverComparator comparator = getTestComparator(context); + + assertEquals("Both pinned should rank alphabetically", -1, comparator.compare(r1, r2)); + } + + private AbstractResolverComparator getTestComparator(Context context) { + Intent intent = new Intent(); + + AbstractResolverComparator testComparator = + new AbstractResolverComparator(context, intent) { + + @Override + int compare(ResolveInfo lhs, ResolveInfo rhs) { + // Used for testing pinning, so we should never get here --- the overrides should + // determine the result instead. + return 1; + } + + @Override + void doCompute(List<ResolverActivity.ResolvedComponentInfo> targets) {} + + @Override + float getScore(ComponentName name) { + return 0; + } + + @Override + void handleResultMessage(Message message) {} + }; + return testComparator; + } + +} diff --git a/data/etc/privapp-permissions-platform.xml b/data/etc/privapp-permissions-platform.xml index 80f3cc68221f..9064abf6ba36 100644 --- a/data/etc/privapp-permissions-platform.xml +++ b/data/etc/privapp-permissions-platform.xml @@ -238,6 +238,7 @@ applications that come with the platform <permission name="android.permission.BIND_CONNECTION_SERVICE"/> <permission name="android.permission.BIND_INCALL_SERVICE"/> <permission name="android.permission.CALL_PRIVILEGED"/> + <permission name="android.permission.HANDLE_CAR_MODE_CHANGES"/> <permission name="android.permission.INTERACT_ACROSS_USERS"/> <permission name="android.permission.MANAGE_USERS"/> <permission name="android.permission.MANAGE_ROLE_HOLDERS"/> @@ -328,6 +329,8 @@ applications that come with the platform <permission name="android.permission.REQUEST_NOTIFICATION_ASSISTANT_SERVICE" /> <!-- Permission required to test ExplicitHealthCheckServiceImpl. --> <permission name="android.permission.BIND_EXPLICIT_HEALTH_CHECK_SERVICE"/> + <!-- Permission required for UiModeManager cts test. --> + <permission name="android.permission.ENTER_CAR_MODE_PRIORITIZED"/> </privapp-permissions> <privapp-permissions package="com.android.statementservice"> diff --git a/keystore/java/android/security/KeyStore.java b/keystore/java/android/security/KeyStore.java index 646aa13664c4..c0a04220cfe7 100644 --- a/keystore/java/android/security/KeyStore.java +++ b/keystore/java/android/security/KeyStore.java @@ -348,16 +348,6 @@ public class KeyStore { return list(prefix, UID_SELF); } - @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P, trackingBug = 115609023) - public boolean reset() { - try { - return mBinder.reset() == NO_ERROR; - } catch (RemoteException e) { - Log.w(TAG, "Cannot connect to keystore", e); - return false; - } - } - /** * Attempt to lock the keystore for {@code user}. * @@ -922,15 +912,26 @@ public class KeyStore { } } - public OperationResult finish(IBinder token, KeymasterArguments arguments, byte[] signature, - byte[] entropy) { + /** + * Android KeyStore finish operation. + * + * @param token Authentication token. + * @param arguments Keymaster arguments + * @param input Optional additional input data. + * @param signature Optional signature to be verified. + * @param entropy Optional additional entropy + * @return OperationResult that will indicate success or error of the operation. + */ + public OperationResult finish(IBinder token, KeymasterArguments arguments, byte[] input, + byte[] signature, byte[] entropy) { OperationPromise promise = new OperationPromise(); try { mBinder.asBinder().linkToDeath(promise, 0); arguments = arguments != null ? arguments : new KeymasterArguments(); entropy = entropy != null ? entropy : new byte[0]; + input = input != null ? input : new byte[0]; signature = signature != null ? signature : new byte[0]; - int errorCode = mBinder.finish(promise, token, arguments, signature, entropy); + int errorCode = mBinder.finish(promise, token, arguments, input, signature, entropy); if (errorCode == NO_ERROR) { return promise.getFuture().get(); } else { @@ -948,7 +949,7 @@ public class KeyStore { } public OperationResult finish(IBinder token, KeymasterArguments arguments, byte[] signature) { - return finish(token, arguments, signature, null); + return finish(token, arguments, null, signature, null); } private class KeystoreResultPromise diff --git a/keystore/java/android/security/keystore/AndroidKeyStoreAuthenticatedAESCipherSpi.java b/keystore/java/android/security/keystore/AndroidKeyStoreAuthenticatedAESCipherSpi.java index 441ee660b53a..c6515efd2c61 100644 --- a/keystore/java/android/security/keystore/AndroidKeyStoreAuthenticatedAESCipherSpi.java +++ b/keystore/java/android/security/keystore/AndroidKeyStoreAuthenticatedAESCipherSpi.java @@ -432,7 +432,7 @@ abstract class AndroidKeyStoreAuthenticatedAESCipherSpi extends AndroidKeyStoreC } @Override - public OperationResult finish(byte[] signature, byte[] additionalEntropy) { + public OperationResult finish(byte[] input, byte[] signature, byte[] additionalEntropy) { if ((additionalEntropy != null) && (additionalEntropy.length > 0)) { throw new ProviderException("AAD stream does not support additional entropy"); } diff --git a/keystore/java/android/security/keystore/AndroidKeyStoreKeyGeneratorSpi.java b/keystore/java/android/security/keystore/AndroidKeyStoreKeyGeneratorSpi.java index 3dc884eb38ad..17aacb9756aa 100644 --- a/keystore/java/android/security/keystore/AndroidKeyStoreKeyGeneratorSpi.java +++ b/keystore/java/android/security/keystore/AndroidKeyStoreKeyGeneratorSpi.java @@ -210,13 +210,9 @@ public abstract class AndroidKeyStoreKeyGeneratorSpi extends KeyGeneratorSpi { } } if (mKeymasterAlgorithm == KeymasterDefs.KM_ALGORITHM_HMAC) { - if (mKeySizeBits < 64) { + if (mKeySizeBits < 64 || mKeySizeBits > 512) { throw new InvalidAlgorithmParameterException( - "HMAC key size must be at least 64 bits."); - } - if (mKeySizeBits > 512 && spec.isStrongBoxBacked()) { - throw new InvalidAlgorithmParameterException( - "StrongBox HMAC key size must be smaller than 512 bits."); + "HMAC key sizes must be within 64-512 bits, inclusive."); } // JCA HMAC key algorithm implies a digest (e.g., HmacSHA256 key algorithm diff --git a/keystore/java/android/security/keystore/KeyStoreCryptoOperationChunkedStreamer.java b/keystore/java/android/security/keystore/KeyStoreCryptoOperationChunkedStreamer.java index e0304787142d..75bea26aecef 100644 --- a/keystore/java/android/security/keystore/KeyStoreCryptoOperationChunkedStreamer.java +++ b/keystore/java/android/security/keystore/KeyStoreCryptoOperationChunkedStreamer.java @@ -62,7 +62,7 @@ class KeyStoreCryptoOperationChunkedStreamer implements KeyStoreCryptoOperationS * Returns the result of the KeyStore {@code finish} operation or null if keystore couldn't * be reached. */ - OperationResult finish(byte[] siganture, byte[] additionalEntropy); + OperationResult finish(byte[] input, byte[] siganture, byte[] additionalEntropy); } // Binder buffer is about 1MB, but it's shared between all active transactions of the process. @@ -217,7 +217,8 @@ class KeyStoreCryptoOperationChunkedStreamer implements KeyStoreCryptoOperationS byte[] output = update(input, inputOffset, inputLength); output = ArrayUtils.concat(output, flush()); - OperationResult opResult = mKeyStoreStream.finish(signature, additionalEntropy); + OperationResult opResult = mKeyStoreStream.finish(EmptyArray.BYTE, signature, + additionalEntropy); if (opResult == null) { throw new KeyStoreConnectException(); } else if (opResult.resultCode != KeyStore.NO_ERROR) { @@ -334,8 +335,8 @@ class KeyStoreCryptoOperationChunkedStreamer implements KeyStoreCryptoOperationS } @Override - public OperationResult finish(byte[] signature, byte[] additionalEntropy) { - return mKeyStore.finish(mOperationToken, null, signature, additionalEntropy); + public OperationResult finish(byte[] input, byte[] signature, byte[] additionalEntropy) { + return mKeyStore.finish(mOperationToken, null, input, signature, additionalEntropy); } } } diff --git a/media/jni/android_media_MediaDataSource.cpp b/media/jni/android_media_MediaDataSource.cpp index 8c38d887f82b..84a0e0d032d9 100644 --- a/media/jni/android_media_MediaDataSource.cpp +++ b/media/jni/android_media_MediaDataSource.cpp @@ -26,7 +26,6 @@ #include <nativehelper/JNIHelp.h> #include <binder/MemoryDealer.h> -#include <drm/drm_framework_common.h> #include <media/stagefright/foundation/ADebug.h> #include <nativehelper/ScopedLocalRef.h> @@ -160,8 +159,4 @@ String8 JMediaDataSource::toString() { return String8::format("JMediaDataSource(pid %d, uid %d)", getpid(), getuid()); } -sp<DecryptHandle> JMediaDataSource::DrmInitialization(const char * /* mime */) { - return NULL; -} - } // namespace android diff --git a/media/jni/android_media_MediaDataSource.h b/media/jni/android_media_MediaDataSource.h index 39405d2db579..378baf433fed 100644 --- a/media/jni/android_media_MediaDataSource.h +++ b/media/jni/android_media_MediaDataSource.h @@ -47,7 +47,6 @@ public: virtual void close(); virtual uint32_t getFlags(); virtual String8 toString(); - virtual sp<DecryptHandle> DrmInitialization(const char *mime); private: // Protect all member variables with mLock because this object will be diff --git a/packages/Shell/AndroidManifest.xml b/packages/Shell/AndroidManifest.xml index 047ac5984fe3..13fc881624ec 100644 --- a/packages/Shell/AndroidManifest.xml +++ b/packages/Shell/AndroidManifest.xml @@ -207,6 +207,9 @@ <!-- Permission required for CTS test - CrossProfileAppsHostSideTest --> <uses-permission android:name="android.permission.INTERACT_ACROSS_PROFILES"/> + <!-- Permission requried for CTS test - UiModeManagerTest --> + <uses-permission android:name="android.permission.ENTER_CAR_MODE_PRIORITIZED"/> + <application android:label="@string/app_label" android:theme="@android:style/Theme.DeviceDefault.DayNight" android:defaultToDeviceProtectedStorage="true" diff --git a/packages/SystemUI/AndroidManifest.xml b/packages/SystemUI/AndroidManifest.xml index 403e894a68e4..43fec6b6805c 100644 --- a/packages/SystemUI/AndroidManifest.xml +++ b/packages/SystemUI/AndroidManifest.xml @@ -60,6 +60,9 @@ <uses-permission android:name="android.permission.GET_APP_OPS_STATS" /> <uses-permission android:name="android.permission.USE_RESERVED_DISK" /> + <!-- to invoke ContentSuggestionsService --> + <uses-permission android:name="android.permission.MANAGE_CONTENT_SUGGESTIONS"/> + <!-- Networking and telephony --> <uses-permission android:name="android.permission.BLUETOOTH" /> <uses-permission android:name="android.permission.BLUETOOTH_ADMIN" /> diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardSecurityContainer.java b/packages/SystemUI/src/com/android/keyguard/KeyguardSecurityContainer.java index ca7cd0d666ad..ace24a3b7ce1 100644 --- a/packages/SystemUI/src/com/android/keyguard/KeyguardSecurityContainer.java +++ b/packages/SystemUI/src/com/android/keyguard/KeyguardSecurityContainer.java @@ -44,6 +44,7 @@ import com.android.internal.logging.MetricsLogger; import com.android.internal.logging.nano.MetricsProto.MetricsEvent; import com.android.internal.widget.LockPatternUtils; import com.android.keyguard.KeyguardSecurityModel.SecurityMode; +import com.android.settingslib.utils.ThreadUtils; import com.android.systemui.Dependency; import com.android.systemui.R; import com.android.systemui.SystemUIFactory; @@ -615,6 +616,15 @@ public class KeyguardSecurityContainer extends FrameLayout implements KeyguardSe StatsLog.write(StatsLog.KEYGUARD_BOUNCER_PASSWORD_ENTERED, StatsLog.KEYGUARD_BOUNCER_PASSWORD_ENTERED__RESULT__SUCCESS); mLockPatternUtils.reportSuccessfulPasswordAttempt(userId); + // Force a garbage collection in an attempt to erase any lockscreen password left in + // memory. Do it asynchronously with a 5-sec delay to avoid making the keyguard + // dismiss animation janky. + ThreadUtils.postOnBackgroundThread(() -> { + try { + Thread.sleep(5000); + } catch (InterruptedException ignored) { } + Runtime.getRuntime().gc(); + }); } else { StatsLog.write(StatsLog.KEYGUARD_BOUNCER_PASSWORD_ENTERED, StatsLog.KEYGUARD_BOUNCER_PASSWORD_ENTERED__RESULT__FAILURE); diff --git a/packages/SystemUI/src/com/android/systemui/SystemUIFactory.java b/packages/SystemUI/src/com/android/systemui/SystemUIFactory.java index 0899d955a1ac..4fc6a36550dc 100644 --- a/packages/SystemUI/src/com/android/systemui/SystemUIFactory.java +++ b/packages/SystemUI/src/com/android/systemui/SystemUIFactory.java @@ -31,6 +31,7 @@ import com.android.keyguard.ViewMediatorCallback; import com.android.systemui.keyguard.DismissCallbackRegistry; import com.android.systemui.plugins.FalsingManager; import com.android.systemui.plugins.statusbar.StatusBarStateController; +import com.android.systemui.screenshot.ScreenshotNotificationSmartActionsProvider; import com.android.systemui.statusbar.KeyguardIndicationController; import com.android.systemui.statusbar.NotificationMediaManager; import com.android.systemui.statusbar.ScrimView; @@ -110,6 +111,15 @@ public class SystemUIFactory { return new StatusBarKeyguardViewManager(context, viewMediatorCallback, lockPatternUtils); } + /** + * Creates an instance of ScreenshotNotificationSmartActionsProvider. + * This method is overridden in vendor specific implementation of Sys UI. + */ + public ScreenshotNotificationSmartActionsProvider + createScreenshotNotificationSmartActionsProvider() { + return new ScreenshotNotificationSmartActionsProvider(); + } + public KeyguardBouncer createKeyguardBouncer(Context context, ViewMediatorCallback callback, LockPatternUtils lockPatternUtils, ViewGroup container, DismissCallbackRegistry dismissCallbackRegistry, diff --git a/packages/SystemUI/src/com/android/systemui/screenshot/GlobalScreenshot.java b/packages/SystemUI/src/com/android/systemui/screenshot/GlobalScreenshot.java index 7bb41034d856..f7a26a8ff8d6 100644 --- a/packages/SystemUI/src/com/android/systemui/screenshot/GlobalScreenshot.java +++ b/packages/SystemUI/src/com/android/systemui/screenshot/GlobalScreenshot.java @@ -17,6 +17,7 @@ package com.android.systemui.screenshot; import static android.content.Context.NOTIFICATION_SERVICE; +import static android.os.AsyncTask.THREAD_POOL_EXECUTOR; import static android.view.WindowManager.LayoutParams.LAYOUT_IN_DISPLAY_CUTOUT_MODE_ALWAYS; import static com.android.systemui.screenshot.GlobalScreenshot.EXTRA_ACTION_INTENT; @@ -29,7 +30,9 @@ import android.animation.AnimatorListenerAdapter; import android.animation.AnimatorSet; import android.animation.ValueAnimator; import android.animation.ValueAnimator.AnimatorUpdateListener; +import android.app.ActivityManager; import android.app.ActivityOptions; +import android.app.ActivityTaskManager; import android.app.Notification; import android.app.Notification.BigPictureStyle; import android.app.NotificationManager; @@ -42,6 +45,7 @@ import android.content.ComponentName; import android.content.ContentResolver; import android.content.Context; import android.content.Intent; +import android.content.pm.UserInfo; import android.content.res.Configuration; import android.content.res.Resources; import android.graphics.Bitmap; @@ -59,9 +63,14 @@ import android.media.MediaActionSound; import android.net.Uri; import android.os.AsyncTask; import android.os.Environment; +import android.os.Handler; import android.os.PowerManager; import android.os.Process; +import android.os.RemoteException; +import android.os.SystemClock; import android.os.UserHandle; +import android.os.UserManager; +import android.provider.DeviceConfig; import android.provider.MediaStore; import android.text.TextUtils; import android.util.DisplayMetrics; @@ -77,10 +86,13 @@ import android.view.animation.Interpolator; import android.widget.ImageView; import android.widget.Toast; +import com.android.internal.annotations.VisibleForTesting; +import com.android.internal.config.sysui.SystemUiDeviceConfigFlags; import com.android.internal.messages.nano.SystemMessageProto.SystemMessage; import com.android.systemui.R; import com.android.systemui.SysUiServiceProvider; import com.android.systemui.SystemUI; +import com.android.systemui.SystemUIFactory; import com.android.systemui.shared.system.ActivityManagerWrapper; import com.android.systemui.statusbar.phone.StatusBar; import com.android.systemui.util.NotificationChannels; @@ -91,7 +103,10 @@ import java.io.IOException; import java.io.OutputStream; import java.text.DateFormat; import java.text.SimpleDateFormat; +import java.util.Collections; import java.util.Date; +import java.util.List; +import java.util.concurrent.CompletableFuture; import java.util.concurrent.ExecutionException; import java.util.concurrent.TimeUnit; import java.util.concurrent.TimeoutException; @@ -138,6 +153,8 @@ class SaveImageInBackgroundTask extends AsyncTask<Void, Void, Void> { private final BigPictureStyle mNotificationStyle; private final int mImageWidth; private final int mImageHeight; + private final Handler mHandler; + private final ScreenshotNotificationSmartActionsProvider mSmartActionsProvider; SaveImageInBackgroundTask(Context context, SaveImageInBackgroundData data, NotificationManager nManager) { @@ -149,6 +166,11 @@ class SaveImageInBackgroundTask extends AsyncTask<Void, Void, Void> { String imageDate = new SimpleDateFormat("yyyyMMdd-HHmmss").format(new Date(mImageTime)); mImageFileName = String.format(SCREENSHOT_FILE_NAME_TEMPLATE, imageDate); + // Initialize screenshot notification smart actions provider. + mHandler = new Handler(); + mSmartActionsProvider = + SystemUIFactory.getInstance().createScreenshotNotificationSmartActionsProvider(); + // Create the large notification icon mImageWidth = data.image.getWidth(); mImageHeight = data.image.getHeight(); @@ -222,6 +244,23 @@ class SaveImageInBackgroundTask extends AsyncTask<Void, Void, Void> { mNotificationStyle.bigLargeIcon((Bitmap) null); } + private int getUserHandleOfForegroundApplication(Context context) { + // This logic matches + // com.android.systemui.statusbar.phone.PhoneStatusBarPolicy#updateManagedProfile + try { + return ActivityTaskManager.getService().getLastResumedActivityUserId(); + } catch (RemoteException e) { + Slog.w(TAG, "getUserHandleOfForegroundApplication: ", e); + return context.getUserId(); + } + } + + private boolean isManagedProfile(Context context) { + UserManager manager = UserManager.get(context); + UserInfo info = manager.getUserInfo(getUserHandleOfForegroundApplication(context)); + return info.isManagedProfile(); + } + /** * Generates a new hardware bitmap with specified values, copying the content from the passed * in bitmap. @@ -248,6 +287,12 @@ class SaveImageInBackgroundTask extends AsyncTask<Void, Void, Void> { Context context = mParams.context; Bitmap image = mParams.image; + boolean smartActionsEnabled = DeviceConfig.getBoolean(DeviceConfig.NAMESPACE_SYSTEMUI, + SystemUiDeviceConfigFlags.ENABLE_SCREENSHOT_NOTIFICATION_SMART_ACTIONS, false); + CompletableFuture<List<Notification.Action>> smartActionsFuture = + GlobalScreenshot.getSmartActionsFuture( + context, image, mSmartActionsProvider, mHandler, smartActionsEnabled, + isManagedProfile(context)); Resources r = context.getResources(); try { @@ -348,6 +393,19 @@ class SaveImageInBackgroundTask extends AsyncTask<Void, Void, Void> { mParams.imageUri = uri; mParams.image = null; mParams.errorMsgResId = 0; + + if (smartActionsEnabled) { + int timeoutMs = DeviceConfig.getInt(DeviceConfig.NAMESPACE_SYSTEMUI, + SystemUiDeviceConfigFlags + .SCREENSHOT_NOTIFICATION_SMART_ACTIONS_TIMEOUT_MS, + 1000); + List<Notification.Action> smartActions = GlobalScreenshot.getSmartActions( + smartActionsFuture, + timeoutMs); + for (Notification.Action action : smartActions) { + mNotificationBuilder.addAction(action); + } + } } catch (Exception e) { // IOException/UnsupportedOperationException may be thrown if external storage is not // mounted @@ -904,6 +962,58 @@ class GlobalScreenshot { nManager.notify(SystemMessage.NOTE_GLOBAL_SCREENSHOT, n); } + @VisibleForTesting + static CompletableFuture<List<Notification.Action>> getSmartActionsFuture(Context context, + Bitmap image, ScreenshotNotificationSmartActionsProvider smartActionsProvider, + Handler handler, boolean smartActionsEnabled, boolean isManagedProfile) { + if (!smartActionsEnabled) { + Slog.i(TAG, "Screenshot Intelligence not enabled, returning empty list."); + return CompletableFuture.completedFuture(Collections.emptyList()); + } + if (image.getConfig() != Bitmap.Config.HARDWARE) { + Slog.w(TAG, String.format( + "Bitmap expected: Hardware, Bitmap found: %s. Returning empty list.", + image.getConfig())); + return CompletableFuture.completedFuture(Collections.emptyList()); + } + + Slog.d(TAG, "Screenshot from a managed profile: " + isManagedProfile); + CompletableFuture<List<Notification.Action>> smartActionsFuture; + try { + ActivityManager.RunningTaskInfo runningTask = + ActivityManagerWrapper.getInstance().getRunningTask(); + ComponentName componentName = + (runningTask != null && runningTask.topActivity != null) + ? runningTask.topActivity + : new ComponentName("", ""); + smartActionsFuture = smartActionsProvider.getActions(image, context, + THREAD_POOL_EXECUTOR, + handler, + componentName, + isManagedProfile); + } catch (Throwable e) { + smartActionsFuture = CompletableFuture.completedFuture(Collections.emptyList()); + Slog.e(TAG, "Failed to get future for screenshot notification smart actions.", e); + } + return smartActionsFuture; + } + + @VisibleForTesting + static List<Notification.Action> getSmartActions( + CompletableFuture<List<Notification.Action>> smartActionsFuture, int timeoutMs) { + try { + long startTimeMs = SystemClock.uptimeMillis(); + List<Notification.Action> actions = smartActionsFuture.get(timeoutMs, + TimeUnit.MILLISECONDS); + Slog.d(TAG, String.format("Wait time for smart actions: %d ms", + SystemClock.uptimeMillis() - startTimeMs)); + return actions; + } catch (Throwable e) { + Slog.e(TAG, "Failed to obtain screenshot notification smart actions.", e); + return Collections.emptyList(); + } + } + /** * Receiver to proxy the share or edit intent, used to clean up the notification and send * appropriate signals to the system (ie. to dismiss the keyguard if necessary). diff --git a/packages/SystemUI/src/com/android/systemui/screenshot/ScreenshotNotificationSmartActionsProvider.java b/packages/SystemUI/src/com/android/systemui/screenshot/ScreenshotNotificationSmartActionsProvider.java new file mode 100644 index 000000000000..fa23bf7d5bde --- /dev/null +++ b/packages/SystemUI/src/com/android/systemui/screenshot/ScreenshotNotificationSmartActionsProvider.java @@ -0,0 +1,58 @@ +/* + * 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.systemui.screenshot; + +import android.app.Notification; +import android.content.ComponentName; +import android.content.Context; +import android.graphics.Bitmap; +import android.os.Handler; +import android.util.Log; + +import java.util.Collections; +import java.util.List; +import java.util.concurrent.CompletableFuture; +import java.util.concurrent.Executor; + +/** + * This class can be overridden by a vendor-specific sys UI implementation, + * in order to provide smart actions in the screenshot notification. + */ +public class ScreenshotNotificationSmartActionsProvider { + private static final String TAG = "ScreenshotActions"; + + /** + * Default implementation that returns an empty list. + * This method is overridden in vendor-specific Sys UI implementation. + * + * @param bitmap The bitmap of the screenshot. The bitmap config must be {@link + * HARDWARE}. + * @param context The current app {@link Context}. + * @param executor A {@link Executor} that can be used to execute tasks in parallel. + * @param handler A {@link Handler} to possibly run UI-thread code. + * @param componentName Contains package and activity class names where the screenshot was + * taken. This is used as an additional signal to generate and rank more + * relevant actions. + * @param isManagedProfile The screenshot was taken for a work profile app. + */ + public CompletableFuture<List<Notification.Action>> getActions(Bitmap bitmap, Context context, + Executor executor, Handler handler, ComponentName componentName, + boolean isManagedProfile) { + Log.d(TAG, "Returning empty smart action list."); + return CompletableFuture.completedFuture(Collections.emptyList()); + } +} diff --git a/packages/SystemUI/tests/src/com/android/systemui/screenshot/ScreenshotNotificationSmartActionsTest.java b/packages/SystemUI/tests/src/com/android/systemui/screenshot/ScreenshotNotificationSmartActionsTest.java new file mode 100644 index 000000000000..02e5515d3ecc --- /dev/null +++ b/packages/SystemUI/tests/src/com/android/systemui/screenshot/ScreenshotNotificationSmartActionsTest.java @@ -0,0 +1,143 @@ +/* + * 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.systemui.screenshot; + +import static org.mockito.ArgumentMatchers.any; +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 android.app.Notification; +import android.graphics.Bitmap; +import android.os.Handler; +import android.testing.AndroidTestingRunner; + +import androidx.test.filters.SmallTest; + +import com.android.systemui.SystemUIFactory; +import com.android.systemui.SysuiTestCase; + +import org.junit.Assert; +import org.junit.Before; +import org.junit.Test; +import org.junit.runner.RunWith; + +import java.util.Collections; +import java.util.List; +import java.util.concurrent.CompletableFuture; +import java.util.concurrent.TimeUnit; + +/** + * Tests for exception handling and bitmap configuration in adding smart actions to Screenshot + * Notification. + */ +@SmallTest +@RunWith(AndroidTestingRunner.class) +public class ScreenshotNotificationSmartActionsTest extends SysuiTestCase { + private ScreenshotNotificationSmartActionsProvider mSmartActionsProvider; + private Handler mHandler; + + @Before + public void setup() { + mSmartActionsProvider = mock( + ScreenshotNotificationSmartActionsProvider.class); + mHandler = mock(Handler.class); + } + + // Tests any exception thrown in getting smart actions future does not affect regular + // screenshot flow. + @Test + public void testExceptionHandlingInGetSmartActionsFuture() + throws Exception { + Bitmap bitmap = mock(Bitmap.class); + when(bitmap.getConfig()).thenReturn(Bitmap.Config.HARDWARE); + ScreenshotNotificationSmartActionsProvider smartActionsProvider = mock( + ScreenshotNotificationSmartActionsProvider.class); + when(smartActionsProvider.getActions(any(), any(), any(), any(), any(), + eq(false))).thenThrow( + RuntimeException.class); + CompletableFuture<List<Notification.Action>> smartActionsFuture = + GlobalScreenshot.getSmartActionsFuture(mContext, bitmap, + smartActionsProvider, mHandler, true, false); + Assert.assertNotNull(smartActionsFuture); + List<Notification.Action> smartActions = smartActionsFuture.get(5, TimeUnit.MILLISECONDS); + Assert.assertEquals(Collections.emptyList(), smartActions); + } + + // Tests any exception thrown in waiting for smart actions future to complete does + // not affect regular screenshot flow. + @Test + public void testExceptionHandlingInGetSmartActions() + throws Exception { + CompletableFuture<List<Notification.Action>> smartActionsFuture = mock( + CompletableFuture.class); + int timeoutMs = 1000; + when(smartActionsFuture.get(timeoutMs, TimeUnit.MILLISECONDS)).thenThrow( + RuntimeException.class); + List<Notification.Action> actions = GlobalScreenshot.getSmartActions( + smartActionsFuture, timeoutMs); + Assert.assertEquals(Collections.emptyList(), actions); + } + + // Tests for a non-hardware bitmap, ScreenshotNotificationSmartActionsProvider is never invoked + // and a completed future is returned. + @Test + public void testUnsupportedBitmapConfiguration() + throws Exception { + Bitmap bitmap = mock(Bitmap.class); + when(bitmap.getConfig()).thenReturn(Bitmap.Config.RGB_565); + CompletableFuture<List<Notification.Action>> smartActionsFuture = + GlobalScreenshot.getSmartActionsFuture(mContext, bitmap, + mSmartActionsProvider, mHandler, true, true); + verify(mSmartActionsProvider, never()).getActions(any(), any(), any(), any(), any(), + eq(false)); + Assert.assertNotNull(smartActionsFuture); + List<Notification.Action> smartActions = smartActionsFuture.get(5, TimeUnit.MILLISECONDS); + Assert.assertEquals(Collections.emptyList(), smartActions); + } + + // Tests for a hardware bitmap, ScreenshotNotificationSmartActionsProvider is invoked once. + @Test + public void testScreenshotNotificationSmartActionsProviderInvokedOnce() { + Bitmap bitmap = mock(Bitmap.class); + when(bitmap.getConfig()).thenReturn(Bitmap.Config.HARDWARE); + GlobalScreenshot.getSmartActionsFuture(mContext, bitmap, mSmartActionsProvider, + mHandler, true, true); + verify(mSmartActionsProvider, times(1)) + .getActions(any(), any(), any(), any(), any(), eq(true)); + } + + // Tests for a hardware bitmap, a completed future is returned. + @Test + public void testSupportedBitmapConfiguration() + throws Exception { + Bitmap bitmap = mock(Bitmap.class); + when(bitmap.getConfig()).thenReturn(Bitmap.Config.HARDWARE); + ScreenshotNotificationSmartActionsProvider actionsProvider = + SystemUIFactory.getInstance().createScreenshotNotificationSmartActionsProvider(); + CompletableFuture<List<Notification.Action>> smartActionsFuture = + GlobalScreenshot.getSmartActionsFuture(mContext, bitmap, + actionsProvider, + mHandler, true, true); + Assert.assertNotNull(smartActionsFuture); + List<Notification.Action> smartActions = smartActionsFuture.get(5, TimeUnit.MILLISECONDS); + Assert.assertEquals(smartActions.size(), 0); + } +} diff --git a/packages/Tethering/src/com/android/server/connectivity/tethering/TetheringConfiguration.java b/packages/Tethering/src/com/android/server/connectivity/tethering/TetheringConfiguration.java index cf0e3b2ddda7..ca9b1681bacc 100644 --- a/packages/Tethering/src/com/android/server/connectivity/tethering/TetheringConfiguration.java +++ b/packages/Tethering/src/com/android/server/connectivity/tethering/TetheringConfiguration.java @@ -251,7 +251,7 @@ public class TetheringConfiguration { /** Check whether dun is required. */ public static boolean checkDunRequired(Context ctx, int id) { final TelephonyManager tm = (TelephonyManager) ctx.getSystemService(TELEPHONY_SERVICE); - return (tm != null) ? tm.isTetherApnRequired(id) : false; + return (tm != null) ? tm.isTetheringApnRequired(id) : false; } private static Collection<Integer> getUpstreamIfaceTypes(Resources res, boolean dunRequired) { diff --git a/packages/Tethering/tests/unit/src/com/android/server/connectivity/tethering/TetheringConfigurationTest.java b/packages/Tethering/tests/unit/src/com/android/server/connectivity/tethering/TetheringConfigurationTest.java index 9c65c0deed07..30bff3560955 100644 --- a/packages/Tethering/tests/unit/src/com/android/server/connectivity/tethering/TetheringConfigurationTest.java +++ b/packages/Tethering/tests/unit/src/com/android/server/connectivity/tethering/TetheringConfigurationTest.java @@ -145,7 +145,7 @@ public class TetheringConfigurationTest { @Test public void testDunFromTelephonyManagerMeansDun() { - when(mTelephonyManager.isTetherApnRequired(anyInt())).thenReturn(true); + when(mTelephonyManager.isTetheringApnRequired(anyInt())).thenReturn(true); final TetheringConfiguration cfgWifi = getTetheringConfiguration(TYPE_WIFI); final TetheringConfiguration cfgMobileWifiHipri = getTetheringConfiguration( @@ -169,7 +169,7 @@ public class TetheringConfigurationTest { @Test public void testDunNotRequiredFromTelephonyManagerMeansNoDun() { - when(mTelephonyManager.isTetherApnRequired(anyInt())).thenReturn(false); + when(mTelephonyManager.isTetheringApnRequired(anyInt())).thenReturn(false); final TetheringConfiguration cfgWifi = getTetheringConfiguration(TYPE_WIFI); final TetheringConfiguration cfgMobileWifiHipri = getTetheringConfiguration( @@ -212,7 +212,7 @@ public class TetheringConfigurationTest { @Test public void testNoDefinedUpstreamTypesAddsEthernet() { when(mResources.getIntArray(config_tether_upstream_types)).thenReturn(new int[]{}); - when(mTelephonyManager.isTetherApnRequired(anyInt())).thenReturn(false); + when(mTelephonyManager.isTetheringApnRequired(anyInt())).thenReturn(false); final TetheringConfiguration cfg = new TetheringConfiguration( mMockContext, mLog, INVALID_SUBSCRIPTION_ID); @@ -235,7 +235,7 @@ public class TetheringConfigurationTest { public void testDefinedUpstreamTypesSansEthernetAddsEthernet() { when(mResources.getIntArray(config_tether_upstream_types)).thenReturn( new int[]{TYPE_WIFI, TYPE_MOBILE_HIPRI}); - when(mTelephonyManager.isTetherApnRequired(anyInt())).thenReturn(false); + when(mTelephonyManager.isTetheringApnRequired(anyInt())).thenReturn(false); final TetheringConfiguration cfg = new TetheringConfiguration( mMockContext, mLog, INVALID_SUBSCRIPTION_ID); @@ -253,7 +253,7 @@ public class TetheringConfigurationTest { public void testDefinedUpstreamTypesWithEthernetDoesNotAddEthernet() { when(mResources.getIntArray(config_tether_upstream_types)) .thenReturn(new int[]{TYPE_WIFI, TYPE_ETHERNET, TYPE_MOBILE_HIPRI}); - when(mTelephonyManager.isTetherApnRequired(anyInt())).thenReturn(false); + when(mTelephonyManager.isTetheringApnRequired(anyInt())).thenReturn(false); final TetheringConfiguration cfg = new TetheringConfiguration( mMockContext, mLog, INVALID_SUBSCRIPTION_ID); diff --git a/services/contentsuggestions/java/com/android/server/contentsuggestions/ContentSuggestionsManagerService.java b/services/contentsuggestions/java/com/android/server/contentsuggestions/ContentSuggestionsManagerService.java index ecea251cc1ac..9cdb58d8c019 100644 --- a/services/contentsuggestions/java/com/android/server/contentsuggestions/ContentSuggestionsManagerService.java +++ b/services/contentsuggestions/java/com/android/server/contentsuggestions/ContentSuggestionsManagerService.java @@ -16,7 +16,6 @@ package com.android.server.contentsuggestions; -import static android.Manifest.permission.BIND_CONTENT_SUGGESTIONS_SERVICE; import static android.Manifest.permission.MANAGE_CONTENT_SUGGESTIONS; import static android.content.pm.PackageManager.PERMISSION_GRANTED; @@ -96,7 +95,7 @@ public class ContentSuggestionsManagerService extends private void enforceCaller(int userId, String func) { Context ctx = getContext(); - if (ctx.checkCallingPermission(BIND_CONTENT_SUGGESTIONS_SERVICE) == PERMISSION_GRANTED + if (ctx.checkCallingPermission(MANAGE_CONTENT_SUGGESTIONS) == PERMISSION_GRANTED || mServiceNameResolver.isTemporary(userId) || mActivityTaskManagerInternal.isCallerRecents(Binder.getCallingUid())) { return; diff --git a/services/contentsuggestions/java/com/android/server/contentsuggestions/ContentSuggestionsPerUserService.java b/services/contentsuggestions/java/com/android/server/contentsuggestions/ContentSuggestionsPerUserService.java index 06d9395cd7d6..7828050223f4 100644 --- a/services/contentsuggestions/java/com/android/server/contentsuggestions/ContentSuggestionsPerUserService.java +++ b/services/contentsuggestions/java/com/android/server/contentsuggestions/ContentSuggestionsPerUserService.java @@ -22,6 +22,7 @@ import android.annotation.Nullable; import android.app.ActivityManager; import android.app.AppGlobals; import android.app.contentsuggestions.ClassificationsRequest; +import android.app.contentsuggestions.ContentSuggestionsManager; import android.app.contentsuggestions.IClassificationsCallback; import android.app.contentsuggestions.ISelectionsCallback; import android.app.contentsuggestions.SelectionsRequest; @@ -97,15 +98,19 @@ public final class ContentSuggestionsPerUserService extends void provideContextImageLocked(int taskId, @NonNull Bundle imageContextRequestExtras) { RemoteContentSuggestionsService service = ensureRemoteServiceLocked(); if (service != null) { - ActivityManager.TaskSnapshot snapshot = - mActivityTaskManagerInternal.getTaskSnapshotNoRestore(taskId, false); GraphicBuffer snapshotBuffer = null; int colorSpaceId = 0; - if (snapshot != null) { - snapshotBuffer = snapshot.getSnapshot(); - ColorSpace colorSpace = snapshot.getColorSpace(); - if (colorSpace != null) { - colorSpaceId = colorSpace.getId(); + + // Skip taking TaskSnapshot when bitmap is provided. + if (!imageContextRequestExtras.containsKey(ContentSuggestionsManager.EXTRA_BITMAP)) { + ActivityManager.TaskSnapshot snapshot = + mActivityTaskManagerInternal.getTaskSnapshotNoRestore(taskId, false); + if (snapshot != null) { + snapshotBuffer = snapshot.getSnapshot(); + ColorSpace colorSpace = snapshot.getColorSpace(); + if (colorSpace != null) { + colorSpaceId = colorSpace.getId(); + } } } diff --git a/services/core/Android.bp b/services/core/Android.bp index a05c2cc31e40..084a74724ad8 100644 --- a/services/core/Android.bp +++ b/services/core/Android.bp @@ -32,6 +32,7 @@ java_library_static { "android.hardware.power-V1.0-java", "android.hardware.tv.cec-V1.0-java", "app-compat-annotations", + "vintf-vibrator-java", ], required: [ @@ -50,7 +51,6 @@ java_library_static { "android.hardware.biometrics.fingerprint-V2.1-java", "android.hardware.oemlock-V1.0-java", "android.hardware.tetheroffload.control-V1.0-java", - "android.hardware.vibrator-V1.4-java", "android.hardware.configstore-V1.0-java", "android.hardware.contexthub-V1.0-java", "android.hidl.manager-V1.2-java", diff --git a/services/core/java/com/android/server/BinderCallsStatsService.java b/services/core/java/com/android/server/BinderCallsStatsService.java index e51025943df4..f2ce444a8b90 100644 --- a/services/core/java/com/android/server/BinderCallsStatsService.java +++ b/services/core/java/com/android/server/BinderCallsStatsService.java @@ -19,6 +19,7 @@ package com.android.server; import static android.content.pm.PackageManager.MATCH_DIRECT_BOOT_AWARE; import static android.content.pm.PackageManager.MATCH_DIRECT_BOOT_UNAWARE; +import android.app.ActivityThread; import android.content.Context; import android.content.pm.PackageInfo; import android.content.pm.PackageManager; @@ -40,6 +41,7 @@ import com.android.internal.os.BackgroundThread; import com.android.internal.os.BinderCallsStats; import com.android.internal.os.BinderInternal; import com.android.internal.os.CachedDeviceState; +import com.android.internal.util.DumpUtils; import java.io.FileDescriptor; import java.io.PrintWriter; @@ -49,6 +51,7 @@ import java.util.List; public class BinderCallsStatsService extends Binder { private static final String TAG = "BinderCallsStatsService"; + private static final String SERVICE_NAME = "binder_calls_stats"; private static final String PERSIST_SYS_BINDER_CALLS_DETAILED_TRACKING = "persist.sys.binder_calls_detailed_tracking"; @@ -246,7 +249,7 @@ public class BinderCallsStatsService extends Binder { mService = new BinderCallsStatsService( mBinderCallsStats, mWorkSourceProvider); publishLocalService(Internal.class, new Internal(mBinderCallsStats)); - publishBinderService("binder_calls_stats", mService); + publishBinderService(SERVICE_NAME, mService); boolean detailedTrackingEnabled = SystemProperties.getBoolean( PERSIST_SYS_BINDER_CALLS_DETAILED_TRACKING, false); @@ -293,6 +296,11 @@ public class BinderCallsStatsService extends Binder { @Override protected void dump(FileDescriptor fd, PrintWriter pw, String[] args) { + if (!DumpUtils.checkDumpAndUsageStatsPermission(ActivityThread.currentApplication(), + SERVICE_NAME, pw)) { + return; + } + boolean verbose = false; if (args != null) { for (final String arg : args) { diff --git a/services/core/java/com/android/server/UiModeManagerService.java b/services/core/java/com/android/server/UiModeManagerService.java index 6b038979a98d..9d71896ef08b 100644 --- a/services/core/java/com/android/server/UiModeManagerService.java +++ b/services/core/java/com/android/server/UiModeManagerService.java @@ -16,6 +16,7 @@ package com.android.server; +import android.annotation.IntRange; import android.annotation.Nullable; import android.app.Activity; import android.app.ActivityManager; @@ -41,6 +42,7 @@ import android.os.Handler; import android.os.PowerManager; import android.os.PowerManager.ServiceType; import android.os.PowerManagerInternal; +import android.os.Process; import android.os.RemoteException; import android.os.ResultReceiver; import android.os.ServiceManager; @@ -52,6 +54,7 @@ import android.provider.Settings.Secure; import android.service.dreams.Sandman; import android.service.vr.IVrManager; import android.service.vr.IVrStateCallbacks; +import android.util.ArraySet; import android.util.Slog; import com.android.internal.R; @@ -65,6 +68,9 @@ import com.android.server.twilight.TwilightState; import java.io.FileDescriptor; import java.io.PrintWriter; +import java.util.HashMap; +import java.util.Map; +import java.util.Set; final class UiModeManagerService extends SystemService { private static final String TAG = UiModeManager.class.getSimpleName(); @@ -80,6 +86,7 @@ final class UiModeManagerService extends SystemService { private int mLastBroadcastState = Intent.EXTRA_DOCK_STATE_UNDOCKED; private int mNightMode = UiModeManager.MODE_NIGHT_NO; + private Map<Integer, String> mCarModePackagePriority = new HashMap<>(); private boolean mCarModeEnabled = false; private boolean mCharging = false; private boolean mPowerSave = false; @@ -349,15 +356,25 @@ final class UiModeManagerService extends SystemService { private final IUiModeManager.Stub mService = new IUiModeManager.Stub() { @Override - public void enableCarMode(int flags) { + public void enableCarMode(@UiModeManager.EnableCarMode int flags, + @IntRange(from = 0) int priority, String callingPackage) { if (isUiModeLocked()) { Slog.e(TAG, "enableCarMode while UI mode is locked"); return; } + + if (priority != UiModeManager.DEFAULT_PRIORITY + && getContext().checkCallingOrSelfPermission( + android.Manifest.permission.ENTER_CAR_MODE_PRIORITIZED) + != PackageManager.PERMISSION_GRANTED) { + throw new SecurityException("Enabling car mode with a priority requires " + + "permission ENTER_CAR_MODE_PRIORITIZED"); + } + final long ident = Binder.clearCallingIdentity(); try { synchronized (mLock) { - setCarModeLocked(true, flags); + setCarModeLocked(true, flags, priority, callingPackage); if (mSystemReady) { updateLocked(flags, 0); } @@ -367,16 +384,49 @@ final class UiModeManagerService extends SystemService { } } + /** + * This method is only kept around for the time being; the AIDL has an UnsupportedAppUsage + * tag which means this method is technically considered part of the greylist "API". + * @param flags + */ + @Override + public void disableCarMode(@UiModeManager.DisableCarMode int flags) { + disableCarModeByCallingPackage(flags, null /* callingPackage */); + } + + /** + * Handles requests to disable car mode. + * @param flags Disable car mode flags + * @param callingPackage + */ @Override - public void disableCarMode(int flags) { + public void disableCarModeByCallingPackage(@UiModeManager.DisableCarMode int flags, + String callingPackage) { if (isUiModeLocked()) { Slog.e(TAG, "disableCarMode while UI mode is locked"); return; } + + // If the caller is the system, we will allow the DISABLE_CAR_MODE_ALL_PRIORITIES car + // mode flag to be specified; this is so that the user can disable car mode at all + // priorities using the persistent notification. + boolean isSystemCaller = Binder.getCallingUid() == Process.SYSTEM_UID; + final int carModeFlags = + isSystemCaller ? flags : flags & ~UiModeManager.DISABLE_CAR_MODE_ALL_PRIORITIES; + final long ident = Binder.clearCallingIdentity(); try { synchronized (mLock) { - setCarModeLocked(false, 0); + // Determine if the caller has enabled car mode at a priority other than the + // default one. If they have, then attempt to disable at that priority. + int priority = mCarModePackagePriority.entrySet() + .stream() + .filter(e -> e.getValue().equals(callingPackage)) + .findFirst() + .map(Map.Entry::getKey) + .orElse(UiModeManager.DEFAULT_PRIORITY); + + setCarModeLocked(false, carModeFlags, priority, callingPackage); if (mSystemReady) { updateLocked(0, flags); } @@ -477,19 +527,32 @@ final class UiModeManagerService extends SystemService { synchronized (mLock) { pw.println("Current UI Mode Service state:"); pw.print(" mDockState="); pw.print(mDockState); - pw.print(" mLastBroadcastState="); pw.println(mLastBroadcastState); + pw.print(" mLastBroadcastState="); pw.println(mLastBroadcastState); + pw.print(" mNightMode="); pw.print(mNightMode); pw.print(" ("); - pw.print(Shell.nightModeToStr(mNightMode)); pw.print(") "); - pw.print(" mNightModeLocked="); pw.print(mNightModeLocked); - pw.print(" mCarModeEnabled="); pw.print(mCarModeEnabled); - pw.print(" mComputedNightMode="); pw.print(mComputedNightMode); - pw.print(" mCarModeEnableFlags="); pw.print(mCarModeEnableFlags); - pw.print(" mEnableCarDockLaunch="); pw.println(mEnableCarDockLaunch); + pw.print(Shell.nightModeToStr(mNightMode)); pw.print(") "); + pw.print(" mNightModeLocked="); pw.println(mNightModeLocked); + + pw.print(" mCarModeEnabled="); pw.print(mCarModeEnabled); + pw.print(" (carModeApps="); + for (Map.Entry<Integer, String> entry : mCarModePackagePriority.entrySet()) { + pw.print(entry.getKey()); + pw.print(":"); + pw.print(entry.getValue()); + pw.print(" "); + } + pw.println(""); + pw.print(" mComputedNightMode="); pw.print(mComputedNightMode); + pw.print(" mCarModeEnableFlags="); pw.print(mCarModeEnableFlags); + pw.print(" mEnableCarDockLaunch="); pw.println(mEnableCarDockLaunch); + pw.print(" mCurUiMode=0x"); pw.print(Integer.toHexString(mCurUiMode)); - pw.print(" mUiModeLocked="); pw.print(mUiModeLocked); - pw.print(" mSetUiMode=0x"); pw.println(Integer.toHexString(mSetUiMode)); + pw.print(" mUiModeLocked="); pw.print(mUiModeLocked); + pw.print(" mSetUiMode=0x"); pw.println(Integer.toHexString(mSetUiMode)); + pw.print(" mHoldingConfiguration="); pw.print(mHoldingConfiguration); - pw.print(" mSystemReady="); pw.println(mSystemReady); + pw.print(" mSystemReady="); pw.println(mSystemReady); + if (mTwilightManager != null) { // We may not have a TwilightManager. pw.print(" mTwilightService.getLastTwilightState()="); @@ -512,12 +575,32 @@ final class UiModeManagerService extends SystemService { } } - void setCarModeLocked(boolean enabled, int flags) { - if (mCarModeEnabled != enabled) { - mCarModeEnabled = enabled; + /** + * Updates the global car mode state. + * The device is considered to be in car mode if there exists an app at any priority level which + * has entered car mode. + * + * @param enabled {@code true} if the caller wishes to enable car mode, {@code false} otherwise. + * @param flags Flags used when enabling/disabling car mode. + * @param priority The priority level for entering or exiting car mode; defaults to + * {@link UiModeManager#DEFAULT_PRIORITY} for callers using + * {@link UiModeManager#enableCarMode(int)}. Callers using + * {@link UiModeManager#enableCarMode(int, int)} may specify a priority. + * @param packageName The package name of the app which initiated the request to enable or + * disable car mode. + */ + void setCarModeLocked(boolean enabled, int flags, int priority, String packageName) { + if (enabled) { + enableCarMode(priority, packageName); + } else { + disableCarMode(flags, priority, packageName); + } + boolean isCarModeNowEnabled = isCarModeEnabled(); + if (mCarModeEnabled != isCarModeNowEnabled) { + mCarModeEnabled = isCarModeNowEnabled; // When exiting car mode, restore night mode from settings - if (!mCarModeEnabled) { + if (!isCarModeNowEnabled) { Context context = getContext(); updateNightModeFromSettings(context, context.getResources(), @@ -527,11 +610,102 @@ final class UiModeManagerService extends SystemService { mCarModeEnableFlags = flags; } + /** + * Handles disabling car mode. + * <p> + * Car mode can be disabled at a priority level if any of the following is true: + * 1. The priority being disabled is the {@link UiModeManager#DEFAULT_PRIORITY}. + * 2. The priority level is enabled and the caller is the app who originally enabled it. + * 3. The {@link UiModeManager#DISABLE_CAR_MODE_ALL_PRIORITIES} flag was specified, meaning all + * car mode priorities are disabled. + * + * @param flags Car mode flags. + * @param priority The priority level at which to disable car mode. + * @param packageName The calling package which initiated the request. + */ + private void disableCarMode(int flags, int priority, String packageName) { + boolean isDisableAll = (flags & UiModeManager.DISABLE_CAR_MODE_ALL_PRIORITIES) != 0; + boolean isPriorityTracked = mCarModePackagePriority.keySet().contains(priority); + boolean isDefaultPriority = priority == UiModeManager.DEFAULT_PRIORITY; + boolean isChangeAllowed = + // Anyone can disable the default priority. + isDefaultPriority + // If priority was enabled, only enabling package can disable it. + || isPriorityTracked && mCarModePackagePriority.get(priority).equals(packageName) + // Disable all priorities flag can disable all regardless. + || isDisableAll; + if (isChangeAllowed) { + Slog.d(TAG, "disableCarMode: disabling, priority=" + priority + + ", packageName=" + packageName); + if (isDisableAll) { + Set<Map.Entry<Integer, String>> entries = + new ArraySet<>(mCarModePackagePriority.entrySet()); + mCarModePackagePriority.clear(); + + for (Map.Entry<Integer, String> entry : entries) { + notifyCarModeDisabled(entry.getKey(), entry.getValue()); + } + } else { + mCarModePackagePriority.remove(priority); + notifyCarModeDisabled(priority, packageName); + } + } + } + + /** + * Handles enabling car mode. + * <p> + * Car mode can be enabled at any priority if it has not already been enabled at that priority. + * The calling package is tracked for the first app which enters priority at the + * {@link UiModeManager#DEFAULT_PRIORITY}, though any app can disable it at that priority. + * + * @param priority The priority for enabling car mode. + * @param packageName The calling package which initiated the request. + */ + private void enableCarMode(int priority, String packageName) { + boolean isPriorityTracked = mCarModePackagePriority.containsKey(priority); + boolean isPackagePresent = mCarModePackagePriority.containsValue(packageName); + if (!isPriorityTracked && !isPackagePresent) { + Slog.d(TAG, "enableCarMode: enabled at priority=" + priority + ", packageName=" + + packageName); + mCarModePackagePriority.put(priority, packageName); + notifyCarModeEnabled(priority, packageName); + } else { + Slog.d(TAG, "enableCarMode: car mode at priority " + priority + " already enabled."); + } + + } + + private void notifyCarModeEnabled(int priority, String packageName) { + Intent intent = new Intent(UiModeManager.ACTION_ENTER_CAR_MODE_PRIORITIZED); + intent.putExtra(UiModeManager.EXTRA_CALLING_PACKAGE, packageName); + intent.putExtra(UiModeManager.EXTRA_PRIORITY, priority); + getContext().sendBroadcastAsUser(intent, UserHandle.ALL, + android.Manifest.permission.HANDLE_CAR_MODE_CHANGES); + } + + private void notifyCarModeDisabled(int priority, String packageName) { + Intent intent = new Intent(UiModeManager.ACTION_EXIT_CAR_MODE_PRIORITIZED); + intent.putExtra(UiModeManager.EXTRA_CALLING_PACKAGE, packageName); + intent.putExtra(UiModeManager.EXTRA_PRIORITY, priority); + getContext().sendBroadcastAsUser(intent, UserHandle.ALL, + android.Manifest.permission.HANDLE_CAR_MODE_CHANGES); + } + + /** + * Determines if car mode is enabled at any priority level. + * @return {@code true} if car mode is enabled, {@code false} otherwise. + */ + private boolean isCarModeEnabled() { + return mCarModePackagePriority.size() > 0; + } + private void updateDockState(int newState) { synchronized (mLock) { if (newState != mDockState) { mDockState = newState; - setCarModeLocked(mDockState == Intent.EXTRA_DOCK_STATE_CAR, 0); + setCarModeLocked(mDockState == Intent.EXTRA_DOCK_STATE_CAR, 0, + UiModeManager.DEFAULT_PRIORITY, "" /* packageName */); if (mSystemReady) { updateLocked(UiModeManager.ENABLE_CAR_MODE_GO_CAR_HOME, 0); } diff --git a/services/core/java/com/android/server/VibratorService.java b/services/core/java/com/android/server/VibratorService.java index d622fb433ed8..0db8495e9af9 100644 --- a/services/core/java/com/android/server/VibratorService.java +++ b/services/core/java/com/android/server/VibratorService.java @@ -28,8 +28,8 @@ import android.content.pm.PackageManager; import android.content.res.Resources; import android.database.ContentObserver; import android.hardware.input.InputManager; +import android.hardware.vibrator.IVibrator; import android.hardware.vibrator.V1_0.EffectStrength; -import android.hardware.vibrator.V1_4.Capabilities; import android.icu.text.DateFormat; import android.media.AudioAttributes; import android.media.AudioManager; @@ -1153,7 +1153,7 @@ public class VibratorService extends IVibratorService.Stub long duration = vibratorPerformEffect(prebaked.getId(), prebaked.getEffectStrength(), vib); long timeout = duration; - if ((mCapabilities & Capabilities.PERFORM_COMPLETION_CALLBACK) != 0) { + if ((mCapabilities & IVibrator.CAP_PERFORM_CALLBACK) != 0) { timeout *= ASYNC_TIMEOUT_MULTIPLIER; } if (timeout > 0) { diff --git a/services/core/java/com/android/server/Watchdog.java b/services/core/java/com/android/server/Watchdog.java index 83ad4e7e7100..454941ccdb03 100644 --- a/services/core/java/com/android/server/Watchdog.java +++ b/services/core/java/com/android/server/Watchdog.java @@ -89,6 +89,7 @@ public class Watchdog extends Thread { "/system/bin/drmserver", "/system/bin/mediadrmserver", "/system/bin/mediaserver", + "/system/bin/netd", "/system/bin/sdcard", "/system/bin/surfaceflinger", "/system/bin/vold", diff --git a/services/core/java/com/android/server/compat/PlatformCompat.java b/services/core/java/com/android/server/compat/PlatformCompat.java index 709f3f82d4c5..ae5ad7ea1261 100644 --- a/services/core/java/com/android/server/compat/PlatformCompat.java +++ b/services/core/java/com/android/server/compat/PlatformCompat.java @@ -126,6 +126,12 @@ public class PlatformCompat extends IPlatformCompat.Stub { } @Override + public void clearOverridesForTest(String packageName) { + CompatConfig config = CompatConfig.get(); + config.removePackageOverrides(packageName); + } + + @Override public boolean clearOverride(long changeId, String packageName) { boolean existed = CompatConfig.get().removeOverride(changeId, packageName); killPackage(packageName); diff --git a/services/core/java/com/android/server/pm/InstructionSets.java b/services/core/java/com/android/server/pm/InstructionSets.java index f326f1d20c46..ec48713b0874 100644 --- a/services/core/java/com/android/server/pm/InstructionSets.java +++ b/services/core/java/com/android/server/pm/InstructionSets.java @@ -22,11 +22,11 @@ import android.os.SystemProperties; import android.text.TextUtils; import android.util.ArraySet; +import dalvik.system.VMRuntime; + import java.util.ArrayList; import java.util.List; -import dalvik.system.VMRuntime; - /** * Provides various methods for obtaining and converting of instruction sets. * @@ -113,12 +113,15 @@ public class InstructionSets { return allInstructionSets; } - public static String getPrimaryInstructionSet(ApplicationInfo info) { - if (info.primaryCpuAbi == null) { + /** + * Calculates the primary instruction set based on the computed Abis of a given package. + */ + public static String getPrimaryInstructionSet(PackageAbiHelper.Abis abis) { + if (abis.primary == null) { return getPreferredInstructionSet(); } - return VMRuntime.getInstructionSet(info.primaryCpuAbi); + return VMRuntime.getInstructionSet(abis.primary); } } diff --git a/services/core/java/com/android/server/pm/PackageAbiHelper.java b/services/core/java/com/android/server/pm/PackageAbiHelper.java new file mode 100644 index 000000000000..6f46564068d9 --- /dev/null +++ b/services/core/java/com/android/server/pm/PackageAbiHelper.java @@ -0,0 +1,135 @@ +/* + * 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.pm; + +import android.annotation.Nullable; +import android.content.pm.PackageParser; +import android.util.Pair; + +import com.android.internal.annotations.VisibleForTesting; + +import java.io.File; +import java.util.Set; + +@VisibleForTesting +interface PackageAbiHelper { + /** + * Derive and get the location of native libraries for the given package, + * which varies depending on where and how the package was installed. + */ + NativeLibraryPaths getNativeLibraryPaths( + PackageParser.Package pkg, File appLib32InstallDir); + + /** + * Calculate the abis for a bundled app. These can uniquely be determined from the contents of + * the system partition, i.e whether it contains 64 or 32 bit shared libraries etc. We do not + * validate any of this information, and instead assume that the system was built sensibly. + */ + Abis getBundledAppAbis(PackageParser.Package pkg); + + /** + * Derive the ABI of a non-system package located at {@code pkg}. This information + * is derived purely on the basis of the contents of {@code pkg} and {@code cpuAbiOverride}. + * + * If {@code extractLibs} is true, native libraries are extracted from the app if required. + */ + Pair<Abis, NativeLibraryPaths> derivePackageAbi( + PackageParser.Package pkg, String cpuAbiOverride, boolean extractLibs) + throws PackageManagerException; + + /** + * Calculates adjusted ABIs for a set of packages belonging to a shared user so that they all + * match. i.e, so that all packages can be run inside a single process if required. + * + * Optionally, callers can pass in a parsed package via {@code scannedPackage} in which case + * this function will either try and make the ABI for all packages in + * {@code packagesForUser} match {@code scannedPackage} or will update the ABI of + * {@code scannedPackage} to match the ABI selected for {@code packagesForUser}. This + * variant is used when installing or updating a package that belongs to a shared user. + * + * NOTE: We currently only match for the primary CPU abi string. Matching the secondary + * adds unnecessary complexity. + * + * @return the calculated primary abi that should be set for all non-specified packages + * belonging to the shared user. + */ + @Nullable + String getAdjustedAbiForSharedUser( + Set<PackageSetting> packagesForUser, PackageParser.Package scannedPackage); + + /** + * The native library paths and related properties that should be set on a + * {@link android.content.pm.PackageParser.Package}. + */ + final class NativeLibraryPaths { + public final String nativeLibraryRootDir; + public final boolean nativeLibraryRootRequiresIsa; + public final String nativeLibraryDir; + public final String secondaryNativeLibraryDir; + + @VisibleForTesting + NativeLibraryPaths(String nativeLibraryRootDir, + boolean nativeLibraryRootRequiresIsa, String nativeLibraryDir, + String secondaryNativeLibraryDir) { + this.nativeLibraryRootDir = nativeLibraryRootDir; + this.nativeLibraryRootRequiresIsa = nativeLibraryRootRequiresIsa; + this.nativeLibraryDir = nativeLibraryDir; + this.secondaryNativeLibraryDir = secondaryNativeLibraryDir; + } + + public void applyTo(PackageParser.Package pkg) { + pkg.applicationInfo.nativeLibraryRootDir = nativeLibraryRootDir; + pkg.applicationInfo.nativeLibraryRootRequiresIsa = nativeLibraryRootRequiresIsa; + pkg.applicationInfo.nativeLibraryDir = nativeLibraryDir; + pkg.applicationInfo.secondaryNativeLibraryDir = secondaryNativeLibraryDir; + } + } + + /** + * The primary and secondary ABIs that should be set on a package and its package setting. + */ + final class Abis { + public final String primary; + public final String secondary; + + @VisibleForTesting + Abis(String primary, String secondary) { + this.primary = primary; + this.secondary = secondary; + } + + Abis(PackageParser.Package pkg) { + this(pkg.applicationInfo.primaryCpuAbi, pkg.applicationInfo.secondaryCpuAbi); + } + + public void applyTo(PackageParser.Package pkg) { + pkg.applicationInfo.primaryCpuAbi = primary; + pkg.applicationInfo.secondaryCpuAbi = secondary; + } + public void applyTo(PackageSetting pkgSetting) { + // pkgSetting might be null during rescan following uninstall of updates + // to a bundled app, so accommodate that possibility. The settings in + // that case will be established later from the parsed package. + // + // If the settings aren't null, sync them up with what we've derived. + if (pkgSetting != null) { + pkgSetting.primaryCpuAbiString = primary; + pkgSetting.secondaryCpuAbiString = secondary; + } + } + } +} diff --git a/services/core/java/com/android/server/pm/PackageAbiHelperImpl.java b/services/core/java/com/android/server/pm/PackageAbiHelperImpl.java new file mode 100644 index 000000000000..1d3d24c27041 --- /dev/null +++ b/services/core/java/com/android/server/pm/PackageAbiHelperImpl.java @@ -0,0 +1,528 @@ +/* + * 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.pm; + +import static android.content.pm.PackageManager.INSTALL_FAILED_INTERNAL_ERROR; +import static android.content.pm.PackageParser.isApkFile; +import static android.os.Trace.TRACE_TAG_PACKAGE_MANAGER; + +import static com.android.internal.content.NativeLibraryHelper.LIB64_DIR_NAME; +import static com.android.internal.content.NativeLibraryHelper.LIB_DIR_NAME; +import static com.android.server.pm.InstructionSets.getPreferredInstructionSet; +import static com.android.server.pm.InstructionSets.getPrimaryInstructionSet; + +import android.annotation.Nullable; +import android.content.pm.ApplicationInfo; +import android.content.pm.PackageManager; +import android.content.pm.PackageParser; +import android.os.Build; +import android.os.Environment; +import android.os.FileUtils; +import android.os.Trace; +import android.text.TextUtils; +import android.util.Pair; +import android.util.Slog; + +import com.android.internal.content.NativeLibraryHelper; +import com.android.internal.util.ArrayUtils; + +import dalvik.system.VMRuntime; + +import libcore.io.IoUtils; + +import java.io.File; +import java.io.IOException; +import java.util.Set; + +final class PackageAbiHelperImpl implements PackageAbiHelper { + + private static String calculateBundledApkRoot(final String codePathString) { + final File codePath = new File(codePathString); + final File codeRoot; + if (FileUtils.contains(Environment.getRootDirectory(), codePath)) { + codeRoot = Environment.getRootDirectory(); + } else if (FileUtils.contains(Environment.getOemDirectory(), codePath)) { + codeRoot = Environment.getOemDirectory(); + } else if (FileUtils.contains(Environment.getVendorDirectory(), codePath)) { + codeRoot = Environment.getVendorDirectory(); + } else if (FileUtils.contains(Environment.getOdmDirectory(), codePath)) { + codeRoot = Environment.getOdmDirectory(); + } else if (FileUtils.contains(Environment.getProductDirectory(), codePath)) { + codeRoot = Environment.getProductDirectory(); + } else if (FileUtils.contains(Environment.getSystemExtDirectory(), codePath)) { + codeRoot = Environment.getSystemExtDirectory(); + } else if (FileUtils.contains(Environment.getOdmDirectory(), codePath)) { + codeRoot = Environment.getOdmDirectory(); + } else { + // Unrecognized code path; take its top real segment as the apk root: + // e.g. /something/app/blah.apk => /something + try { + File f = codePath.getCanonicalFile(); + File parent = f.getParentFile(); // non-null because codePath is a file + File tmp; + while ((tmp = parent.getParentFile()) != null) { + f = parent; + parent = tmp; + } + codeRoot = f; + Slog.w(PackageManagerService.TAG, "Unrecognized code path " + + codePath + " - using " + codeRoot); + } catch (IOException e) { + // Can't canonicalize the code path -- shenanigans? + Slog.w(PackageManagerService.TAG, "Can't canonicalize code path " + codePath); + return Environment.getRootDirectory().getPath(); + } + } + return codeRoot.getPath(); + } + + // Utility method that returns the relative package path with respect + // to the installation directory. Like say for /data/data/com.test-1.apk + // string com.test-1 is returned. + private static String deriveCodePathName(String codePath) { + if (codePath == null) { + return null; + } + final File codeFile = new File(codePath); + final String name = codeFile.getName(); + if (codeFile.isDirectory()) { + return name; + } else if (name.endsWith(".apk") || name.endsWith(".tmp")) { + final int lastDot = name.lastIndexOf('.'); + return name.substring(0, lastDot); + } else { + Slog.w(PackageManagerService.TAG, "Odd, " + codePath + " doesn't look like an APK"); + return null; + } + } + + private static void maybeThrowExceptionForMultiArchCopy(String message, int copyRet) throws + PackageManagerException { + if (copyRet < 0) { + if (copyRet != PackageManager.NO_NATIVE_LIBRARIES + && copyRet != PackageManager.INSTALL_FAILED_NO_MATCHING_ABIS) { + throw new PackageManagerException(copyRet, message); + } + } + } + + @Override + public NativeLibraryPaths getNativeLibraryPaths( + PackageParser.Package pkg, File appLib32InstallDir) { + return getNativeLibraryPaths(new Abis(pkg), appLib32InstallDir, pkg.codePath, + pkg.applicationInfo.sourceDir, pkg.applicationInfo.isSystemApp(), + pkg.applicationInfo.isUpdatedSystemApp()); + } + + private static NativeLibraryPaths getNativeLibraryPaths(final Abis abis, + final File appLib32InstallDir, final String codePath, final String sourceDir, + final boolean isSystemApp, final boolean isUpdatedSystemApp) { + final File codeFile = new File(codePath); + final boolean bundledApp = isSystemApp && !isUpdatedSystemApp; + + final String nativeLibraryRootDir; + final boolean nativeLibraryRootRequiresIsa; + final String nativeLibraryDir; + final String secondaryNativeLibraryDir; + + if (isApkFile(codeFile)) { + // Monolithic install + if (bundledApp) { + // If "/system/lib64/apkname" exists, assume that is the per-package + // native library directory to use; otherwise use "/system/lib/apkname". + final String apkRoot = calculateBundledApkRoot(sourceDir); + final boolean is64Bit = VMRuntime.is64BitInstructionSet( + getPrimaryInstructionSet(abis)); + + // This is a bundled system app so choose the path based on the ABI. + // if it's a 64 bit abi, use lib64 otherwise use lib32. Note that this + // is just the default path. + final String apkName = deriveCodePathName(codePath); + final String libDir = is64Bit ? LIB64_DIR_NAME : LIB_DIR_NAME; + nativeLibraryRootDir = Environment.buildPath(new File(apkRoot), libDir, + apkName).getAbsolutePath(); + + if (abis.secondary != null) { + final String secondaryLibDir = is64Bit ? LIB_DIR_NAME : LIB64_DIR_NAME; + secondaryNativeLibraryDir = Environment.buildPath(new File(apkRoot), + secondaryLibDir, apkName).getAbsolutePath(); + } else { + secondaryNativeLibraryDir = null; + } + } else { + final String apkName = deriveCodePathName(codePath); + nativeLibraryRootDir = new File(appLib32InstallDir, apkName) + .getAbsolutePath(); + secondaryNativeLibraryDir = null; + } + + nativeLibraryRootRequiresIsa = false; + nativeLibraryDir = nativeLibraryRootDir; + } else { + // Cluster install + nativeLibraryRootDir = new File(codeFile, LIB_DIR_NAME).getAbsolutePath(); + nativeLibraryRootRequiresIsa = true; + + nativeLibraryDir = new File(nativeLibraryRootDir, + getPrimaryInstructionSet(abis)).getAbsolutePath(); + + if (abis.secondary != null) { + secondaryNativeLibraryDir = new File(nativeLibraryRootDir, + VMRuntime.getInstructionSet(abis.secondary)).getAbsolutePath(); + } else { + secondaryNativeLibraryDir = null; + } + } + return new NativeLibraryPaths(nativeLibraryRootDir, nativeLibraryRootRequiresIsa, + nativeLibraryDir, secondaryNativeLibraryDir); + } + + @Override + public Abis getBundledAppAbis(PackageParser.Package pkg) { + final String apkName = deriveCodePathName(pkg.applicationInfo.getCodePath()); + + // If "/system/lib64/apkname" exists, assume that is the per-package + // native library directory to use; otherwise use "/system/lib/apkname". + final String apkRoot = calculateBundledApkRoot(pkg.applicationInfo.sourceDir); + final Abis abis = getBundledAppAbi(pkg, apkRoot, apkName); + return abis; + } + + /** + * Deduces the ABI of a bundled app and sets the relevant fields on the + * parsed pkg object. + * + * @param apkRoot the root of the installed apk, something like {@code /system} or + * {@code /oem} under which system libraries are installed. + * @param apkName the name of the installed package. + */ + private Abis getBundledAppAbi(PackageParser.Package pkg, String apkRoot, String apkName) { + final File codeFile = new File(pkg.codePath); + + final boolean has64BitLibs; + final boolean has32BitLibs; + + final String primaryCpuAbi; + final String secondaryCpuAbi; + if (isApkFile(codeFile)) { + // Monolithic install + has64BitLibs = + (new File(apkRoot, new File(LIB64_DIR_NAME, apkName).getPath())).exists(); + has32BitLibs = (new File(apkRoot, new File(LIB_DIR_NAME, apkName).getPath())).exists(); + } else { + // Cluster install + final File rootDir = new File(codeFile, LIB_DIR_NAME); + if (!ArrayUtils.isEmpty(Build.SUPPORTED_64_BIT_ABIS) + && !TextUtils.isEmpty(Build.SUPPORTED_64_BIT_ABIS[0])) { + final String isa = VMRuntime.getInstructionSet(Build.SUPPORTED_64_BIT_ABIS[0]); + has64BitLibs = (new File(rootDir, isa)).exists(); + } else { + has64BitLibs = false; + } + if (!ArrayUtils.isEmpty(Build.SUPPORTED_32_BIT_ABIS) + && !TextUtils.isEmpty(Build.SUPPORTED_32_BIT_ABIS[0])) { + final String isa = VMRuntime.getInstructionSet(Build.SUPPORTED_32_BIT_ABIS[0]); + has32BitLibs = (new File(rootDir, isa)).exists(); + } else { + has32BitLibs = false; + } + } + + if (has64BitLibs && !has32BitLibs) { + // The package has 64 bit libs, but not 32 bit libs. Its primary + // ABI should be 64 bit. We can safely assume here that the bundled + // native libraries correspond to the most preferred ABI in the list. + + primaryCpuAbi = Build.SUPPORTED_64_BIT_ABIS[0]; + secondaryCpuAbi = null; + } else if (has32BitLibs && !has64BitLibs) { + // The package has 32 bit libs but not 64 bit libs. Its primary + // ABI should be 32 bit. + + primaryCpuAbi = Build.SUPPORTED_32_BIT_ABIS[0]; + secondaryCpuAbi = null; + } else if (has32BitLibs && has64BitLibs) { + // The application has both 64 and 32 bit bundled libraries. We check + // here that the app declares multiArch support, and warn if it doesn't. + // + // We will be lenient here and record both ABIs. The primary will be the + // ABI that's higher on the list, i.e, a device that's configured to prefer + // 64 bit apps will see a 64 bit primary ABI, + + if ((pkg.applicationInfo.flags & ApplicationInfo.FLAG_MULTIARCH) == 0) { + Slog.e(PackageManagerService.TAG, + "Package " + pkg + " has multiple bundled libs, but is not multiarch."); + } + + if (VMRuntime.is64BitInstructionSet(getPreferredInstructionSet())) { + primaryCpuAbi = Build.SUPPORTED_64_BIT_ABIS[0]; + secondaryCpuAbi = Build.SUPPORTED_32_BIT_ABIS[0]; + } else { + primaryCpuAbi = Build.SUPPORTED_32_BIT_ABIS[0]; + secondaryCpuAbi = Build.SUPPORTED_64_BIT_ABIS[0]; + } + } else { + primaryCpuAbi = null; + secondaryCpuAbi = null; + } + return new Abis(primaryCpuAbi, secondaryCpuAbi); + } + + @Override + public Pair<Abis, NativeLibraryPaths> derivePackageAbi( + PackageParser.Package pkg, String cpuAbiOverride, boolean extractLibs) + throws PackageManagerException { + // Give ourselves some initial paths; we'll come back for another + // pass once we've determined ABI below. + final NativeLibraryPaths initialLibraryPaths = getNativeLibraryPaths(new Abis(pkg), + PackageManagerService.sAppLib32InstallDir, pkg.codePath, + pkg.applicationInfo.sourceDir, pkg.applicationInfo.isSystemApp(), + pkg.applicationInfo.isUpdatedSystemApp()); + + // We shouldn't attempt to extract libs from system app when it was not updated. + if (PackageManagerService.isSystemApp(pkg) && !pkg.isUpdatedSystemApp()) { + extractLibs = false; + } + + final String nativeLibraryRootStr = initialLibraryPaths.nativeLibraryRootDir; + final boolean useIsaSpecificSubdirs = initialLibraryPaths.nativeLibraryRootRequiresIsa; + + String primaryCpuAbi = null; + String secondaryCpuAbi = null; + + NativeLibraryHelper.Handle handle = null; + try { + handle = NativeLibraryHelper.Handle.create(pkg); + // TODO(multiArch): This can be null for apps that didn't go through the + // usual installation process. We can calculate it again, like we + // do during install time. + // + // TODO(multiArch): Why do we need to rescan ASEC apps again ? It seems totally + // unnecessary. + final File nativeLibraryRoot = new File(nativeLibraryRootStr); + + // Null out the abis so that they can be recalculated. + primaryCpuAbi = null; + secondaryCpuAbi = null; + if ((pkg.applicationInfo.flags & ApplicationInfo.FLAG_MULTIARCH) != 0) { + // Warn if we've set an abiOverride for multi-lib packages.. + // By definition, we need to copy both 32 and 64 bit libraries for + // such packages. + if (pkg.cpuAbiOverride != null + && !NativeLibraryHelper.CLEAR_ABI_OVERRIDE.equals(pkg.cpuAbiOverride)) { + Slog.w(PackageManagerService.TAG, + "Ignoring abiOverride for multi arch application."); + } + + int abi32 = PackageManager.NO_NATIVE_LIBRARIES; + int abi64 = PackageManager.NO_NATIVE_LIBRARIES; + if (Build.SUPPORTED_32_BIT_ABIS.length > 0) { + if (extractLibs) { + Trace.traceBegin(TRACE_TAG_PACKAGE_MANAGER, "copyNativeBinaries"); + abi32 = NativeLibraryHelper.copyNativeBinariesForSupportedAbi(handle, + nativeLibraryRoot, Build.SUPPORTED_32_BIT_ABIS, + useIsaSpecificSubdirs); + } else { + Trace.traceBegin(TRACE_TAG_PACKAGE_MANAGER, "findSupportedAbi"); + abi32 = NativeLibraryHelper.findSupportedAbi( + handle, Build.SUPPORTED_32_BIT_ABIS); + } + Trace.traceEnd(TRACE_TAG_PACKAGE_MANAGER); + } + + // Shared library native code should be in the APK zip aligned + if (abi32 >= 0 && pkg.isLibrary() && extractLibs) { + throw new PackageManagerException(INSTALL_FAILED_INTERNAL_ERROR, + "Shared library native lib extraction not supported"); + } + + maybeThrowExceptionForMultiArchCopy( + "Error unpackaging 32 bit native libs for multiarch app.", abi32); + + if (Build.SUPPORTED_64_BIT_ABIS.length > 0) { + if (extractLibs) { + Trace.traceBegin(TRACE_TAG_PACKAGE_MANAGER, "copyNativeBinaries"); + abi64 = NativeLibraryHelper.copyNativeBinariesForSupportedAbi(handle, + nativeLibraryRoot, Build.SUPPORTED_64_BIT_ABIS, + useIsaSpecificSubdirs); + } else { + Trace.traceBegin(TRACE_TAG_PACKAGE_MANAGER, "findSupportedAbi"); + abi64 = NativeLibraryHelper.findSupportedAbi( + handle, Build.SUPPORTED_64_BIT_ABIS); + } + Trace.traceEnd(TRACE_TAG_PACKAGE_MANAGER); + } + + maybeThrowExceptionForMultiArchCopy( + "Error unpackaging 64 bit native libs for multiarch app.", abi64); + + if (abi64 >= 0) { + // Shared library native libs should be in the APK zip aligned + if (extractLibs && pkg.isLibrary()) { + throw new PackageManagerException(INSTALL_FAILED_INTERNAL_ERROR, + "Shared library native lib extraction not supported"); + } + primaryCpuAbi = Build.SUPPORTED_64_BIT_ABIS[abi64]; + } + + if (abi32 >= 0) { + final String abi = Build.SUPPORTED_32_BIT_ABIS[abi32]; + if (abi64 >= 0) { + if (pkg.use32bitAbi) { + secondaryCpuAbi = primaryCpuAbi; + primaryCpuAbi = abi; + } else { + secondaryCpuAbi = abi; + } + } else { + primaryCpuAbi = abi; + } + } + } else { + String[] abiList = (cpuAbiOverride != null) + ? new String[]{cpuAbiOverride} : Build.SUPPORTED_ABIS; + + // Enable gross and lame hacks for apps that are built with old + // SDK tools. We must scan their APKs for renderscript bitcode and + // not launch them if it's present. Don't bother checking on devices + // that don't have 64 bit support. + boolean needsRenderScriptOverride = false; + if (Build.SUPPORTED_64_BIT_ABIS.length > 0 && cpuAbiOverride == null + && NativeLibraryHelper.hasRenderscriptBitcode(handle)) { + abiList = Build.SUPPORTED_32_BIT_ABIS; + needsRenderScriptOverride = true; + } + + final int copyRet; + if (extractLibs) { + Trace.traceBegin(TRACE_TAG_PACKAGE_MANAGER, "copyNativeBinaries"); + copyRet = NativeLibraryHelper.copyNativeBinariesForSupportedAbi(handle, + nativeLibraryRoot, abiList, useIsaSpecificSubdirs); + } else { + Trace.traceBegin(TRACE_TAG_PACKAGE_MANAGER, "findSupportedAbi"); + copyRet = NativeLibraryHelper.findSupportedAbi(handle, abiList); + } + Trace.traceEnd(TRACE_TAG_PACKAGE_MANAGER); + + if (copyRet < 0 && copyRet != PackageManager.NO_NATIVE_LIBRARIES) { + throw new PackageManagerException(INSTALL_FAILED_INTERNAL_ERROR, + "Error unpackaging native libs for app, errorCode=" + copyRet); + } + + if (copyRet >= 0) { + // Shared libraries that have native libs must be multi-architecture + if (pkg.isLibrary()) { + throw new PackageManagerException(INSTALL_FAILED_INTERNAL_ERROR, + "Shared library with native libs must be multiarch"); + } + primaryCpuAbi = abiList[copyRet]; + } else if (copyRet == PackageManager.NO_NATIVE_LIBRARIES + && cpuAbiOverride != null) { + primaryCpuAbi = cpuAbiOverride; + } else if (needsRenderScriptOverride) { + primaryCpuAbi = abiList[0]; + } + } + } catch (IOException ioe) { + Slog.e(PackageManagerService.TAG, "Unable to get canonical file " + ioe.toString()); + } finally { + IoUtils.closeQuietly(handle); + } + + // Now that we've calculated the ABIs and determined if it's an internal app, + // we will go ahead and populate the nativeLibraryPath. + + final Abis abis = new Abis(primaryCpuAbi, secondaryCpuAbi); + return new Pair<>(abis, + getNativeLibraryPaths(abis, PackageManagerService.sAppLib32InstallDir, + pkg.codePath, pkg.applicationInfo.sourceDir, + pkg.applicationInfo.isSystemApp(), + pkg.applicationInfo.isUpdatedSystemApp())); + } + + /** + * Adjusts ABIs for a set of packages belonging to a shared user so that they all match. + * i.e, so that all packages can be run inside a single process if required. + * + * Optionally, callers can pass in a parsed package via {@code newPackage} in which case + * this function will either try and make the ABI for all packages in + * {@code packagesForUser} match {@code scannedPackage} or will update the ABI of + * {@code scannedPackage} to match the ABI selected for {@code packagesForUser}. This + * variant is used when installing or updating a package that belongs to a shared user. + * + * NOTE: We currently only match for the primary CPU abi string. Matching the secondary + * adds unnecessary complexity. + */ + @Override + @Nullable + public String getAdjustedAbiForSharedUser( + Set<PackageSetting> packagesForUser, PackageParser.Package scannedPackage) { + String requiredInstructionSet = null; + if (scannedPackage != null && scannedPackage.applicationInfo.primaryCpuAbi != null) { + requiredInstructionSet = VMRuntime.getInstructionSet( + scannedPackage.applicationInfo.primaryCpuAbi); + } + + PackageSetting requirer = null; + for (PackageSetting ps : packagesForUser) { + // If packagesForUser contains scannedPackage, we skip it. This will happen + // when scannedPackage is an update of an existing package. Without this check, + // we will never be able to change the ABI of any package belonging to a shared + // user, even if it's compatible with other packages. + if (scannedPackage != null && scannedPackage.packageName.equals(ps.name)) { + continue; + } + if (ps.primaryCpuAbiString == null) { + continue; + } + + final String instructionSet = + VMRuntime.getInstructionSet(ps.primaryCpuAbiString); + if (requiredInstructionSet != null && !requiredInstructionSet.equals(instructionSet)) { + // We have a mismatch between instruction sets (say arm vs arm64) warn about + // this but there's not much we can do. + String errorMessage = "Instruction set mismatch, " + + ((requirer == null) ? "[caller]" : requirer) + + " requires " + requiredInstructionSet + " whereas " + ps + + " requires " + instructionSet; + Slog.w(PackageManagerService.TAG, errorMessage); + } + + if (requiredInstructionSet == null) { + requiredInstructionSet = instructionSet; + requirer = ps; + } + } + + if (requiredInstructionSet == null) { + return null; + } + final String adjustedAbi; + if (requirer != null) { + // requirer != null implies that either scannedPackage was null or that + // scannedPackage did not require an ABI, in which case we have to adjust + // scannedPackage to match the ABI of the set (which is the same as + // requirer's ABI) + adjustedAbi = requirer.primaryCpuAbiString; + } else { + // requirer == null implies that we're updating all ABIs in the set to + // match scannedPackage. + adjustedAbi = scannedPackage.applicationInfo.primaryCpuAbi; + } + return adjustedAbi; + } +} diff --git a/services/core/java/com/android/server/pm/PackageManagerService.java b/services/core/java/com/android/server/pm/PackageManagerService.java index 38d6399743f9..1bbc0cfdf0de 100644 --- a/services/core/java/com/android/server/pm/PackageManagerService.java +++ b/services/core/java/com/android/server/pm/PackageManagerService.java @@ -93,14 +93,12 @@ import static android.os.storage.StorageManager.FLAG_STORAGE_EXTERNAL; import static com.android.internal.app.IntentForwarderActivity.FORWARD_INTENT_TO_MANAGED_PROFILE; import static com.android.internal.app.IntentForwarderActivity.FORWARD_INTENT_TO_PARENT; -import static com.android.internal.content.NativeLibraryHelper.LIB64_DIR_NAME; import static com.android.internal.content.NativeLibraryHelper.LIB_DIR_NAME; import static com.android.server.pm.ComponentResolver.RESOLVE_PRIORITY_SORTER; import static com.android.server.pm.InstructionSets.getAppDexInstructionSets; import static com.android.server.pm.InstructionSets.getDexCodeInstructionSet; import static com.android.server.pm.InstructionSets.getDexCodeInstructionSets; import static com.android.server.pm.InstructionSets.getPreferredInstructionSet; -import static com.android.server.pm.InstructionSets.getPrimaryInstructionSet; import static com.android.server.pm.PackageManagerServiceCompilerMapping.getDefaultCompilerFilter; import static com.android.server.pm.PackageManagerServiceUtils.compareSignatures; import static com.android.server.pm.PackageManagerServiceUtils.compressedFileExists; @@ -280,6 +278,7 @@ import android.view.Display; import com.android.internal.R; import com.android.internal.annotations.GuardedBy; +import com.android.internal.annotations.VisibleForTesting; import com.android.internal.app.ResolverActivity; import com.android.internal.content.NativeLibraryHelper; import com.android.internal.content.PackageHelper; @@ -435,7 +434,7 @@ public class PackageManagerService extends IPackageManager.Stub // user, but by default initialize to this. public static final boolean DEBUG_DEXOPT = false; - private static final boolean DEBUG_ABI_SELECTION = false; + static final boolean DEBUG_ABI_SELECTION = false; private static final boolean DEBUG_INSTANT = Build.IS_DEBUGGABLE; private static final boolean DEBUG_APP_DATA = false; @@ -665,7 +664,8 @@ public class PackageManagerService extends IPackageManager.Stub private static final File sAppInstallDir = new File(Environment.getDataDirectory(), "app"); /** Directory where installed application's 32-bit native libraries are copied. */ - private static final File sAppLib32InstallDir = + @VisibleForTesting + static final File sAppLib32InstallDir = new File(Environment.getDataDirectory(), "app-lib"); // ---------------------------------------------------------------- @@ -756,6 +756,30 @@ public class PackageManagerService extends IPackageManager.Stub private final ApexManager mApexManager; + private final Injector mInjector; + + /** + * Unit tests will instantiate and / or extend to mock dependencies / behaviors. + */ + @VisibleForTesting + static class Injector { + private final UserManagerInternal mUserManager; + private final PackageAbiHelper mAbiHelper; + + Injector(UserManagerInternal userManager, PackageAbiHelper abiHelper) { + mUserManager = userManager; + mAbiHelper = abiHelper; + } + + public UserManagerInternal getUserManager() { + return mUserManager; + } + + public PackageAbiHelper getAbiHelper() { + return mAbiHelper; + } + } + class PackageParserCallback implements PackageParser.Callback { @Override public final boolean hasFeature(String feature) { return PackageManagerService.this.hasSystemFeature(feature, 0); @@ -2103,8 +2127,8 @@ public class PackageManagerService extends IPackageManager.Stub // survive long enough to benefit of background optimizations. for (int userId : firstUserIds) { PackageInfo info = getPackageInfo(packageName, /*flags*/ 0, userId); - // There's a race currently where some install events may interleave with an uninstall. - // This can lead to package info being null (b/36642664). + // There's a race currently where some install events may interleave with an + // uninstall. This can lead to package info being null (b/36642664). if (info != null) { mDexManager.notifyPackageInstalled(info, userId); } @@ -2180,6 +2204,7 @@ public class PackageManagerService extends IPackageManager.Stub * external/removable/unprotected storage. * @return {@link StorageEnum#TYPE_UNKNOWN} if the package is not stored externally or the * corresponding {@link StorageEnum} storage type value if it is. + * corresponding {@link StorageEnum} storage type value if it is. */ private static int getPackageExternalStorageType(VolumeInfo packageVolume, boolean packageIsExternal) { @@ -2437,6 +2462,11 @@ public class PackageManagerService extends IPackageManager.Stub mPermissionManager.getPermissionSettings(), mPackages); } } + + // TODO(b/137961986): We should pass this via constructor, but would first need to create + // a packages lock that could also be passed in. + mInjector = new Injector(getUserManagerInternal(), new PackageAbiHelperImpl()); + mSettings.addSharedUserLPw("android.uid.system", Process.SYSTEM_UID, ApplicationInfo.FLAG_SYSTEM, ApplicationInfo.PRIVATE_FLAG_PRIVILEGED); mSettings.addSharedUserLPw("android.uid.phone", RADIO_UID, @@ -3144,7 +3174,9 @@ public class PackageManagerService extends IPackageManager.Stub // the rest of the commands above) because there's precious little we // can do about it. A settings error is reported, though. final List<String> changedAbiCodePath = - adjustCpuAbisForSharedUserLPw(setting.packages, null /*scannedPackage*/); + applyAdjustedAbiToSharedUser(setting, null /*scannedPackage*/, + mInjector.getAbiHelper().getAdjustedAbiForSharedUser( + setting.packages, null /*scannedPackage*/)); if (changedAbiCodePath != null && changedAbiCodePath.size() > 0) { for (int i = changedAbiCodePath.size() - 1; i >= 0; --i) { final String codePathString = changedAbiCodePath.get(i); @@ -9413,7 +9445,8 @@ public class PackageManagerService extends IPackageManager.Stub null /* originalPkgSetting */, null, parseFlags, scanFlags, (pkg == mPlatformPackage), user); applyPolicy(pkg, parseFlags, scanFlags, mPlatformPackage); - final ScanResult scanResult = scanPackageOnlyLI(request, mFactoryTest, -1L); + final ScanResult scanResult = + scanPackageOnlyLI(request, mInjector, mFactoryTest, -1L); if (scanResult.existingSettingCopied && scanResult.request.pkgSetting != null) { scanResult.request.pkgSetting.updateFrom(scanResult.pkgSetting); } @@ -10775,7 +10808,8 @@ public class PackageManagerService extends IPackageManager.Stub } /** The result of a package scan. */ - private static class ScanResult { + @VisibleForTesting + static class ScanResult { /** The request that initiated the scan that produced this result. */ public final ScanRequest request; /** Whether or not the package scan was successful */ @@ -10814,7 +10848,8 @@ public class PackageManagerService extends IPackageManager.Stub } /** A package to be scanned */ - private static class ScanRequest { + @VisibleForTesting + static class ScanRequest { /** The parsed package */ @NonNull public final PackageParser.Package pkg; /** The package this package replaces */ @@ -11007,7 +11042,7 @@ public class PackageManagerService extends IPackageManager.Stub pkgSetting == null ? null : pkgSetting.pkg, pkgSetting, disabledPkgSetting, originalPkgSetting, realPkgName, parseFlags, scanFlags, (pkg == mPlatformPackage), user); - return scanPackageOnlyLI(request, mFactoryTest, currentTime); + return scanPackageOnlyLI(request, mInjector, mFactoryTest, currentTime); } } @@ -11232,20 +11267,70 @@ public class PackageManagerService extends IPackageManager.Stub } /** + * Applies the adjusted ABI calculated by + * {@link PackageAbiHelper#getAdjustedAbiForSharedUser(Set, PackageParser.Package)} to all + * relevant packages and settings. + * @param sharedUserSetting The {@code SharedUserSetting} to adjust + * @param scannedPackage the package being scanned or null + * @param adjustedAbi the adjusted ABI calculated by {@link PackageAbiHelper} + * @return the list of code paths that belong to packages that had their ABIs adjusted. + */ + private static List<String> applyAdjustedAbiToSharedUser(SharedUserSetting sharedUserSetting, + PackageParser.Package scannedPackage, String adjustedAbi) { + if (scannedPackage != null) { + scannedPackage.applicationInfo.primaryCpuAbi = adjustedAbi; + } + List<String> changedAbiCodePath = null; + for (PackageSetting ps : sharedUserSetting.packages) { + if (scannedPackage == null || !scannedPackage.packageName.equals(ps.name)) { + if (ps.primaryCpuAbiString != null) { + continue; + } + + ps.primaryCpuAbiString = adjustedAbi; + if (ps.pkg != null && ps.pkg.applicationInfo != null + && !TextUtils.equals( + adjustedAbi, ps.pkg.applicationInfo.primaryCpuAbi)) { + ps.pkg.applicationInfo.primaryCpuAbi = adjustedAbi; + if (DEBUG_ABI_SELECTION) { + Slog.i(TAG, + "Adjusting ABI for " + ps.name + " to " + adjustedAbi + + " (scannedPackage=" + + (scannedPackage != null ? scannedPackage : "null") + + ")"); + } + if (changedAbiCodePath == null) { + changedAbiCodePath = new ArrayList<>(); + } + changedAbiCodePath.add(ps.codePathString); + } + } + } + return changedAbiCodePath; + } + + + /** * Just scans the package without any side effects. * <p>Not entirely true at the moment. There is still one side effect -- this * method potentially modifies a live {@link PackageSetting} object representing * the package being scanned. This will be resolved in the future. * + * @param injector injector for acquiring dependencies * @param request Information about the package to be scanned * @param isUnderFactoryTest Whether or not the device is under factory test * @param currentTime The current time, in millis * @return The results of the scan */ @GuardedBy("mInstallLock") - private static @NonNull ScanResult scanPackageOnlyLI(@NonNull ScanRequest request, + @VisibleForTesting + @NonNull + static ScanResult scanPackageOnlyLI(@NonNull ScanRequest request, + Injector injector, boolean isUnderFactoryTest, long currentTime) - throws PackageManagerException { + throws PackageManagerException { + final PackageAbiHelper packageAbiHelper = injector.getAbiHelper(); + final UserManagerInternal userManager = injector.getUserManager(); final PackageParser.Package pkg = request.pkg; PackageSetting pkgSetting = request.pkgSetting; final PackageSetting disabledPkgSetting = request.disabledPkgSetting; @@ -11351,7 +11436,7 @@ public class PackageManagerService extends IPackageManager.Stub if (!createNewPackage) { final boolean instantApp = (scanFlags & SCAN_AS_INSTANT_APP) != 0; final boolean fullApp = (scanFlags & SCAN_AS_FULL_APP) != 0; - setInstantAppForUser(pkgSetting, userId, instantApp, fullApp); + setInstantAppForUser(userManager, pkgSetting, userId, instantApp, fullApp); } // TODO(patb): see if we can do away with disabled check here. if (disabledPkgSetting != null @@ -11397,7 +11482,10 @@ public class PackageManagerService extends IPackageManager.Stub if (needToDeriveAbi) { Trace.traceBegin(TRACE_TAG_PACKAGE_MANAGER, "derivePackageAbi"); final boolean extractNativeLibs = !pkg.isLibrary(); - derivePackageAbi(pkg, cpuAbiOverride, extractNativeLibs); + final Pair<PackageAbiHelper.Abis, PackageAbiHelper.NativeLibraryPaths> derivedAbi = + packageAbiHelper.derivePackageAbi(pkg, cpuAbiOverride, extractNativeLibs); + derivedAbi.first.applyTo(pkg); + derivedAbi.second.applyTo(pkg); Trace.traceEnd(TRACE_TAG_PACKAGE_MANAGER); // Some system apps still use directory structure for native libraries @@ -11405,8 +11493,13 @@ public class PackageManagerService extends IPackageManager.Stub // structure. Try to detect abi based on directory structure. if (isSystemApp(pkg) && !pkg.isUpdatedSystemApp() && pkg.applicationInfo.primaryCpuAbi == null) { - setBundledAppAbisAndRoots(pkg, pkgSetting); - setNativeLibraryPaths(pkg, sAppLib32InstallDir); + final PackageAbiHelper.Abis abis = packageAbiHelper.getBundledAppAbis( + pkg); + abis.applyTo(pkg); + abis.applyTo(pkgSetting); + final PackageAbiHelper.NativeLibraryPaths nativeLibraryPaths = + packageAbiHelper.getNativeLibraryPaths(pkg, sAppLib32InstallDir); + nativeLibraryPaths.applyTo(pkg); } } else { // This is not a first boot or an upgrade, don't bother deriving the @@ -11415,7 +11508,9 @@ public class PackageManagerService extends IPackageManager.Stub pkg.applicationInfo.primaryCpuAbi = primaryCpuAbiFromSettings; pkg.applicationInfo.secondaryCpuAbi = secondaryCpuAbiFromSettings; - setNativeLibraryPaths(pkg, sAppLib32InstallDir); + final PackageAbiHelper.NativeLibraryPaths nativeLibraryPaths = + packageAbiHelper.getNativeLibraryPaths(pkg, sAppLib32InstallDir); + nativeLibraryPaths.applyTo(pkg); if (DEBUG_ABI_SELECTION) { Slog.i(TAG, "Using ABIS and native lib paths from settings : " + @@ -11436,7 +11531,9 @@ public class PackageManagerService extends IPackageManager.Stub // ABIs we've determined above. For non-moves, the path will be updated based on the // ABIs we determined during compilation, but the path will depend on the final // package path (after the rename away from the stage path). - setNativeLibraryPaths(pkg, sAppLib32InstallDir); + final PackageAbiHelper.NativeLibraryPaths nativeLibraryPaths = + packageAbiHelper.getNativeLibraryPaths(pkg, sAppLib32InstallDir); + nativeLibraryPaths.applyTo(pkg); } // This is a special case for the "system" package, where the ABI is @@ -11490,8 +11587,9 @@ public class PackageManagerService extends IPackageManager.Stub // We also do this *before* we perform dexopt on this package, so that // we can avoid redundant dexopts, and also to make sure we've got the // code and package path correct. - changedAbiCodePath = - adjustCpuAbisForSharedUserLPw(pkgSetting.sharedUser.packages, pkg); + changedAbiCodePath = applyAdjustedAbiToSharedUser(pkgSetting.sharedUser, pkg, + packageAbiHelper.getAdjustedAbiForSharedUser( + pkgSetting.sharedUser.packages, pkg)); } if (isUnderFactoryTest && pkg.requestedPermissions.contains( @@ -12344,264 +12442,6 @@ public class PackageManagerService extends IPackageManager.Stub Trace.traceEnd(TRACE_TAG_PACKAGE_MANAGER); } - /** - * Derive the ABI of a non-system package located at {@code scanFile}. This information - * is derived purely on the basis of the contents of {@code scanFile} and - * {@code cpuAbiOverride}. - * - * If {@code extractLibs} is true, native libraries are extracted from the app if required. - */ - private static void derivePackageAbi(PackageParser.Package pkg, String cpuAbiOverride, - boolean extractLibs) - throws PackageManagerException { - // Give ourselves some initial paths; we'll come back for another - // pass once we've determined ABI below. - setNativeLibraryPaths(pkg, sAppLib32InstallDir); - - // We shouldn't attempt to extract libs from system app when it was not updated. - if (isSystemApp(pkg) && !pkg.isUpdatedSystemApp()) { - extractLibs = false; - } - - final String nativeLibraryRootStr = pkg.applicationInfo.nativeLibraryRootDir; - final boolean useIsaSpecificSubdirs = pkg.applicationInfo.nativeLibraryRootRequiresIsa; - - NativeLibraryHelper.Handle handle = null; - try { - handle = NativeLibraryHelper.Handle.create(pkg); - // TODO(multiArch): This can be null for apps that didn't go through the - // usual installation process. We can calculate it again, like we - // do during install time. - // - // TODO(multiArch): Why do we need to rescan ASEC apps again ? It seems totally - // unnecessary. - final File nativeLibraryRoot = new File(nativeLibraryRootStr); - - // Null out the abis so that they can be recalculated. - pkg.applicationInfo.primaryCpuAbi = null; - pkg.applicationInfo.secondaryCpuAbi = null; - if (isMultiArch(pkg.applicationInfo)) { - // Warn if we've set an abiOverride for multi-lib packages.. - // By definition, we need to copy both 32 and 64 bit libraries for - // such packages. - if (pkg.cpuAbiOverride != null - && !NativeLibraryHelper.CLEAR_ABI_OVERRIDE.equals(pkg.cpuAbiOverride)) { - Slog.w(TAG, "Ignoring abiOverride for multi arch application."); - } - - int abi32 = PackageManager.NO_NATIVE_LIBRARIES; - int abi64 = PackageManager.NO_NATIVE_LIBRARIES; - if (Build.SUPPORTED_32_BIT_ABIS.length > 0) { - if (extractLibs) { - Trace.traceBegin(TRACE_TAG_PACKAGE_MANAGER, "copyNativeBinaries"); - abi32 = NativeLibraryHelper.copyNativeBinariesForSupportedAbi(handle, - nativeLibraryRoot, Build.SUPPORTED_32_BIT_ABIS, - useIsaSpecificSubdirs); - } else { - Trace.traceBegin(TRACE_TAG_PACKAGE_MANAGER, "findSupportedAbi"); - abi32 = NativeLibraryHelper.findSupportedAbi(handle, Build.SUPPORTED_32_BIT_ABIS); - } - Trace.traceEnd(TRACE_TAG_PACKAGE_MANAGER); - } - - // Shared library native code should be in the APK zip aligned - if (abi32 >= 0 && pkg.isLibrary() && extractLibs) { - throw new PackageManagerException(INSTALL_FAILED_INTERNAL_ERROR, - "Shared library native lib extraction not supported"); - } - - maybeThrowExceptionForMultiArchCopy( - "Error unpackaging 32 bit native libs for multiarch app.", abi32); - - if (Build.SUPPORTED_64_BIT_ABIS.length > 0) { - if (extractLibs) { - Trace.traceBegin(TRACE_TAG_PACKAGE_MANAGER, "copyNativeBinaries"); - abi64 = NativeLibraryHelper.copyNativeBinariesForSupportedAbi(handle, - nativeLibraryRoot, Build.SUPPORTED_64_BIT_ABIS, - useIsaSpecificSubdirs); - } else { - Trace.traceBegin(TRACE_TAG_PACKAGE_MANAGER, "findSupportedAbi"); - abi64 = NativeLibraryHelper.findSupportedAbi(handle, Build.SUPPORTED_64_BIT_ABIS); - } - Trace.traceEnd(TRACE_TAG_PACKAGE_MANAGER); - } - - maybeThrowExceptionForMultiArchCopy( - "Error unpackaging 64 bit native libs for multiarch app.", abi64); - - if (abi64 >= 0) { - // Shared library native libs should be in the APK zip aligned - if (extractLibs && pkg.isLibrary()) { - throw new PackageManagerException(INSTALL_FAILED_INTERNAL_ERROR, - "Shared library native lib extraction not supported"); - } - pkg.applicationInfo.primaryCpuAbi = Build.SUPPORTED_64_BIT_ABIS[abi64]; - } - - if (abi32 >= 0) { - final String abi = Build.SUPPORTED_32_BIT_ABIS[abi32]; - if (abi64 >= 0) { - if (pkg.use32bitAbi) { - pkg.applicationInfo.secondaryCpuAbi = pkg.applicationInfo.primaryCpuAbi; - pkg.applicationInfo.primaryCpuAbi = abi; - } else { - pkg.applicationInfo.secondaryCpuAbi = abi; - } - } else { - pkg.applicationInfo.primaryCpuAbi = abi; - } - } - } else { - String[] abiList = (cpuAbiOverride != null) ? - new String[] { cpuAbiOverride } : Build.SUPPORTED_ABIS; - - // Enable gross and lame hacks for apps that are built with old - // SDK tools. We must scan their APKs for renderscript bitcode and - // not launch them if it's present. Don't bother checking on devices - // that don't have 64 bit support. - boolean needsRenderScriptOverride = false; - if (Build.SUPPORTED_64_BIT_ABIS.length > 0 && cpuAbiOverride == null && - NativeLibraryHelper.hasRenderscriptBitcode(handle)) { - abiList = Build.SUPPORTED_32_BIT_ABIS; - needsRenderScriptOverride = true; - } - - final int copyRet; - if (extractLibs) { - Trace.traceBegin(TRACE_TAG_PACKAGE_MANAGER, "copyNativeBinaries"); - copyRet = NativeLibraryHelper.copyNativeBinariesForSupportedAbi(handle, - nativeLibraryRoot, abiList, useIsaSpecificSubdirs); - } else { - Trace.traceBegin(TRACE_TAG_PACKAGE_MANAGER, "findSupportedAbi"); - copyRet = NativeLibraryHelper.findSupportedAbi(handle, abiList); - } - Trace.traceEnd(TRACE_TAG_PACKAGE_MANAGER); - - if (copyRet < 0 && copyRet != PackageManager.NO_NATIVE_LIBRARIES) { - throw new PackageManagerException(INSTALL_FAILED_INTERNAL_ERROR, - "Error unpackaging native libs for app, errorCode=" + copyRet); - } - - if (copyRet >= 0) { - // Shared libraries that have native libs must be multi-architecture - if (pkg.isLibrary()) { - throw new PackageManagerException(INSTALL_FAILED_INTERNAL_ERROR, - "Shared library with native libs must be multiarch"); - } - pkg.applicationInfo.primaryCpuAbi = abiList[copyRet]; - } else if (copyRet == PackageManager.NO_NATIVE_LIBRARIES && cpuAbiOverride != null) { - pkg.applicationInfo.primaryCpuAbi = cpuAbiOverride; - } else if (needsRenderScriptOverride) { - pkg.applicationInfo.primaryCpuAbi = abiList[0]; - } - } - } catch (IOException ioe) { - Slog.e(TAG, "Unable to get canonical file " + ioe.toString()); - } finally { - IoUtils.closeQuietly(handle); - } - - // Now that we've calculated the ABIs and determined if it's an internal app, - // we will go ahead and populate the nativeLibraryPath. - setNativeLibraryPaths(pkg, sAppLib32InstallDir); - } - - /** - * Adjusts ABIs for a set of packages belonging to a shared user so that they all match. - * i.e, so that all packages can be run inside a single process if required. - * - * Optionally, callers can pass in a parsed package via {@code newPackage} in which case - * this function will either try and make the ABI for all packages in {@code packagesForUser} - * match {@code scannedPackage} or will update the ABI of {@code scannedPackage} to match - * the ABI selected for {@code packagesForUser}. This variant is used when installing or - * updating a package that belongs to a shared user. - * - * NOTE: We currently only match for the primary CPU abi string. Matching the secondary - * adds unnecessary complexity. - */ - private static @Nullable List<String> adjustCpuAbisForSharedUserLPw( - Set<PackageSetting> packagesForUser, PackageParser.Package scannedPackage) { - List<String> changedAbiCodePath = null; - String requiredInstructionSet = null; - if (scannedPackage != null && scannedPackage.applicationInfo.primaryCpuAbi != null) { - requiredInstructionSet = VMRuntime.getInstructionSet( - scannedPackage.applicationInfo.primaryCpuAbi); - } - - PackageSetting requirer = null; - for (PackageSetting ps : packagesForUser) { - // If packagesForUser contains scannedPackage, we skip it. This will happen - // when scannedPackage is an update of an existing package. Without this check, - // we will never be able to change the ABI of any package belonging to a shared - // user, even if it's compatible with other packages. - if (scannedPackage == null || !scannedPackage.packageName.equals(ps.name)) { - if (ps.primaryCpuAbiString == null) { - continue; - } - - final String instructionSet = VMRuntime.getInstructionSet(ps.primaryCpuAbiString); - if (requiredInstructionSet != null && !instructionSet.equals(requiredInstructionSet)) { - // We have a mismatch between instruction sets (say arm vs arm64) warn about - // this but there's not much we can do. - String errorMessage = "Instruction set mismatch, " - + ((requirer == null) ? "[caller]" : requirer) - + " requires " + requiredInstructionSet + " whereas " + ps - + " requires " + instructionSet; - Slog.w(TAG, errorMessage); - } - - if (requiredInstructionSet == null) { - requiredInstructionSet = instructionSet; - requirer = ps; - } - } - } - - if (requiredInstructionSet != null) { - String adjustedAbi; - if (requirer != null) { - // requirer != null implies that either scannedPackage was null or that scannedPackage - // did not require an ABI, in which case we have to adjust scannedPackage to match - // the ABI of the set (which is the same as requirer's ABI) - adjustedAbi = requirer.primaryCpuAbiString; - if (scannedPackage != null) { - scannedPackage.applicationInfo.primaryCpuAbi = adjustedAbi; - } - } else { - // requirer == null implies that we're updating all ABIs in the set to - // match scannedPackage. - adjustedAbi = scannedPackage.applicationInfo.primaryCpuAbi; - } - - for (PackageSetting ps : packagesForUser) { - if (scannedPackage == null || !scannedPackage.packageName.equals(ps.name)) { - if (ps.primaryCpuAbiString != null) { - continue; - } - - ps.primaryCpuAbiString = adjustedAbi; - if (ps.pkg != null && ps.pkg.applicationInfo != null && - !TextUtils.equals(adjustedAbi, ps.pkg.applicationInfo.primaryCpuAbi)) { - ps.pkg.applicationInfo.primaryCpuAbi = adjustedAbi; - if (DEBUG_ABI_SELECTION) { - Slog.i(TAG, "Adjusting ABI for " + ps.name + " to " + adjustedAbi - + " (requirer=" - + (requirer != null ? requirer.pkg : "null") - + ", scannedPackage=" - + (scannedPackage != null ? scannedPackage : "null") - + ")"); - } - if (changedAbiCodePath == null) { - changedAbiCodePath = new ArrayList<>(); - } - changedAbiCodePath.add(ps.codePathString); - } - } - } - } - return changedAbiCodePath; - } - private void setUpCustomResolverActivity(PackageParser.Package pkg) { synchronized (mPackages) { mResolverReplaced = true; @@ -12653,207 +12493,6 @@ public class PackageManagerService extends IPackageManager.Stub | IntentFilter.MATCH_ADJUSTMENT_NORMAL; } - private static String calculateBundledApkRoot(final String codePathString) { - final File codePath = new File(codePathString); - final File codeRoot; - if (FileUtils.contains(Environment.getRootDirectory(), codePath)) { - codeRoot = Environment.getRootDirectory(); - } else if (FileUtils.contains(Environment.getOemDirectory(), codePath)) { - codeRoot = Environment.getOemDirectory(); - } else if (FileUtils.contains(Environment.getVendorDirectory(), codePath)) { - codeRoot = Environment.getVendorDirectory(); - } else if (FileUtils.contains(Environment.getOdmDirectory(), codePath)) { - codeRoot = Environment.getOdmDirectory(); - } else if (FileUtils.contains(Environment.getProductDirectory(), codePath)) { - codeRoot = Environment.getProductDirectory(); - } else if (FileUtils.contains(Environment.getSystemExtDirectory(), codePath)) { - codeRoot = Environment.getSystemExtDirectory(); - } else if (FileUtils.contains(Environment.getOdmDirectory(), codePath)) { - codeRoot = Environment.getOdmDirectory(); - } else { - // Unrecognized code path; take its top real segment as the apk root: - // e.g. /something/app/blah.apk => /something - try { - File f = codePath.getCanonicalFile(); - File parent = f.getParentFile(); // non-null because codePath is a file - File tmp; - while ((tmp = parent.getParentFile()) != null) { - f = parent; - parent = tmp; - } - codeRoot = f; - Slog.w(TAG, "Unrecognized code path " - + codePath + " - using " + codeRoot); - } catch (IOException e) { - // Can't canonicalize the code path -- shenanigans? - Slog.w(TAG, "Can't canonicalize code path " + codePath); - return Environment.getRootDirectory().getPath(); - } - } - return codeRoot.getPath(); - } - - /** - * Derive and set the location of native libraries for the given package, - * which varies depending on where and how the package was installed. - */ - private static void setNativeLibraryPaths(PackageParser.Package pkg, File appLib32InstallDir) { - final ApplicationInfo info = pkg.applicationInfo; - final String codePath = pkg.codePath; - final File codeFile = new File(codePath); - final boolean bundledApp = info.isSystemApp() && !info.isUpdatedSystemApp(); - - info.nativeLibraryRootDir = null; - info.nativeLibraryRootRequiresIsa = false; - info.nativeLibraryDir = null; - info.secondaryNativeLibraryDir = null; - - if (isApkFile(codeFile)) { - // Monolithic install - if (bundledApp) { - // If "/system/lib64/apkname" exists, assume that is the per-package - // native library directory to use; otherwise use "/system/lib/apkname". - final String apkRoot = calculateBundledApkRoot(info.sourceDir); - final boolean is64Bit = VMRuntime.is64BitInstructionSet( - getPrimaryInstructionSet(info)); - - // This is a bundled system app so choose the path based on the ABI. - // if it's a 64 bit abi, use lib64 otherwise use lib32. Note that this - // is just the default path. - final String apkName = deriveCodePathName(codePath); - final String libDir = is64Bit ? LIB64_DIR_NAME : LIB_DIR_NAME; - info.nativeLibraryRootDir = Environment.buildPath(new File(apkRoot), libDir, - apkName).getAbsolutePath(); - - if (info.secondaryCpuAbi != null) { - final String secondaryLibDir = is64Bit ? LIB_DIR_NAME : LIB64_DIR_NAME; - info.secondaryNativeLibraryDir = Environment.buildPath(new File(apkRoot), - secondaryLibDir, apkName).getAbsolutePath(); - } - } else { - final String apkName = deriveCodePathName(codePath); - info.nativeLibraryRootDir = new File(appLib32InstallDir, apkName) - .getAbsolutePath(); - } - - info.nativeLibraryRootRequiresIsa = false; - info.nativeLibraryDir = info.nativeLibraryRootDir; - } else { - // Cluster install - info.nativeLibraryRootDir = new File(codeFile, LIB_DIR_NAME).getAbsolutePath(); - info.nativeLibraryRootRequiresIsa = true; - - info.nativeLibraryDir = new File(info.nativeLibraryRootDir, - getPrimaryInstructionSet(info)).getAbsolutePath(); - - if (info.secondaryCpuAbi != null) { - info.secondaryNativeLibraryDir = new File(info.nativeLibraryRootDir, - VMRuntime.getInstructionSet(info.secondaryCpuAbi)).getAbsolutePath(); - } - } - } - - /** - * Calculate the abis and roots for a bundled app. These can uniquely - * be determined from the contents of the system partition, i.e whether - * it contains 64 or 32 bit shared libraries etc. We do not validate any - * of this information, and instead assume that the system was built - * sensibly. - */ - private static void setBundledAppAbisAndRoots(PackageParser.Package pkg, - PackageSetting pkgSetting) { - final String apkName = deriveCodePathName(pkg.applicationInfo.getCodePath()); - - // If "/system/lib64/apkname" exists, assume that is the per-package - // native library directory to use; otherwise use "/system/lib/apkname". - final String apkRoot = calculateBundledApkRoot(pkg.applicationInfo.sourceDir); - setBundledAppAbi(pkg, apkRoot, apkName); - // pkgSetting might be null during rescan following uninstall of updates - // to a bundled app, so accommodate that possibility. The settings in - // that case will be established later from the parsed package. - // - // If the settings aren't null, sync them up with what we've just derived. - // note that apkRoot isn't stored in the package settings. - if (pkgSetting != null) { - pkgSetting.primaryCpuAbiString = pkg.applicationInfo.primaryCpuAbi; - pkgSetting.secondaryCpuAbiString = pkg.applicationInfo.secondaryCpuAbi; - } - } - - /** - * Deduces the ABI of a bundled app and sets the relevant fields on the - * parsed pkg object. - * - * @param apkRoot the root of the installed apk, something like {@code /system} or {@code /oem} - * under which system libraries are installed. - * @param apkName the name of the installed package. - */ - private static void setBundledAppAbi(PackageParser.Package pkg, String apkRoot, String apkName) { - final File codeFile = new File(pkg.codePath); - - final boolean has64BitLibs; - final boolean has32BitLibs; - if (isApkFile(codeFile)) { - // Monolithic install - has64BitLibs = (new File(apkRoot, new File(LIB64_DIR_NAME, apkName).getPath())).exists(); - has32BitLibs = (new File(apkRoot, new File(LIB_DIR_NAME, apkName).getPath())).exists(); - } else { - // Cluster install - final File rootDir = new File(codeFile, LIB_DIR_NAME); - if (!ArrayUtils.isEmpty(Build.SUPPORTED_64_BIT_ABIS) - && !TextUtils.isEmpty(Build.SUPPORTED_64_BIT_ABIS[0])) { - final String isa = VMRuntime.getInstructionSet(Build.SUPPORTED_64_BIT_ABIS[0]); - has64BitLibs = (new File(rootDir, isa)).exists(); - } else { - has64BitLibs = false; - } - if (!ArrayUtils.isEmpty(Build.SUPPORTED_32_BIT_ABIS) - && !TextUtils.isEmpty(Build.SUPPORTED_32_BIT_ABIS[0])) { - final String isa = VMRuntime.getInstructionSet(Build.SUPPORTED_32_BIT_ABIS[0]); - has32BitLibs = (new File(rootDir, isa)).exists(); - } else { - has32BitLibs = false; - } - } - - if (has64BitLibs && !has32BitLibs) { - // The package has 64 bit libs, but not 32 bit libs. Its primary - // ABI should be 64 bit. We can safely assume here that the bundled - // native libraries correspond to the most preferred ABI in the list. - - pkg.applicationInfo.primaryCpuAbi = Build.SUPPORTED_64_BIT_ABIS[0]; - pkg.applicationInfo.secondaryCpuAbi = null; - } else if (has32BitLibs && !has64BitLibs) { - // The package has 32 bit libs but not 64 bit libs. Its primary - // ABI should be 32 bit. - - pkg.applicationInfo.primaryCpuAbi = Build.SUPPORTED_32_BIT_ABIS[0]; - pkg.applicationInfo.secondaryCpuAbi = null; - } else if (has32BitLibs && has64BitLibs) { - // The application has both 64 and 32 bit bundled libraries. We check - // here that the app declares multiArch support, and warn if it doesn't. - // - // We will be lenient here and record both ABIs. The primary will be the - // ABI that's higher on the list, i.e, a device that's configured to prefer - // 64 bit apps will see a 64 bit primary ABI, - - if ((pkg.applicationInfo.flags & ApplicationInfo.FLAG_MULTIARCH) == 0) { - Slog.e(TAG, "Package " + pkg + " has multiple bundled libs, but is not multiarch."); - } - - if (VMRuntime.is64BitInstructionSet(getPreferredInstructionSet())) { - pkg.applicationInfo.primaryCpuAbi = Build.SUPPORTED_64_BIT_ABIS[0]; - pkg.applicationInfo.secondaryCpuAbi = Build.SUPPORTED_32_BIT_ABIS[0]; - } else { - pkg.applicationInfo.primaryCpuAbi = Build.SUPPORTED_32_BIT_ABIS[0]; - pkg.applicationInfo.secondaryCpuAbi = Build.SUPPORTED_64_BIT_ABIS[0]; - } - } else { - pkg.applicationInfo.primaryCpuAbi = null; - pkg.applicationInfo.secondaryCpuAbi = null; - } - } - private void killApplication(String pkgName, int appId, String reason) { killApplication(pkgName, appId, UserHandle.USER_ALL, reason); } @@ -13575,7 +13214,8 @@ public class PackageManagerService extends IPackageManager.Stub // upgrade app from instant to full; we don't allow app downgrade installed = true; } - setInstantAppForUser(pkgSetting, userId, instantApp, fullApp); + setInstantAppForUser( + getUserManagerInternal(), pkgSetting, userId, instantApp, fullApp); } if (installed) { @@ -13623,8 +13263,8 @@ public class PackageManagerService extends IPackageManager.Stub } } - static void setInstantAppForUser(PackageSetting pkgSetting, int userId, - boolean instantApp, boolean fullApp) { + static void setInstantAppForUser(UserManagerInternal userManager, PackageSetting pkgSetting, + int userId, boolean instantApp, boolean fullApp) { // no state specified; do nothing if (!instantApp && !fullApp) { return; @@ -13636,7 +13276,7 @@ public class PackageManagerService extends IPackageManager.Stub pkgSetting.setInstantApp(false /*instantApp*/, userId); } } else { - for (int currentUserId : sUserManager.getUserIds()) { + for (int currentUserId : userManager.getUserIds()) { if (instantApp && !pkgSetting.getInstantApp(currentUserId)) { pkgSetting.setInstantApp(true /*instantApp*/, currentUserId); } else if (fullApp && pkgSetting.getInstantApp(currentUserId)) { @@ -15910,16 +15550,6 @@ public class PackageManagerService extends IPackageManager.Stub } } - private static void maybeThrowExceptionForMultiArchCopy(String message, int copyRet) throws - PackageManagerException { - if (copyRet < 0) { - if (copyRet != PackageManager.NO_NATIVE_LIBRARIES && - copyRet != PackageManager.INSTALL_FAILED_NO_MATCHING_ABIS) { - throw new PackageManagerException(copyRet, message); - } - } - } - /** * Logic to handle movement of existing installed applications. */ @@ -16046,26 +15676,6 @@ public class PackageManagerService extends IPackageManager.Stub return result; } - // Utility method that returns the relative package path with respect - // to the installation directory. Like say for /data/data/com.test-1.apk - // string com.test-1 is returned. - static String deriveCodePathName(String codePath) { - if (codePath == null) { - return null; - } - final File codeFile = new File(codePath); - final String name = codeFile.getName(); - if (codeFile.isDirectory()) { - return name; - } else if (name.endsWith(".apk") || name.endsWith(".tmp")) { - final int lastDot = name.lastIndexOf('.'); - return name.substring(0, lastDot); - } else { - Slog.w(TAG, "Odd, " + codePath + " doesn't look like an APK"); - return null; - } - } - static class PackageInstalledInfo { String name; int uid; @@ -17000,7 +16610,8 @@ public class PackageManagerService extends IPackageManager.Stub final PrepareResult prepareResult; try { Trace.traceBegin(TRACE_TAG_PACKAGE_MANAGER, "preparePackage"); - prepareResult = preparePackageLI(request.args, request.installResult); + prepareResult = + preparePackageLI(request.args, request.installResult); } catch (PrepareFailure prepareFailure) { request.installResult.setError(prepareFailure.error, prepareFailure.getMessage()); @@ -17660,7 +17271,11 @@ public class PackageManagerService extends IPackageManager.Stub String abiOverride = (TextUtils.isEmpty(pkg.cpuAbiOverride) ? args.abiOverride : pkg.cpuAbiOverride); final boolean extractNativeLibs = !pkg.isLibrary(); - derivePackageAbi(pkg, abiOverride, extractNativeLibs); + final Pair<PackageAbiHelper.Abis, PackageAbiHelper.NativeLibraryPaths> + derivedAbi = mInjector.getAbiHelper().derivePackageAbi( + pkg, abiOverride, extractNativeLibs); + derivedAbi.first.applyTo(pkg); + derivedAbi.second.applyTo(pkg); } catch (PackageManagerException pme) { Slog.e(TAG, "Error deriving application ABI", pme); throw new PrepareFailure(INSTALL_FAILED_INTERNAL_ERROR, @@ -18189,10 +17804,6 @@ public class PackageManagerService extends IPackageManager.Stub } } - private static boolean isMultiArch(ApplicationInfo info) { - return (info.flags & ApplicationInfo.FLAG_MULTIARCH) != 0; - } - private static boolean isExternal(PackageParser.Package pkg) { return (pkg.applicationInfo.flags & ApplicationInfo.FLAG_EXTERNAL_STORAGE) != 0; } @@ -18201,7 +17812,7 @@ public class PackageManagerService extends IPackageManager.Stub return (ps.pkgFlags & ApplicationInfo.FLAG_EXTERNAL_STORAGE) != 0; } - private static boolean isSystemApp(PackageParser.Package pkg) { + static boolean isSystemApp(PackageParser.Package pkg) { return (pkg.applicationInfo.flags & ApplicationInfo.FLAG_SYSTEM) != 0; } @@ -24560,6 +24171,12 @@ public class PackageManagerService extends IPackageManager.Stub public List<ResolveInfo> queryIntentActivities( Intent intent, int flags, int filterCallingUid, int userId) { final String resolvedType = intent.resolveTypeIfNeeded(mContext.getContentResolver()); + return queryIntentActivities(intent, resolvedType, flags, filterCallingUid, userId); + } + + @Override + public List<ResolveInfo> queryIntentActivities( + Intent intent, String resolvedType, int flags, int filterCallingUid, int userId) { return PackageManagerService.this .queryIntentActivitiesInternal(intent, resolvedType, flags, filterCallingUid, userId, false /*resolveForStart*/, true /*allowDynamicSplits*/); diff --git a/services/core/java/com/android/server/pm/PackageSettingBase.java b/services/core/java/com/android/server/pm/PackageSettingBase.java index 58f262c4c889..029673ffd87b 100644 --- a/services/core/java/com/android/server/pm/PackageSettingBase.java +++ b/services/core/java/com/android/server/pm/PackageSettingBase.java @@ -270,7 +270,8 @@ public abstract class PackageSettingBase extends SettingBase { updateAvailable = orig.updateAvailable; } - private PackageUserState modifyUserState(int userId) { + @VisibleForTesting + PackageUserState modifyUserState(int userId) { PackageUserState state = mUserState.get(userId); if (state == null) { state = new PackageUserState(); @@ -463,6 +464,18 @@ public abstract class PackageSettingBase extends SettingBase { state.harmfulAppWarning = harmfulAppWarning; } + void setUserState(int userId, PackageUserState otherState) { + setUserState(userId, otherState.ceDataInode, otherState.enabled, otherState.installed, + otherState.stopped, otherState.notLaunched, otherState.hidden, + otherState.distractionFlags, otherState.suspended, otherState.suspendingPackage, + otherState.dialogInfo, otherState.suspendedAppExtras, + otherState.suspendedLauncherExtras, otherState.instantApp, + otherState.virtualPreload, otherState.lastDisableAppCaller, + otherState.enabledComponents, otherState.disabledComponents, + otherState.domainVerificationStatus, otherState.appLinkGeneration, + otherState.installReason, otherState.harmfulAppWarning); + } + ArraySet<String> getEnabledComponents(int userId) { return readUserState(userId).enabledComponents; } diff --git a/services/core/java/com/android/server/pm/Settings.java b/services/core/java/com/android/server/pm/Settings.java index 1336d407631b..0c582ca853f5 100644 --- a/services/core/java/com/android/server/pm/Settings.java +++ b/services/core/java/com/android/server/pm/Settings.java @@ -3341,7 +3341,8 @@ public final class Settings { int flags, ComponentName cn, String scheme, PatternMatcher ssp, IntentFilter.AuthorityEntry auth, PatternMatcher path, int userId) { final List<ResolveInfo> ri = - pmInternal.queryIntentActivities(intent, flags, Binder.getCallingUid(), 0); + pmInternal.queryIntentActivities( + intent, intent.getType(), flags, Binder.getCallingUid(), 0); if (PackageManagerService.DEBUG_PREFERRED) { Log.d(TAG, "Queried " + intent + " results: " + ri); } diff --git a/services/core/java/com/android/server/pm/TEST_MAPPING b/services/core/java/com/android/server/pm/TEST_MAPPING index 15dc6ae36656..0101366e48b4 100644 --- a/services/core/java/com/android/server/pm/TEST_MAPPING +++ b/services/core/java/com/android/server/pm/TEST_MAPPING @@ -8,6 +8,20 @@ }, { "name": "CtsCompilationTestCases" + }, + { + "name": "FrameworksServicesTests", + "options": [ + { + "include-filter": "com.android.server.pm." + }, + { + "include-annotation": "android.platform.test.annotations.Presubmit" + }, + { + "exclude-annotation": "androidx.test.filters.FlakyTest" + } + ] } ], "imports": [ diff --git a/services/core/jni/Android.bp b/services/core/jni/Android.bp index 6175d4183020..7b3fbb97da56 100644 --- a/services/core/jni/Android.bp +++ b/services/core/jni/Android.bp @@ -130,7 +130,6 @@ cc_defaults { "android.hardware.vibrator@1.1", "android.hardware.vibrator@1.2", "android.hardware.vibrator@1.3", - "android.hardware.vibrator@1.4", "android.hardware.vr@1.0", "android.frameworks.schedulerservice@1.0", "android.frameworks.sensorservice@1.0", diff --git a/services/core/jni/com_android_server_VibratorService.cpp b/services/core/jni/com_android_server_VibratorService.cpp index 372622801aa0..746610df11ae 100644 --- a/services/core/jni/com_android_server_VibratorService.cpp +++ b/services/core/jni/com_android_server_VibratorService.cpp @@ -16,7 +16,7 @@ #define LOG_TAG "VibratorService" -#include <android/hardware/vibrator/1.4/IVibrator.h> +#include <android/hardware/vibrator/1.3/IVibrator.h> #include <android/hardware/vibrator/BnVibratorCallback.h> #include <android/hardware/vibrator/IVibrator.h> #include <binder/IServiceManager.h> @@ -43,166 +43,41 @@ namespace V1_0 = android::hardware::vibrator::V1_0; namespace V1_1 = android::hardware::vibrator::V1_1; namespace V1_2 = android::hardware::vibrator::V1_2; namespace V1_3 = android::hardware::vibrator::V1_3; -namespace V1_4 = android::hardware::vibrator::V1_4; namespace aidl = android::hardware::vibrator; namespace android { static jmethodID sMethodIdOnComplete; -// TODO(b/141828236): remove HIDL 1.4 and re-write all of this code to remove -// shim -class VibratorShim : public V1_4::IVibrator { - public: - VibratorShim(const sp<aidl::IVibrator>& vib) : mVib(vib) {} - - Return<V1_0::Status> on(uint32_t timeoutMs) override { - return on_1_4(timeoutMs, nullptr); - } - - Return<V1_0::Status> off() override { - return toHidlStatus(mVib->off()); - } - - Return<bool> supportsAmplitudeControl() override { - int32_t cap = 0; - if (!mVib->getCapabilities(&cap).isOk()) return false; - if (mUnderExternalControl) { - return (cap & aidl::IVibrator::CAP_EXTERNAL_AMPLITUDE_CONTROL) > 0; - } else { - return (cap & aidl::IVibrator::CAP_AMPLITUDE_CONTROL) > 0; - } - } - - Return<V1_0::Status> setAmplitude(uint8_t amplitude) override { - return toHidlStatus(mVib->setAmplitude(amplitude)); - } - - Return<void> perform(V1_0::Effect effect, V1_0::EffectStrength strength, - perform_cb _hidl_cb) override { - return perform_1_4(static_cast<V1_3::Effect>(effect), strength, nullptr, _hidl_cb); - } - - Return<void> perform_1_1(V1_1::Effect_1_1 effect, V1_0::EffectStrength strength, - perform_1_1_cb _hidl_cb) override { - return perform_1_4(static_cast<V1_3::Effect>(effect), strength, nullptr, _hidl_cb); - } - - Return<void> perform_1_2(V1_2::Effect effect, V1_0::EffectStrength strength, - perform_1_2_cb _hidl_cb) override { - return perform_1_4(static_cast<V1_3::Effect>(effect), strength, nullptr, _hidl_cb); - } - - Return<bool> supportsExternalControl() override { - int32_t cap = 0; - if (!mVib->getCapabilities(&cap).isOk()) return false; - return (cap & aidl::IVibrator::CAP_EXTERNAL_CONTROL) > 0; - } - - Return<V1_0::Status> setExternalControl(bool enabled) override { - Return<V1_0::Status> status = toHidlStatus(mVib->setExternalControl(enabled)); - if (status.isOk() && status == V1_0::Status::OK) { - mUnderExternalControl = enabled; - } - return status; - } - - Return<void> perform_1_3(V1_3::Effect effect, V1_0::EffectStrength strength, - perform_1_3_cb _hidl_cb) override { - return perform_1_4(static_cast<V1_3::Effect>(effect), strength, nullptr, _hidl_cb); - } - - Return<uint32_t> getCapabilities() override { - static_assert(static_cast<int32_t>(V1_4::Capabilities::ON_COMPLETION_CALLBACK) == - static_cast<int32_t>(aidl::IVibrator::CAP_ON_CALLBACK)); - static_assert(static_cast<int32_t>(V1_4::Capabilities::PERFORM_COMPLETION_CALLBACK) == - static_cast<int32_t>(aidl::IVibrator::CAP_PERFORM_CALLBACK)); - - int32_t cap; - if (!mVib->getCapabilities(&cap).isOk()) return 0; - return (cap & (aidl::IVibrator::CAP_ON_CALLBACK | - aidl::IVibrator::CAP_PERFORM_CALLBACK)) > 0; - } - - Return<V1_0::Status> on_1_4(uint32_t timeoutMs, - const sp<V1_4::IVibratorCallback>& callback) override { - sp<aidl::IVibratorCallback> cb = callback ? new CallbackShim(callback) : nullptr; - return toHidlStatus(mVib->on(timeoutMs, cb)); - } - - Return<void> perform_1_4(V1_3::Effect effect, V1_0::EffectStrength strength, - const sp<V1_4::IVibratorCallback>& callback, - perform_1_4_cb _hidl_cb) override { - static_assert(static_cast<uint8_t>(V1_0::EffectStrength::LIGHT) == - static_cast<uint8_t>(aidl::EffectStrength::LIGHT)); - static_assert(static_cast<uint8_t>(V1_0::EffectStrength::MEDIUM) == - static_cast<uint8_t>(aidl::EffectStrength::MEDIUM)); - static_assert(static_cast<uint8_t>(V1_0::EffectStrength::STRONG) == - static_cast<uint8_t>(aidl::EffectStrength::STRONG)); - static_assert(static_cast<uint8_t>(V1_3::Effect::CLICK) == - static_cast<uint8_t>(aidl::Effect::CLICK)); - static_assert(static_cast<uint8_t>(V1_3::Effect::DOUBLE_CLICK) == - static_cast<uint8_t>(aidl::Effect::DOUBLE_CLICK)); - static_assert(static_cast<uint8_t>(V1_3::Effect::TICK) == - static_cast<uint8_t>(aidl::Effect::TICK)); - static_assert(static_cast<uint8_t>(V1_3::Effect::THUD) == - static_cast<uint8_t>(aidl::Effect::THUD)); - static_assert(static_cast<uint8_t>(V1_3::Effect::POP) == - static_cast<uint8_t>(aidl::Effect::POP)); - static_assert(static_cast<uint8_t>(V1_3::Effect::HEAVY_CLICK) == - static_cast<uint8_t>(aidl::Effect::HEAVY_CLICK)); - static_assert(static_cast<uint8_t>(V1_3::Effect::RINGTONE_1) == - static_cast<uint8_t>(aidl::Effect::RINGTONE_1)); - static_assert(static_cast<uint8_t>(V1_3::Effect::RINGTONE_2) == - static_cast<uint8_t>(aidl::Effect::RINGTONE_2)); - static_assert(static_cast<uint8_t>(V1_3::Effect::RINGTONE_15) == - static_cast<uint8_t>(aidl::Effect::RINGTONE_15)); - static_assert(static_cast<uint8_t>(V1_3::Effect::TEXTURE_TICK) == - static_cast<uint8_t>(aidl::Effect::TEXTURE_TICK)); - - sp<aidl::IVibratorCallback> cb = callback ? new CallbackShim(callback) : nullptr; - int timeoutMs = 0; - Return<V1_0::Status> status = toHidlStatus( - mVib->perform(static_cast<aidl::Effect>(effect), - static_cast<aidl::EffectStrength>(strength), cb, &timeoutMs)); - - if (status.isOk()) { - _hidl_cb(status, timeoutMs); - return android::hardware::Status::ok(); - } else { - return android::hardware::details::StatusOf<V1_0::Status, void>(status); - } - } - private: - sp<aidl::IVibrator> mVib; - bool mUnderExternalControl = false; - - Return<V1_0::Status> toHidlStatus(const android::binder::Status& status) { - switch(status.exceptionCode()) { - using android::hardware::Status; - case Status::EX_NONE: return V1_0::Status::OK; - case Status::EX_ILLEGAL_ARGUMENT: return V1_0::Status::BAD_VALUE; - case Status::EX_UNSUPPORTED_OPERATION: return V1_0::Status::UNSUPPORTED_OPERATION; - case Status::EX_TRANSACTION_FAILED: { - return Status::fromStatusT(status.transactionError()); - } - } - return V1_0::Status::UNKNOWN_ERROR; - } - - class CallbackShim : public aidl::BnVibratorCallback { - public: - CallbackShim(const sp<V1_4::IVibratorCallback>& cb) : mCb(cb) {} - binder::Status onComplete() { - mCb->onComplete(); - return binder::Status::ok(); // oneway, local call - } - private: - sp<V1_4::IVibratorCallback> mCb; - }; -}; - -class VibratorCallback : public V1_4::IVibratorCallback { +static_assert(static_cast<uint8_t>(V1_0::EffectStrength::LIGHT) == + static_cast<uint8_t>(aidl::EffectStrength::LIGHT)); +static_assert(static_cast<uint8_t>(V1_0::EffectStrength::MEDIUM) == + static_cast<uint8_t>(aidl::EffectStrength::MEDIUM)); +static_assert(static_cast<uint8_t>(V1_0::EffectStrength::STRONG) == + static_cast<uint8_t>(aidl::EffectStrength::STRONG)); + +static_assert(static_cast<uint8_t>(V1_3::Effect::CLICK) == + static_cast<uint8_t>(aidl::Effect::CLICK)); +static_assert(static_cast<uint8_t>(V1_3::Effect::DOUBLE_CLICK) == + static_cast<uint8_t>(aidl::Effect::DOUBLE_CLICK)); +static_assert(static_cast<uint8_t>(V1_3::Effect::TICK) == + static_cast<uint8_t>(aidl::Effect::TICK)); +static_assert(static_cast<uint8_t>(V1_3::Effect::THUD) == + static_cast<uint8_t>(aidl::Effect::THUD)); +static_assert(static_cast<uint8_t>(V1_3::Effect::POP) == + static_cast<uint8_t>(aidl::Effect::POP)); +static_assert(static_cast<uint8_t>(V1_3::Effect::HEAVY_CLICK) == + static_cast<uint8_t>(aidl::Effect::HEAVY_CLICK)); +static_assert(static_cast<uint8_t>(V1_3::Effect::RINGTONE_1) == + static_cast<uint8_t>(aidl::Effect::RINGTONE_1)); +static_assert(static_cast<uint8_t>(V1_3::Effect::RINGTONE_2) == + static_cast<uint8_t>(aidl::Effect::RINGTONE_2)); +static_assert(static_cast<uint8_t>(V1_3::Effect::RINGTONE_15) == + static_cast<uint8_t>(aidl::Effect::RINGTONE_15)); +static_assert(static_cast<uint8_t>(V1_3::Effect::TEXTURE_TICK) == + static_cast<uint8_t>(aidl::Effect::TEXTURE_TICK)); + +class VibratorCallback { public: VibratorCallback(JNIEnv *env, jobject vibration) : mVibration(MakeGlobalRefOrDie(env, vibration)) {} @@ -212,47 +87,92 @@ class VibratorCallback : public V1_4::IVibratorCallback { env->DeleteGlobalRef(mVibration); } - Return<void> onComplete() override { + void onComplete() { auto env = AndroidRuntime::getJNIEnv(); env->CallVoidMethod(mVibration, sMethodIdOnComplete); - return Void(); } private: jobject mVibration; }; +class AidlVibratorCallback : public aidl::BnVibratorCallback { + public: + AidlVibratorCallback(JNIEnv *env, jobject vibration) : + mCb(env, vibration) {} + + binder::Status onComplete() override { + mCb.onComplete(); + return binder::Status::ok(); // oneway, local call + } + + private: + VibratorCallback mCb; +}; + static constexpr int NUM_TRIES = 2; +template<class R> +inline R NoneStatus() { + using ::android::hardware::Status; + return Status::fromExceptionCode(Status::EX_NONE); +} + +template<> +inline binder::Status NoneStatus() { + using binder::Status; + return Status::fromExceptionCode(Status::EX_NONE); +} + // Creates a Return<R> with STATUS::EX_NULL_POINTER. template<class R> -inline Return<R> NullptrStatus() { +inline R NullptrStatus() { using ::android::hardware::Status; - return Return<R>{Status::fromExceptionCode(Status::EX_NULL_POINTER)}; + return Status::fromExceptionCode(Status::EX_NULL_POINTER); +} + +template<> +inline binder::Status NullptrStatus() { + using binder::Status; + return Status::fromExceptionCode(Status::EX_NULL_POINTER); +} + +template <typename I> +sp<I> getService() { + return I::getService(); +} + +template <> +sp<aidl::IVibrator> getService() { + return waitForVintfService<aidl::IVibrator>(); +} + +template <typename I> +sp<I> tryGetService() { + return I::tryGetService(); +} + +template <> +sp<aidl::IVibrator> tryGetService() { + return checkVintfService<aidl::IVibrator>(); } template <typename I> class HalWrapper { public: static std::unique_ptr<HalWrapper> Create() { - sp<aidl::IVibrator> aidlVib = waitForVintfService<aidl::IVibrator>(); - if (aidlVib) { - return std::unique_ptr<HalWrapper>(new HalWrapper(new VibratorShim(aidlVib))); - } - // Assume that if getService returns a nullptr, HAL is not available on the // device. - auto hal = I::getService(); + auto hal = getService<I>(); return hal ? std::unique_ptr<HalWrapper>(new HalWrapper(std::move(hal))) : nullptr; } // Helper used to transparently deal with the vibrator HAL becoming unavailable. template<class R, class... Args0, class... Args1> - Return<R> call(Return<R> (I::* fn)(Args0...), Args1&&... args1) { + R call(R (I::* fn)(Args0...), Args1&&... args1) { // Return<R> doesn't have a default constructor, so make a Return<R> with // STATUS::EX_NONE. - using ::android::hardware::Status; - Return<R> ret{Status::fromExceptionCode(Status::EX_NONE)}; + R ret{NoneStatus<R>()}; // Note that ret is guaranteed to be changed after this loop. for (int i = 0; i < NUM_TRIES; ++i) { @@ -266,12 +186,7 @@ class HalWrapper { ALOGE("Failed to issue command to vibrator HAL. Retrying."); // Restoring connection to the HAL. - sp<aidl::IVibrator> aidlVib = checkVintfService<aidl::IVibrator>(); - if (aidlVib) { - mHal = new VibratorShim(aidlVib); - } else { - mHal = I::tryGetService(); - } + mHal = tryGetService<I>(); } return ret; } @@ -290,7 +205,7 @@ static auto getHal() { } template<class R, class I, class... Args0, class... Args1> -Return<R> halCall(Return<R> (I::* fn)(Args0...), Args1&&... args1) { +R halCall(R (I::* fn)(Args0...), Args1&&... args1) { auto hal = getHal<I>(); return hal ? hal->call(fn, std::forward<Args1>(args1)...) : NullptrStatus<R>(); } @@ -307,110 +222,192 @@ bool isValidEffect(jlong effect) { static void vibratorInit(JNIEnv *env, jclass clazz) { - halCall(&V1_0::IVibrator::ping).isOk(); + if (auto hal = getHal<aidl::IVibrator>()) { + // IBinder::pingBinder isn't accessible as a pointer function + // but getCapabilities can serve the same purpose + int32_t cap; + hal->call(&aidl::IVibrator::getCapabilities, &cap).isOk(); + } else { + halCall(&V1_0::IVibrator::ping).isOk(); + } } static jboolean vibratorExists(JNIEnv* /* env */, jclass /* clazz */) { - return halCall(&V1_0::IVibrator::ping).isOk() ? JNI_TRUE : JNI_FALSE; + bool ok; + + if (auto hal = getHal<aidl::IVibrator>()) { + // IBinder::pingBinder isn't accessible as a pointer function + // but getCapabilities can serve the same purpose + int32_t cap; + ok = hal->call(&aidl::IVibrator::getCapabilities, &cap).isOk(); + } else { + ok = halCall(&V1_0::IVibrator::ping).isOk(); + } + return ok ? JNI_TRUE : JNI_FALSE; } static void vibratorOn(JNIEnv* /* env */, jclass /* clazz */, jlong timeout_ms) { - Status retStatus = halCall(&V1_0::IVibrator::on, timeout_ms).withDefault(Status::UNKNOWN_ERROR); - if (retStatus != Status::OK) { - ALOGE("vibratorOn command failed (%" PRIu32 ").", static_cast<uint32_t>(retStatus)); + if (auto hal = getHal<aidl::IVibrator>()) { + auto status = hal->call(&aidl::IVibrator::on, timeout_ms, nullptr); + if (!status.isOk()) { + ALOGE("vibratorOn command failed: %s", status.toString8().string()); + } + } else { + Status retStatus = halCall(&V1_0::IVibrator::on, timeout_ms).withDefault(Status::UNKNOWN_ERROR); + if (retStatus != Status::OK) { + ALOGE("vibratorOn command failed (%" PRIu32 ").", static_cast<uint32_t>(retStatus)); + } } } static void vibratorOff(JNIEnv* /* env */, jclass /* clazz */) { - Status retStatus = halCall(&V1_0::IVibrator::off).withDefault(Status::UNKNOWN_ERROR); - if (retStatus != Status::OK) { - ALOGE("vibratorOff command failed (%" PRIu32 ").", static_cast<uint32_t>(retStatus)); + if (auto hal = getHal<aidl::IVibrator>()) { + auto status = hal->call(&aidl::IVibrator::off); + if (!status.isOk()) { + ALOGE("vibratorOff command failed: %s", status.toString8().string()); + } + } else { + Status retStatus = halCall(&V1_0::IVibrator::off).withDefault(Status::UNKNOWN_ERROR); + if (retStatus != Status::OK) { + ALOGE("vibratorOff command failed (%" PRIu32 ").", static_cast<uint32_t>(retStatus)); + } } } static jlong vibratorSupportsAmplitudeControl(JNIEnv*, jclass) { - return halCall(&V1_0::IVibrator::supportsAmplitudeControl).withDefault(false); + if (auto hal = getHal<aidl::IVibrator>()) { + int32_t cap = 0; + if (!hal->call(&aidl::IVibrator::getCapabilities, &cap).isOk()) { + return false; + } + return (cap & aidl::IVibrator::CAP_AMPLITUDE_CONTROL) > 0; + } else { + return halCall(&V1_0::IVibrator::supportsAmplitudeControl).withDefault(false); + } } static void vibratorSetAmplitude(JNIEnv*, jclass, jint amplitude) { - Status status = halCall(&V1_0::IVibrator::setAmplitude, static_cast<uint32_t>(amplitude)) - .withDefault(Status::UNKNOWN_ERROR); - if (status != Status::OK) { - ALOGE("Failed to set vibrator amplitude (%" PRIu32 ").", - static_cast<uint32_t>(status)); + if (auto hal = getHal<aidl::IVibrator>()) { + auto status = hal->call(&aidl::IVibrator::IVibrator::setAmplitude, amplitude); + if (!status.isOk()) { + ALOGE("Failed to set vibrator amplitude: %s", status.toString8().string()); + } + } else { + Status status = halCall(&V1_0::IVibrator::setAmplitude, static_cast<uint32_t>(amplitude)) + .withDefault(Status::UNKNOWN_ERROR); + if (status != Status::OK) { + ALOGE("Failed to set vibrator amplitude (%" PRIu32 ").", + static_cast<uint32_t>(status)); + } } } static jboolean vibratorSupportsExternalControl(JNIEnv*, jclass) { - return halCall(&V1_3::IVibrator::supportsExternalControl).withDefault(false); + if (auto hal = getHal<aidl::IVibrator>()) { + int32_t cap = 0; + if (!hal->call(&aidl::IVibrator::getCapabilities, &cap).isOk()) { + return false; + } + return (cap & aidl::IVibrator::CAP_EXTERNAL_CONTROL) > 0; + } else { + return halCall(&V1_3::IVibrator::supportsExternalControl).withDefault(false); + } } static void vibratorSetExternalControl(JNIEnv*, jclass, jboolean enabled) { - Status status = halCall(&V1_3::IVibrator::setExternalControl, static_cast<uint32_t>(enabled)) - .withDefault(Status::UNKNOWN_ERROR); - if (status != Status::OK) { - ALOGE("Failed to set vibrator external control (%" PRIu32 ").", - static_cast<uint32_t>(status)); + if (auto hal = getHal<aidl::IVibrator>()) { + auto status = hal->call(&aidl::IVibrator::IVibrator::setExternalControl, enabled); + if (!status.isOk()) { + ALOGE("Failed to set vibrator external control: %s", status.toString8().string()); + } + } else { + Status status = halCall(&V1_3::IVibrator::setExternalControl, static_cast<uint32_t>(enabled)) + .withDefault(Status::UNKNOWN_ERROR); + if (status != Status::OK) { + ALOGE("Failed to set vibrator external control (%" PRIu32 ").", + static_cast<uint32_t>(status)); + } } } static jlong vibratorPerformEffect(JNIEnv* env, jclass, jlong effect, jlong strength, jobject vibration) { - Status status; - uint32_t lengthMs; - auto callback = [&status, &lengthMs](Status retStatus, uint32_t retLengthMs) { - status = retStatus; - lengthMs = retLengthMs; - }; - EffectStrength effectStrength(static_cast<EffectStrength>(strength)); - - Return<void> ret; - if (auto hal = getHal<V1_4::IVibrator>(); hal && isValidEffect<V1_3::Effect>(effect)) { - sp<VibratorCallback> effectCallback = new VibratorCallback(env, vibration); - ret = hal->call(&V1_4::IVibrator::perform_1_4, static_cast<V1_3::Effect>(effect), - effectStrength, effectCallback, callback); - } else if (isValidEffect<V1_0::Effect>(effect)) { - ret = halCall(&V1_0::IVibrator::perform, static_cast<V1_0::Effect>(effect), - effectStrength, callback); - } else if (isValidEffect<Effect_1_1>(effect)) { - ret = halCall(&V1_1::IVibrator::perform_1_1, static_cast<Effect_1_1>(effect), - effectStrength, callback); - } else if (isValidEffect<V1_2::Effect>(effect)) { - ret = halCall(&V1_2::IVibrator::perform_1_2, static_cast<V1_2::Effect>(effect), - effectStrength, callback); - } else if (isValidEffect<V1_3::Effect>(effect)) { - ret = halCall(&V1_3::IVibrator::perform_1_3, static_cast<V1_3::Effect>(effect), - effectStrength, callback); + if (auto hal = getHal<aidl::IVibrator>()) { + int32_t lengthMs; + sp<AidlVibratorCallback> effectCallback = new AidlVibratorCallback(env, vibration); + aidl::Effect effectType(static_cast<aidl::Effect>(strength)); + aidl::EffectStrength effectStrength(static_cast<aidl::EffectStrength>(strength)); + + auto status = hal->call(&aidl::IVibrator::perform, effectType, effectStrength, effectCallback, &lengthMs); + if (!status.isOk()) { + if (status.exceptionCode() != binder::Status::EX_UNSUPPORTED_OPERATION) { + ALOGE("Failed to perform haptic effect: effect=%" PRId64 ", strength=%" PRId32 + ": %s", static_cast<int64_t>(effect), static_cast<int32_t>(strength), status.toString8().string()); + } + return -1; + } + return lengthMs; } else { - ALOGW("Unable to perform haptic effect, invalid effect ID (%" PRId32 ")", - static_cast<int32_t>(effect)); - return -1; - } + Status status; + uint32_t lengthMs; + auto callback = [&status, &lengthMs](Status retStatus, uint32_t retLengthMs) { + status = retStatus; + lengthMs = retLengthMs; + }; + EffectStrength effectStrength(static_cast<EffectStrength>(strength)); + + Return<void> ret; + if (isValidEffect<V1_0::Effect>(effect)) { + ret = halCall(&V1_0::IVibrator::perform, static_cast<V1_0::Effect>(effect), + effectStrength, callback); + } else if (isValidEffect<Effect_1_1>(effect)) { + ret = halCall(&V1_1::IVibrator::perform_1_1, static_cast<Effect_1_1>(effect), + effectStrength, callback); + } else if (isValidEffect<V1_2::Effect>(effect)) { + ret = halCall(&V1_2::IVibrator::perform_1_2, static_cast<V1_2::Effect>(effect), + effectStrength, callback); + } else if (isValidEffect<V1_3::Effect>(effect)) { + ret = halCall(&V1_3::IVibrator::perform_1_3, static_cast<V1_3::Effect>(effect), + effectStrength, callback); + } else { + ALOGW("Unable to perform haptic effect, invalid effect ID (%" PRId32 ")", + static_cast<int32_t>(effect)); + return -1; + } - if (!ret.isOk()) { - ALOGW("Failed to perform effect (%" PRId32 ")", static_cast<int32_t>(effect)); - return -1; - } + if (!ret.isOk()) { + ALOGW("Failed to perform effect (%" PRId32 ")", static_cast<int32_t>(effect)); + return -1; + } - if (status == Status::OK) { - return lengthMs; - } else if (status != Status::UNSUPPORTED_OPERATION) { - // Don't warn on UNSUPPORTED_OPERATION, that's a normal event and just means the motor - // doesn't have a pre-defined waveform to perform for it, so we should just give the - // opportunity to fall back to the framework waveforms. - ALOGE("Failed to perform haptic effect: effect=%" PRId64 ", strength=%" PRId32 - ", error=%" PRIu32 ").", static_cast<int64_t>(effect), - static_cast<int32_t>(strength), static_cast<uint32_t>(status)); + if (status == Status::OK) { + return lengthMs; + } else if (status != Status::UNSUPPORTED_OPERATION) { + // Don't warn on UNSUPPORTED_OPERATION, that's a normal event and just means the motor + // doesn't have a pre-defined waveform to perform for it, so we should just give the + // opportunity to fall back to the framework waveforms. + ALOGE("Failed to perform haptic effect: effect=%" PRId64 ", strength=%" PRId32 + ", error=%" PRIu32 ").", static_cast<int64_t>(effect), + static_cast<int32_t>(strength), static_cast<uint32_t>(status)); + } } return -1; } static jlong vibratorGetCapabilities(JNIEnv*, jclass) { - return halCall(&V1_4::IVibrator::getCapabilities).withDefault(0); + if (auto hal = getHal<aidl::IVibrator>()) { + int32_t cap = 0; + if (!hal->call(&aidl::IVibrator::getCapabilities, &cap).isOk()) { + return 0; + } + return cap; + } + + return 0; } static const JNINativeMethod method_table[] = { diff --git a/services/net/java/android/net/NetworkStackClient.java b/services/net/java/android/net/NetworkStackClient.java index 69e24063210d..865e3b8cc109 100644 --- a/services/net/java/android/net/NetworkStackClient.java +++ b/services/net/java/android/net/NetworkStackClient.java @@ -103,7 +103,7 @@ public class NetworkStackClient { // checks here should be kept in sync with PermissionUtil. if (caller != Process.SYSTEM_UID && caller != Process.NETWORK_STACK_UID - && !UserHandle.isSameApp(caller, Process.BLUETOOTH_UID)) { + && UserHandle.getAppId(caller) != Process.BLUETOOTH_UID) { throw new SecurityException( "Only the system server should try to bind to the network stack."); } diff --git a/services/tests/servicestests/Android.bp b/services/tests/servicestests/Android.bp index eecdeed2cd25..f1ceb3ca1673 100644 --- a/services/tests/servicestests/Android.bp +++ b/services/tests/servicestests/Android.bp @@ -22,6 +22,7 @@ android_test { "services.appwidget", "services.autofill", "services.backup", + "services.contentsuggestions", "services.core", "services.devicepolicy", "services.net", diff --git a/services/tests/servicestests/src/com/android/server/contentsuggestions/ContentSuggestionsPerUserServiceTest.java b/services/tests/servicestests/src/com/android/server/contentsuggestions/ContentSuggestionsPerUserServiceTest.java new file mode 100644 index 000000000000..80cf6ad6d88e --- /dev/null +++ b/services/tests/servicestests/src/com/android/server/contentsuggestions/ContentSuggestionsPerUserServiceTest.java @@ -0,0 +1,90 @@ +/* + * 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.contentsuggestions; + +import static androidx.test.InstrumentationRegistry.getContext; + +import static org.mockito.ArgumentMatchers.anyBoolean; +import static org.mockito.ArgumentMatchers.anyInt; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.times; +import static org.mockito.Mockito.verify; + +import android.app.contentsuggestions.ContentSuggestionsManager; +import android.graphics.Bitmap; +import android.os.Bundle; +import android.os.UserManagerInternal; +import android.testing.AndroidTestingRunner; + +import androidx.test.filters.SmallTest; + +import com.android.server.LocalServices; +import com.android.server.wm.ActivityTaskManagerInternal; + +import org.junit.Before; +import org.junit.Test; +import org.junit.runner.RunWith; + +@SmallTest +@RunWith(AndroidTestingRunner.class) +public class ContentSuggestionsPerUserServiceTest { + private int mUserId; + private ContentSuggestionsPerUserService mPerUserService; + private ActivityTaskManagerInternal mActivityTaskManagerInternal; + + @Before + public void setup() { + UserManagerInternal umi = mock(UserManagerInternal.class); + LocalServices.removeServiceForTest(UserManagerInternal.class); + LocalServices.addService(UserManagerInternal.class, umi); + + mActivityTaskManagerInternal = mock(ActivityTaskManagerInternal.class); + LocalServices.removeServiceForTest(ActivityTaskManagerInternal.class); + LocalServices.addService(ActivityTaskManagerInternal.class, mActivityTaskManagerInternal); + + ContentSuggestionsManagerService contentSuggestionsManagerService = + new ContentSuggestionsManagerService(getContext()); + mUserId = 1; + mPerUserService = new ContentSuggestionsPerUserService(contentSuggestionsManagerService, + new Object(), + mUserId); + } + + // Tests TaskSnapshot is taken when the key ContentSuggestionsManager.EXTRA_BITMAP is missing + // from imageContextRequestExtras provided. + @Test + public void testProvideContextImageLocked_noBitmapInBundle() { + Bundle imageContextRequestExtras = Bundle.EMPTY; + mPerUserService.provideContextImageLocked(mUserId, imageContextRequestExtras); + verify(mActivityTaskManagerInternal, times(1)).getTaskSnapshotNoRestore(anyInt(), + anyBoolean()); + } + + // Tests TaskSnapshot is not taken when the key ContentSuggestionsManager.EXTRA_BITMAP is + // provided in imageContextRequestExtras. + @Test + public void testProvideContextImageLocked_bitmapInBundle() { + Bundle imageContextRequestExtras = new Bundle(); + imageContextRequestExtras.putParcelable(ContentSuggestionsManager.EXTRA_BITMAP, + Bitmap.createBitmap(100, 100, Bitmap.Config.ARGB_8888)); + mPerUserService.provideContextImageLocked(mUserId, imageContextRequestExtras); + verify(mActivityTaskManagerInternal, times(0)) + .getTaskSnapshotNoRestore(anyInt(), anyBoolean()); + } +} + + diff --git a/services/tests/servicestests/src/com/android/server/pm/PackageBuilder.java b/services/tests/servicestests/src/com/android/server/pm/PackageBuilder.java new file mode 100644 index 000000000000..470d4fa92833 --- /dev/null +++ b/services/tests/servicestests/src/com/android/server/pm/PackageBuilder.java @@ -0,0 +1,122 @@ +/* + * 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.pm; + +import android.content.pm.PackageParser; + +import com.android.internal.util.ArrayUtils; + +class PackageBuilder { + final PackageParser.Package mPkg; + + PackageBuilder(String packageName) { + mPkg = new PackageParser.Package(packageName); + } + + PackageBuilder setApplicationInfoCodePath(String codePath) { + mPkg.applicationInfo.setCodePath(codePath); + return this; + } + + PackageBuilder setApplicationInfoResourcePath(String resourcePath) { + mPkg.applicationInfo.setResourcePath(resourcePath); + return this; + } + + PackageBuilder setCodePath(String codePath) { + mPkg.codePath = codePath; + return this; + } + + PackageBuilder setBaseCodePath(String baseCodePath) { + mPkg.baseCodePath = baseCodePath; + return this; + } + + PackageBuilder addUsesStaticLibrary(String name, long version) { + mPkg.usesStaticLibraries = ArrayUtils.add(mPkg.usesStaticLibraries, name); + mPkg.usesStaticLibrariesVersions = + ArrayUtils.appendLong(mPkg.usesStaticLibrariesVersions, version); + return this; + } + + PackageBuilder setApplicationInfoNativeLibraryRootDir(String dir) { + mPkg.applicationInfo.nativeLibraryRootDir = dir; + return this; + } + + PackageBuilder setStaticSharedLib(String staticSharedLibName, long staticSharedLibVersion) { + mPkg.staticSharedLibVersion = staticSharedLibVersion; + mPkg.staticSharedLibName = staticSharedLibName; + return this; + } + + PackageBuilder setManifestPackageName(String manifestPackageName) { + mPkg.manifestPackageName = manifestPackageName; + return this; + } + + PackageBuilder setVersionCodeMajor(int versionCodeMajor) { + mPkg.mVersionCodeMajor = versionCodeMajor; + return this; + } + + PackageBuilder setVersionCode(int versionCode) { + mPkg.mVersionCode = versionCode; + return this; + } + + PackageBuilder addSplitCodePath(String splitCodePath) { + mPkg.splitCodePaths = + ArrayUtils.appendElement(String.class, mPkg.splitCodePaths, splitCodePath); + return this; + } + + PackageBuilder setApplicationInfoVolumeUuid(String volumeUuid) { + mPkg.applicationInfo.volumeUuid = volumeUuid; + return this; + } + + PackageBuilder addLibraryName(String libraryName) { + mPkg.libraryNames = ArrayUtils.add(mPkg.libraryNames, libraryName); + return this; + } + + PackageBuilder setRealPackageName(String realPackageName) { + mPkg.mRealPackage = realPackageName; + return this; + } + + PackageBuilder setCpuAbiOVerride(String cpuAbiOverride) { + mPkg.cpuAbiOverride = cpuAbiOverride; + return this; + } + + PackageBuilder addPermissionRequest(String permissionName) { + mPkg.requestedPermissions.add(permissionName); + return this; + } + + PackageParser.Package build() { + return mPkg; + } + + public PackageBuilder addApplicationInfoFlag(int flag) { + mPkg.applicationInfo.flags |= flag; + return this; + } +} diff --git a/services/tests/servicestests/src/com/android/server/pm/PackageSettingBuilder.java b/services/tests/servicestests/src/com/android/server/pm/PackageSettingBuilder.java new file mode 100644 index 000000000000..b42cfd8be4a6 --- /dev/null +++ b/services/tests/servicestests/src/com/android/server/pm/PackageSettingBuilder.java @@ -0,0 +1,154 @@ +/* + * 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.pm; + +import android.content.pm.PackageUserState; +import android.util.SparseArray; + +import java.io.File; +import java.util.List; + +class PackageSettingBuilder { + private String mName; + private String mRealName; + private String mCodePath; + private String mResourcePath; + private String mLegacyNativeLibraryPathString; + private String mPrimaryCpuAbiString; + private String mSecondaryCpuAbiString; + private String mCpuAbiOverrideString; + private long mPVersionCode; + private int mPkgFlags; + private int mPrivateFlags; + private String mParentPackageName; + private List<String> mChildPackageNames; + private int mSharedUserId; + private String[] mUsesStaticLibraries; + private long[] mUsesStaticLibrariesVersions; + private String mVolumeUuid; + private SparseArray<PackageUserState> mUserStates = new SparseArray<>(); + + public PackageSettingBuilder setName(String name) { + this.mName = name; + return this; + } + + public PackageSettingBuilder setRealName(String realName) { + this.mRealName = realName; + return this; + } + + public PackageSettingBuilder setCodePath(String codePath) { + this.mCodePath = codePath; + return this; + } + + public PackageSettingBuilder setResourcePath(String resourcePath) { + this.mResourcePath = resourcePath; + return this; + } + + public PackageSettingBuilder setLegacyNativeLibraryPathString( + String legacyNativeLibraryPathString) { + this.mLegacyNativeLibraryPathString = legacyNativeLibraryPathString; + return this; + } + + public PackageSettingBuilder setPrimaryCpuAbiString(String primaryCpuAbiString) { + this.mPrimaryCpuAbiString = primaryCpuAbiString; + return this; + } + + public PackageSettingBuilder setSecondaryCpuAbiString(String secondaryCpuAbiString) { + this.mSecondaryCpuAbiString = secondaryCpuAbiString; + return this; + } + + public PackageSettingBuilder setCpuAbiOverrideString(String cpuAbiOverrideString) { + this.mCpuAbiOverrideString = cpuAbiOverrideString; + return this; + } + + public PackageSettingBuilder setPVersionCode(long pVersionCode) { + this.mPVersionCode = pVersionCode; + return this; + } + + public PackageSettingBuilder setPkgFlags(int pkgFlags) { + this.mPkgFlags = pkgFlags; + return this; + } + + public PackageSettingBuilder setPrivateFlags(int privateFlags) { + this.mPrivateFlags = privateFlags; + return this; + } + + public PackageSettingBuilder setParentPackageName(String parentPackageName) { + this.mParentPackageName = parentPackageName; + return this; + } + + public PackageSettingBuilder setChildPackageNames(List<String> childPackageNames) { + this.mChildPackageNames = childPackageNames; + return this; + } + + public PackageSettingBuilder setSharedUserId(int sharedUserId) { + this.mSharedUserId = sharedUserId; + return this; + } + + public PackageSettingBuilder setUsesStaticLibraries(String[] usesStaticLibraries) { + this.mUsesStaticLibraries = usesStaticLibraries; + return this; + } + + public PackageSettingBuilder setUsesStaticLibrariesVersions( + long[] usesStaticLibrariesVersions) { + this.mUsesStaticLibrariesVersions = usesStaticLibrariesVersions; + return this; + } + + public PackageSettingBuilder setVolumeUuid(String volumeUuid) { + this.mVolumeUuid = volumeUuid; + return this; + } + + public PackageSettingBuilder setInstantAppUserState(int userId, boolean isInstant) { + if (mUserStates.indexOfKey(userId) < 0) { + mUserStates.put(userId, new PackageUserState()); + } + mUserStates.get(userId).instantApp = isInstant; + return this; + } + + public PackageSetting build() { + final PackageSetting packageSetting = new PackageSetting(mName, mRealName, + new File(mCodePath), new File(mResourcePath), + mLegacyNativeLibraryPathString, mPrimaryCpuAbiString, mSecondaryCpuAbiString, + mCpuAbiOverrideString, mPVersionCode, mPkgFlags, mPrivateFlags, mParentPackageName, + mChildPackageNames, mSharedUserId, mUsesStaticLibraries, + mUsesStaticLibrariesVersions); + packageSetting.volumeUuid = this.mVolumeUuid; + for (int i = 0; i < mUserStates.size(); i++) { + packageSetting.setUserState(mUserStates.keyAt(i), mUserStates.valueAt(i)); + } + return packageSetting; + + } +} diff --git a/services/tests/servicestests/src/com/android/server/pm/ScanRequestBuilder.java b/services/tests/servicestests/src/com/android/server/pm/ScanRequestBuilder.java new file mode 100644 index 000000000000..34a3f860496a --- /dev/null +++ b/services/tests/servicestests/src/com/android/server/pm/ScanRequestBuilder.java @@ -0,0 +1,105 @@ +/* + * 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.pm; + +import android.content.pm.PackageParser; +import android.os.UserHandle; + +class ScanRequestBuilder { + private final PackageParser.Package mPkg; + private PackageParser.Package mOldPkg; + private SharedUserSetting mSharedUserSetting; + private PackageSetting mPkgSetting; + private PackageSetting mDisabledPkgSetting; + private PackageSetting mOriginalPkgSetting; + private String mRealPkgName; + private int mParseFlags; + private int mScanFlags; + private UserHandle mUser; + private boolean mIsPlatformPackage; + + ScanRequestBuilder(PackageParser.Package pkg) { + this.mPkg = pkg; + } + + public ScanRequestBuilder setOldPkg(PackageParser.Package oldPkg) { + this.mOldPkg = oldPkg; + return this; + } + + public ScanRequestBuilder setSharedUserSetting(SharedUserSetting sharedUserSetting) { + this.mSharedUserSetting = sharedUserSetting; + return this; + } + + public ScanRequestBuilder setPkgSetting(PackageSetting pkgSetting) { + this.mPkgSetting = pkgSetting; + return this; + } + + public ScanRequestBuilder setDisabledPkgSetting(PackageSetting disabledPkgSetting) { + this.mDisabledPkgSetting = disabledPkgSetting; + return this; + } + + public ScanRequestBuilder setOriginalPkgSetting(PackageSetting originalPkgSetting) { + this.mOriginalPkgSetting = originalPkgSetting; + return this; + } + + public ScanRequestBuilder setRealPkgName(String realPkgName) { + this.mRealPkgName = realPkgName; + return this; + } + + public ScanRequestBuilder setParseFlags(int parseFlags) { + this.mParseFlags = parseFlags; + return this; + } + + public ScanRequestBuilder addParseFlag(int parseFlag) { + this.mParseFlags |= parseFlag; + return this; + } + + public ScanRequestBuilder setScanFlags(int scanFlags) { + this.mScanFlags = scanFlags; + return this; + } + + public ScanRequestBuilder addScanFlag(int scanFlag) { + this.mScanFlags |= scanFlag; + return this; + } + + public ScanRequestBuilder setUser(UserHandle user) { + this.mUser = user; + return this; + } + + public ScanRequestBuilder setIsPlatformPackage(boolean isPlatformPackage) { + this.mIsPlatformPackage = isPlatformPackage; + return this; + } + + PackageManagerService.ScanRequest build() { + return new PackageManagerService.ScanRequest( + mPkg, mSharedUserSetting, mOldPkg, mPkgSetting, mDisabledPkgSetting, + mOriginalPkgSetting, mRealPkgName, mParseFlags, mScanFlags, mIsPlatformPackage, + mUser); + } +} diff --git a/services/tests/servicestests/src/com/android/server/pm/ScanTests.java b/services/tests/servicestests/src/com/android/server/pm/ScanTests.java new file mode 100644 index 000000000000..dd3d8b929793 --- /dev/null +++ b/services/tests/servicestests/src/com/android/server/pm/ScanTests.java @@ -0,0 +1,551 @@ +/* + * 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.pm; + +import static android.content.pm.SharedLibraryInfo.TYPE_DYNAMIC; +import static android.content.pm.SharedLibraryInfo.TYPE_STATIC; +import static android.content.pm.SharedLibraryInfo.VERSION_UNDEFINED; + +import static com.android.server.pm.PackageManagerService.SCAN_AS_FULL_APP; +import static com.android.server.pm.PackageManagerService.SCAN_AS_INSTANT_APP; +import static com.android.server.pm.PackageManagerService.SCAN_FIRST_BOOT_OR_UPGRADE; +import static com.android.server.pm.PackageManagerService.SCAN_NEW_INSTALL; + +import static org.hamcrest.MatcherAssert.assertThat; +import static org.hamcrest.Matchers.empty; +import static org.hamcrest.Matchers.hasItems; +import static org.hamcrest.Matchers.nullValue; +import static org.hamcrest.collection.IsArrayContainingInOrder.arrayContaining; +import static org.hamcrest.core.Is.is; +import static org.junit.Assert.assertNotSame; +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.ArgumentMatchers.anyBoolean; +import static org.mockito.ArgumentMatchers.nullable; +import static org.mockito.Mockito.when; + +import android.Manifest; +import android.content.pm.ApplicationInfo; +import android.content.pm.PackageInfo; +import android.content.pm.PackageParser; +import android.content.pm.SharedLibraryInfo; +import android.os.Environment; +import android.os.UserHandle; +import android.os.UserManagerInternal; +import android.platform.test.annotations.Presubmit; +import android.util.Pair; + +import org.hamcrest.BaseMatcher; +import org.hamcrest.Description; +import org.hamcrest.Matcher; +import org.junit.Before; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.mockito.Mock; +import org.mockito.junit.MockitoJUnitRunner; + +import java.io.File; + +@RunWith(MockitoJUnitRunner.class) +@Presubmit +// TODO: shared user tests +public class ScanTests { + + private static final String DUMMY_PACKAGE_NAME = "some.app.to.test"; + + @Mock + PackageAbiHelper mMockPackageAbiHelper; + @Mock + UserManagerInternal mMockUserManager; + + @Before + public void setupDefaultUser() { + when(mMockUserManager.getUserIds()).thenReturn(new int[]{0}); + } + + @Before + public void setupDefaultAbiBehavior() throws Exception { + when(mMockPackageAbiHelper.derivePackageAbi( + any(PackageParser.Package.class), nullable(String.class), anyBoolean())) + .thenReturn(new Pair<>( + new PackageAbiHelper.Abis("derivedPrimary", "derivedSecondary"), + new PackageAbiHelper.NativeLibraryPaths( + "derivedRootDir", true, "derivedNativeDir", "derivedNativeDir2"))); + when(mMockPackageAbiHelper.getNativeLibraryPaths( + any(PackageParser.Package.class), any(File.class))) + .thenReturn(new PackageAbiHelper.NativeLibraryPaths( + "getRootDir", true, "getNativeDir", "getNativeDir2" + )); + when(mMockPackageAbiHelper.getBundledAppAbis( + any(PackageParser.Package.class))) + .thenReturn(new PackageAbiHelper.Abis("bundledPrimary", "bundledSecondary")); + } + + @Test + public void newInstallSimpleAllNominal() throws Exception { + final PackageManagerService.ScanRequest scanRequest = + createBasicScanRequestBuilder(createBasicPackage(DUMMY_PACKAGE_NAME).build()) + .addScanFlag(PackageManagerService.SCAN_NEW_INSTALL) + .addScanFlag(PackageManagerService.SCAN_AS_FULL_APP) + .build(); + + final PackageManagerService.ScanResult scanResult = executeScan(scanRequest); + + assertBasicPackageScanResult(scanResult, DUMMY_PACKAGE_NAME, false /*isInstant*/); + assertThat(scanResult.existingSettingCopied, is(false)); + assertPathsNotDerived(scanResult); + } + + @Test + public void newInstallForAllUsers() throws Exception { + final int[] userIds = {0, 10, 11}; + when(mMockUserManager.getUserIds()).thenReturn(userIds); + + final PackageManagerService.ScanRequest scanRequest = + createBasicScanRequestBuilder(createBasicPackage(DUMMY_PACKAGE_NAME).build()) + .setRealPkgName(null) + .addScanFlag(PackageManagerService.SCAN_NEW_INSTALL) + .addScanFlag(PackageManagerService.SCAN_AS_FULL_APP) + .build(); + final PackageManagerService.ScanResult scanResult = executeScan(scanRequest); + + for (int uid : userIds) { + assertThat(scanResult.pkgSetting.readUserState(uid).installed, is(true)); + } + } + + @Test + public void installRealPackageName() throws Exception { + final PackageManagerService.ScanRequest scanRequest = + createBasicScanRequestBuilder(createBasicPackage(DUMMY_PACKAGE_NAME).build()) + .setRealPkgName("com.package.real") + .build(); + + final PackageManagerService.ScanResult scanResult = executeScan(scanRequest); + + assertThat(scanResult.pkgSetting.realName, is("com.package.real")); + + final PackageManagerService.ScanRequest scanRequestNoRealPkg = + createBasicScanRequestBuilder( + createBasicPackage(DUMMY_PACKAGE_NAME) + .setRealPackageName("com.package.real").build()) + .build(); + + final PackageManagerService.ScanResult scanResultNoReal = executeScan(scanRequestNoRealPkg); + assertThat(scanResultNoReal.pkgSetting.realName, nullValue()); + } + + @Test + public void updateSimpleNominal() throws Exception { + when(mMockUserManager.getUserIds()).thenReturn(new int[]{0}); + + final PackageSetting pkgSetting = createBasicPackageSettingBuilder(DUMMY_PACKAGE_NAME) + .setPrimaryCpuAbiString("primaryCpuAbi") + .setSecondaryCpuAbiString("secondaryCpuAbi") + .build(); + final PackageManagerService.ScanRequest scanRequest = + createBasicScanRequestBuilder(createBasicPackage(DUMMY_PACKAGE_NAME).build()) + .addScanFlag(PackageManagerService.SCAN_AS_FULL_APP) + .setPkgSetting(pkgSetting) + .build(); + + + final PackageManagerService.ScanResult scanResult = executeScan(scanRequest); + + assertThat(scanResult.existingSettingCopied, is(true)); + + // ensure we don't overwrite the existing pkgSetting, in case something post-scan fails + assertNotSame(pkgSetting, scanResult.pkgSetting); + + assertBasicPackageScanResult(scanResult, DUMMY_PACKAGE_NAME, false /*isInstant*/); + + assertThat(scanResult.pkgSetting.primaryCpuAbiString, is("primaryCpuAbi")); + assertThat(scanResult.pkgSetting.secondaryCpuAbiString, is("secondaryCpuAbi")); + assertThat(scanResult.pkgSetting.cpuAbiOverrideString, nullValue()); + + assertPathsNotDerived(scanResult); + } + + @Test + public void updateInstantSimpleNominal() throws Exception { + when(mMockUserManager.getUserIds()).thenReturn(new int[]{0}); + + final PackageSetting existingPkgSetting = + createBasicPackageSettingBuilder(DUMMY_PACKAGE_NAME) + .setInstantAppUserState(0, true) + .build(); + + final PackageManagerService.ScanRequest scanRequest = + createBasicScanRequestBuilder(createBasicPackage(DUMMY_PACKAGE_NAME).build()) + .setPkgSetting(existingPkgSetting) + .build(); + + + final PackageManagerService.ScanResult scanResult = executeScan(scanRequest); + + assertBasicPackageScanResult(scanResult, DUMMY_PACKAGE_NAME, true /*isInstant*/); + } + + @Test + public void installStaticSharedLibrary() throws Exception { + final PackageParser.Package pkg = createBasicPackage("static.lib.pkg.123") + .setStaticSharedLib("static.lib", 123L) + .setManifestPackageName("static.lib.pkg") + .setVersionCodeMajor(1) + .setVersionCode(234) + .setBaseCodePath("/some/path.apk") + .addSplitCodePath("/some/other/path.apk") + .build(); + + final PackageManagerService.ScanRequest scanRequest = new ScanRequestBuilder( + pkg).setUser(UserHandle.of(0)).build(); + + + final PackageManagerService.ScanResult scanResult = executeScan(scanRequest); + + assertThat(scanResult.staticSharedLibraryInfo.getPackageName(), is("static.lib.pkg.123")); + assertThat(scanResult.staticSharedLibraryInfo.getName(), is("static.lib")); + assertThat(scanResult.staticSharedLibraryInfo.getLongVersion(), is(123L)); + assertThat(scanResult.staticSharedLibraryInfo.getType(), is(TYPE_STATIC)); + assertThat(scanResult.staticSharedLibraryInfo.getDeclaringPackage().getPackageName(), + is("static.lib.pkg")); + assertThat(scanResult.staticSharedLibraryInfo.getDeclaringPackage().getLongVersionCode(), + is(pkg.getLongVersionCode())); + assertThat(scanResult.staticSharedLibraryInfo.getAllCodePaths(), + hasItems("/some/path.apk", "/some/other/path.apk")); + assertThat(scanResult.staticSharedLibraryInfo.getDependencies(), nullValue()); + assertThat(scanResult.staticSharedLibraryInfo.getDependentPackages(), empty()); + } + + @Test + public void installDynamicLibraries() throws Exception { + final PackageParser.Package pkg = createBasicPackage("dynamic.lib.pkg") + .setManifestPackageName("dynamic.lib.pkg") + .addLibraryName("liba") + .addLibraryName("libb") + .setVersionCodeMajor(1) + .setVersionCode(234) + .setBaseCodePath("/some/path.apk") + .addSplitCodePath("/some/other/path.apk") + .build(); + + final PackageManagerService.ScanRequest scanRequest = + new ScanRequestBuilder(pkg).setUser(UserHandle.of(0)).build(); + + + final PackageManagerService.ScanResult scanResult = executeScan(scanRequest); + + final SharedLibraryInfo dynamicLib0 = scanResult.dynamicSharedLibraryInfos.get(0); + assertThat(dynamicLib0.getPackageName(), is("dynamic.lib.pkg")); + assertThat(dynamicLib0.getName(), is("liba")); + assertThat(dynamicLib0.getLongVersion(), is((long) VERSION_UNDEFINED)); + assertThat(dynamicLib0.getType(), is(TYPE_DYNAMIC)); + assertThat(dynamicLib0.getDeclaringPackage().getPackageName(), is("dynamic.lib.pkg")); + assertThat(dynamicLib0.getDeclaringPackage().getLongVersionCode(), + is(pkg.getLongVersionCode())); + assertThat(dynamicLib0.getAllCodePaths(), + hasItems("/some/path.apk", "/some/other/path.apk")); + assertThat(dynamicLib0.getDependencies(), nullValue()); + assertThat(dynamicLib0.getDependentPackages(), empty()); + + final SharedLibraryInfo dynamicLib1 = scanResult.dynamicSharedLibraryInfos.get(1); + assertThat(dynamicLib1.getPackageName(), is("dynamic.lib.pkg")); + assertThat(dynamicLib1.getName(), is("libb")); + assertThat(dynamicLib1.getLongVersion(), is((long) VERSION_UNDEFINED)); + assertThat(dynamicLib1.getType(), is(TYPE_DYNAMIC)); + assertThat(dynamicLib1.getDeclaringPackage().getPackageName(), is("dynamic.lib.pkg")); + assertThat(dynamicLib1.getDeclaringPackage().getLongVersionCode(), + is(pkg.getLongVersionCode())); + assertThat(dynamicLib1.getAllCodePaths(), + hasItems("/some/path.apk", "/some/other/path.apk")); + assertThat(dynamicLib1.getDependencies(), nullValue()); + assertThat(dynamicLib1.getDependentPackages(), empty()); + } + + @Test + public void volumeUuidChangesOnUpdate() throws Exception { + final PackageSetting pkgSetting = + createBasicPackageSettingBuilder(DUMMY_PACKAGE_NAME) + .setVolumeUuid("someUuid") + .build(); + + final PackageParser.Package basicPackage = createBasicPackage(DUMMY_PACKAGE_NAME) + .setApplicationInfoVolumeUuid("someNewUuid") + .build(); + + + final PackageManagerService.ScanResult scanResult = executeScan( + new ScanRequestBuilder(basicPackage).setPkgSetting(pkgSetting).build()); + + assertThat(scanResult.pkgSetting.volumeUuid, is("someNewUuid")); + } + + @Test + public void scanFirstBoot_derivesAbis() throws Exception { + final PackageSetting pkgSetting = + createBasicPackageSettingBuilder(DUMMY_PACKAGE_NAME).build(); + + final PackageParser.Package basicPackage = + createBasicPackage(DUMMY_PACKAGE_NAME) + .setCpuAbiOVerride("testOverride") + .build(); + + + final PackageManagerService.ScanResult scanResult = executeScan(new ScanRequestBuilder( + basicPackage) + .setPkgSetting(pkgSetting) + .addScanFlag(SCAN_FIRST_BOOT_OR_UPGRADE) + .build()); + + assertAbiAndPathssDerived(scanResult); + } + + @Test + public void scanWithOriginalPkgSetting_packageNameChanges() throws Exception { + final PackageSetting originalPkgSetting = + createBasicPackageSettingBuilder("original.package").build(); + + final PackageParser.Package basicPackage = + createBasicPackage(DUMMY_PACKAGE_NAME) + .build(); + + + final PackageManagerService.ScanResult result = + executeScan(new ScanRequestBuilder(basicPackage) + .setOriginalPkgSetting(originalPkgSetting) + .build()); + + assertThat(result.request.pkg.packageName, is("original.package")); + } + + @Test + public void updateInstant_changeToFull() throws Exception { + when(mMockUserManager.getUserIds()).thenReturn(new int[]{0}); + + final PackageSetting existingPkgSetting = + createBasicPackageSettingBuilder(DUMMY_PACKAGE_NAME) + .setInstantAppUserState(0, true) + .build(); + + final PackageManagerService.ScanRequest scanRequest = + createBasicScanRequestBuilder(createBasicPackage(DUMMY_PACKAGE_NAME).build()) + .setPkgSetting(existingPkgSetting) + .addScanFlag(SCAN_AS_FULL_APP) + .build(); + + + final PackageManagerService.ScanResult scanResult = executeScan(scanRequest); + + assertBasicPackageScanResult(scanResult, DUMMY_PACKAGE_NAME, false /*isInstant*/); + } + + @Test + public void updateFull_changeToInstant() throws Exception { + when(mMockUserManager.getUserIds()).thenReturn(new int[]{0}); + + final PackageSetting existingPkgSetting = + createBasicPackageSettingBuilder(DUMMY_PACKAGE_NAME) + .setInstantAppUserState(0, false) + .build(); + + final PackageManagerService.ScanRequest scanRequest = + createBasicScanRequestBuilder(createBasicPackage(DUMMY_PACKAGE_NAME).build()) + .setPkgSetting(existingPkgSetting) + .addScanFlag(SCAN_AS_INSTANT_APP) + .build(); + + + final PackageManagerService.ScanResult scanResult = executeScan(scanRequest); + + assertBasicPackageScanResult(scanResult, DUMMY_PACKAGE_NAME, true /*isInstant*/); + } + + @Test + public void updateSystemApp_applicationInfoFlagSet() throws Exception { + final PackageSetting existingPkgSetting = + createBasicPackageSettingBuilder(DUMMY_PACKAGE_NAME) + .setPkgFlags(ApplicationInfo.FLAG_SYSTEM) + .build(); + + final PackageManagerService.ScanRequest scanRequest = + createBasicScanRequestBuilder(createBasicPackage(DUMMY_PACKAGE_NAME).build()) + .setPkgSetting(existingPkgSetting) + .setDisabledPkgSetting(existingPkgSetting) + .addScanFlag(SCAN_NEW_INSTALL) + .build(); + + final PackageManagerService.ScanResult scanResult = executeScan(scanRequest); + + assertThat(scanResult.request.pkg.applicationInfo.flags, + hasFlag(ApplicationInfo.FLAG_UPDATED_SYSTEM_APP)); + } + + @Test + public void factoryTestFlagSet() throws Exception { + final PackageParser.Package basicPackage = createBasicPackage(DUMMY_PACKAGE_NAME) + .addPermissionRequest(Manifest.permission.FACTORY_TEST) + .build(); + + final PackageManagerService.ScanResult scanResult = PackageManagerService.scanPackageOnlyLI( + createBasicScanRequestBuilder(basicPackage).build(), + new PackageManagerService.Injector(mMockUserManager, mMockPackageAbiHelper), + true /*isUnderFactoryTest*/, + System.currentTimeMillis()); + + assertThat(scanResult.request.pkg.applicationInfo.flags, + hasFlag(ApplicationInfo.FLAG_FACTORY_TEST)); + } + + @Test + public void scanSystemApp_isOrphanedTrue() throws Exception { + final PackageParser.Package pkg = createBasicPackage(DUMMY_PACKAGE_NAME) + .addApplicationInfoFlag(ApplicationInfo.FLAG_SYSTEM) + .build(); + + final PackageManagerService.ScanRequest scanRequest = + createBasicScanRequestBuilder(pkg) + .build(); + + final PackageManagerService.ScanResult scanResult = executeScan(scanRequest); + + assertThat(scanResult.pkgSetting.isOrphaned, is(true)); + } + + private static Matcher<Integer> hasFlag(final int flag) { + return new BaseMatcher<Integer>() { + @Override public void describeTo(Description description) { + description.appendText("flags "); + } + + @Override public boolean matches(Object item) { + return ((int) item & flag) != 0; + } + + @Override + public void describeMismatch(Object item, Description mismatchDescription) { + mismatchDescription + .appendValue(item) + .appendText(" does not contain flag ") + .appendValue(flag); + } + }; + } + + private PackageManagerService.ScanResult executeScan( + PackageManagerService.ScanRequest scanRequest) throws PackageManagerException { + return PackageManagerService.scanPackageOnlyLI( + scanRequest, + new PackageManagerService.Injector(mMockUserManager, mMockPackageAbiHelper), + false /*isUnderFactoryTest*/, + System.currentTimeMillis()); + } + + private static String createResourcePath(String packageName) { + return "/data/app/" + packageName + "-randompath/base.apk"; + } + + private static String createCodePath(String packageName) { + return "/data/app/" + packageName + "-randompath"; + } + + private static PackageSettingBuilder createBasicPackageSettingBuilder(String packageName) { + return new PackageSettingBuilder() + .setName(packageName) + .setCodePath(createCodePath(packageName)) + .setResourcePath(createResourcePath(packageName)); + } + + private static ScanRequestBuilder createBasicScanRequestBuilder(PackageParser.Package pkg) { + return new ScanRequestBuilder(pkg) + .setUser(UserHandle.of(0)); + } + + + private static PackageBuilder createBasicPackage(String packageName) { + return new PackageBuilder(packageName) + .setCodePath("/data/tmp/randompath") + .setApplicationInfoCodePath(createCodePath(packageName)) + .setApplicationInfoResourcePath(createResourcePath(packageName)) + .setApplicationInfoVolumeUuid("volumeUuid") + .setBaseCodePath("/data/tmp/randompath/base.apk") + .addUsesStaticLibrary("some.static.library", 234L) + .addUsesStaticLibrary("some.other.static.library", 456L) + .setApplicationInfoNativeLibraryRootDir("/data/tmp/randompath/base.apk:/lib") + .setVersionCodeMajor(1) + .setVersionCode(2345); + } + + private static void assertBasicPackageScanResult( + PackageManagerService.ScanResult scanResult, String packageName, boolean isInstant) { + assertThat(scanResult.success, is(true)); + + final PackageSetting pkgSetting = scanResult.pkgSetting; + assertBasicPackageSetting(scanResult, packageName, isInstant, pkgSetting); + + final ApplicationInfo applicationInfo = pkgSetting.pkg.applicationInfo; + assertBasicApplicationInfo(scanResult, applicationInfo); + + } + + private static void assertBasicPackageSetting(PackageManagerService.ScanResult scanResult, + String packageName, boolean isInstant, PackageSetting pkgSetting) { + assertThat(pkgSetting.pkg.packageName, is(packageName)); + assertThat(pkgSetting.getInstantApp(0), is(isInstant)); + assertThat(pkgSetting.usesStaticLibraries, + arrayContaining("some.static.library", "some.other.static.library")); + assertThat(pkgSetting.usesStaticLibrariesVersions, is(new long[]{234L, 456L})); + assertThat(pkgSetting.pkg, is(scanResult.request.pkg)); + assertThat(pkgSetting.pkg.mExtras, is(pkgSetting)); + assertThat(pkgSetting.codePath, is(new File(createCodePath(packageName)))); + assertThat(pkgSetting.resourcePath, is(new File(createResourcePath(packageName)))); + assertThat(pkgSetting.versionCode, is(PackageInfo.composeLongVersionCode(1, 2345))); + } + + private static void assertBasicApplicationInfo(PackageManagerService.ScanResult scanResult, + ApplicationInfo applicationInfo) { + assertThat(applicationInfo.processName, is(scanResult.request.pkg.packageName)); + + final int uid = applicationInfo.uid; + assertThat(UserHandle.getUserId(uid), is(UserHandle.USER_SYSTEM)); + + final String calculatedCredentialId = Environment.getDataUserCePackageDirectory( + applicationInfo.volumeUuid, UserHandle.USER_SYSTEM, + scanResult.request.pkg.packageName).getAbsolutePath(); + assertThat(applicationInfo.credentialProtectedDataDir, is(calculatedCredentialId)); + assertThat(applicationInfo.dataDir, is(applicationInfo.credentialProtectedDataDir)); + } + + private static void assertAbiAndPathssDerived(PackageManagerService.ScanResult scanResult) { + final ApplicationInfo applicationInfo = scanResult.pkgSetting.pkg.applicationInfo; + assertThat(applicationInfo.primaryCpuAbi, is("derivedPrimary")); + assertThat(applicationInfo.secondaryCpuAbi, is("derivedSecondary")); + + assertThat(applicationInfo.nativeLibraryRootDir, is("derivedRootDir")); + assertThat(scanResult.pkgSetting.legacyNativeLibraryPathString, is("derivedRootDir")); + assertThat(applicationInfo.nativeLibraryRootRequiresIsa, is(true)); + assertThat(applicationInfo.nativeLibraryDir, is("derivedNativeDir")); + assertThat(applicationInfo.secondaryNativeLibraryDir, is("derivedNativeDir2")); + } + + private static void assertPathsNotDerived(PackageManagerService.ScanResult scanResult) { + final ApplicationInfo applicationInfo = scanResult.pkgSetting.pkg.applicationInfo; + assertThat(applicationInfo.nativeLibraryRootDir, is("getRootDir")); + assertThat(scanResult.pkgSetting.legacyNativeLibraryPathString, is("getRootDir")); + assertThat(applicationInfo.nativeLibraryRootRequiresIsa, is(true)); + assertThat(applicationInfo.nativeLibraryDir, is("getNativeDir")); + assertThat(applicationInfo.secondaryNativeLibraryDir, is("getNativeDir2")); + } +} diff --git a/telecomm/java/android/telecom/DisconnectCause.java b/telecomm/java/android/telecom/DisconnectCause.java index ae924645d3da..bebbbd01fd88 100644 --- a/telecomm/java/android/telecom/DisconnectCause.java +++ b/telecomm/java/android/telecom/DisconnectCause.java @@ -97,6 +97,14 @@ public final class DisconnectCause implements Parcelable { */ public static final String REASON_EMULATING_SINGLE_CALL = "EMULATING_SINGLE_CALL"; + /** + * This reason is set when a call is ended in order to place an emergency call when a + * {@link PhoneAccount} doesn't support holding an ongoing call to place an emergency call. This + * reason string should only be associated with the {@link #LOCAL} disconnect code returned from + * {@link #getCode()}. + */ + public static final String REASON_EMERGENCY_CALL_PLACED = "REASON_EMERGENCY_CALL_PLACED"; + private int mDisconnectCode; private CharSequence mDisconnectLabel; private CharSequence mDisconnectDescription; diff --git a/telephony/java/com/android/internal/telephony/GsmAlphabet.java b/telephony/common/com/android/internal/telephony/GsmAlphabet.java index 79d366037f08..79d366037f08 100644 --- a/telephony/java/com/android/internal/telephony/GsmAlphabet.java +++ b/telephony/common/com/android/internal/telephony/GsmAlphabet.java diff --git a/telephony/java/android/telephony/CarrierConfigManager.java b/telephony/java/android/telephony/CarrierConfigManager.java index b289781eb223..d71723be35fa 100644 --- a/telephony/java/android/telephony/CarrierConfigManager.java +++ b/telephony/java/android/telephony/CarrierConfigManager.java @@ -809,7 +809,9 @@ public class CarrierConfigManager { /** * The default flag specifying whether ETWS/CMAS test setting is forcibly disabled in * Settings->More->Emergency broadcasts menu even though developer options is turned on. + * @deprecated moved to cellbroadcastreceiver resource show_test_settings */ + @Deprecated public static final String KEY_CARRIER_FORCE_DISABLE_ETWS_CMAS_TEST_BOOL = "carrier_force_disable_etws_cmas_test_bool"; @@ -1750,6 +1752,15 @@ public class CarrierConfigManager { "allow_emergency_video_calls_bool"; /** + * Flag indicating whether or not an ongoing call will be held when an outgoing emergency call + * is placed. If true, ongoing calls will be put on hold when an emergency call is placed. If + * false, placing an emergency call will trigger the disconnect of all ongoing calls before + * the emergency call is placed. + */ + public static final String KEY_ALLOW_HOLD_CALL_DURING_EMERGENCY_BOOL = + "allow_hold_call_during_emergency_bool"; + + /** * Flag indicating whether the carrier supports RCS presence indication for * User Capability Exchange (UCE). When presence is supported, the device should use the * {@link android.provider.ContactsContract.Data#CARRIER_PRESENCE} bit mask and set the @@ -3570,6 +3581,7 @@ public class CarrierConfigManager { sDefaults.putString(KEY_MMS_USER_AGENT_STRING, ""); sDefaults.putBoolean(KEY_ALLOW_NON_EMERGENCY_CALLS_IN_ECM_BOOL, true); sDefaults.putInt(KEY_EMERGENCY_SMS_MODE_TIMER_MS_INT, 0); + sDefaults.putBoolean(KEY_ALLOW_HOLD_CALL_DURING_EMERGENCY_BOOL, true); sDefaults.putBoolean(KEY_USE_RCS_PRESENCE_BOOL, false); sDefaults.putBoolean(KEY_USE_RCS_SIP_OPTIONS_BOOL, false); sDefaults.putBoolean(KEY_FORCE_IMEI_BOOL, false); diff --git a/telephony/java/com/android/internal/telephony/CbGeoUtils.java b/telephony/java/android/telephony/CbGeoUtils.java index 0b73252a1e1b..f4ce6e75769c 100644 --- a/telephony/java/com/android/internal/telephony/CbGeoUtils.java +++ b/telephony/java/android/telephony/CbGeoUtils.java @@ -14,10 +14,10 @@ * limitations under the License. */ -package com.android.internal.telephony; +package android.telephony; import android.annotation.NonNull; -import android.telephony.Rlog; +import android.annotation.SystemApi; import android.text.TextUtils; import java.util.ArrayList; @@ -30,8 +30,17 @@ import java.util.stream.Collectors; * The coordinates used by this utils class are latitude and longitude, but some algorithms in this * class only use them as coordinates on plane, so the calculation will be inaccurate. So don't use * this class for anything other then geo-targeting of cellbroadcast messages. + * @hide */ +@SystemApi public class CbGeoUtils { + + /** + * This class is never instantiated + * @hide + */ + private CbGeoUtils() {} + /** Geometric interface. */ public interface Geometry { /** @@ -39,27 +48,36 @@ public class CbGeoUtils { * @param p point in latitude, longitude format. * @return {@code True} if the given point is inside the geometry. */ - boolean contains(LatLng p); + boolean contains(@NonNull LatLng p); } /** * Tolerance for determining if the value is 0. If the absolute value of a value is less than * this tolerance, it will be treated as 0. + * @hide */ public static final double EPS = 1e-7; - /** The radius of earth. */ + /** + * The radius of earth. + * @hide + */ public static final int EARTH_RADIUS_METER = 6371 * 1000; private static final String TAG = "CbGeoUtils"; - /** The TLV tags of WAC, defined in ATIS-0700041 5.2.3 WAC tag coding. */ + // The TLV tags of WAC, defined in ATIS-0700041 5.2.3 WAC tag coding. + /** @hide */ public static final int GEO_FENCING_MAXIMUM_WAIT_TIME = 0x01; + /** @hide */ public static final int GEOMETRY_TYPE_POLYGON = 0x02; + /** @hide */ public static final int GEOMETRY_TYPE_CIRCLE = 0x03; - /** The identifier of geometry in the encoded string. */ + // The identifier of geometry in the encoded string. + /** @hide */ private static final String CIRCLE_SYMBOL = "circle"; + /** @hide */ private static final String POLYGON_SYMBOL = "polygon"; /** Point represent by (latitude, longitude). */ @@ -81,7 +99,8 @@ public class CbGeoUtils { * @param p the point use to calculate the subtraction result. * @return the result of this point subtract the given point {@code p}. */ - public LatLng subtract(LatLng p) { + @NonNull + public LatLng subtract(@NonNull LatLng p) { return new LatLng(lat - p.lat, lng - p.lng); } @@ -90,7 +109,7 @@ public class CbGeoUtils { * @param p the point use to calculate the distance. * @return the distance in meter. */ - public double distance(LatLng p) { + public double distance(@NonNull LatLng p) { double dlat = Math.sin(0.5 * Math.toRadians(lat - p.lat)); double dlng = Math.sin(0.5 * Math.toRadians(lng - p.lng)); double x = dlat * dlat @@ -106,6 +125,7 @@ public class CbGeoUtils { /** * The class represents a simple polygon with at least 3 points. + * @hide */ public static class Polygon implements Geometry { /** @@ -239,7 +259,10 @@ public class CbGeoUtils { } } - /** The class represents a circle. */ + /** + * The class represents a circle. + * @hide + */ public static class Circle implements Geometry { private final LatLng mCenter; private final double mRadiusMeter; @@ -266,6 +289,7 @@ public class CbGeoUtils { /** * Parse the geometries from the encoded string {@code str}. The string must follow the * geometry encoding specified by {@link android.provider.Telephony.CellBroadcasts#GEOMETRIES}. + * @hide */ @NonNull public static List<Geometry> parseGeometriesFromString(@NonNull String str) { @@ -297,6 +321,7 @@ public class CbGeoUtils { * * @param geometries the list of geometry objects need to be encoded. * @return the encoded string. + * @hide */ @NonNull public static String encodeGeometriesToString(List<Geometry> geometries) { @@ -313,6 +338,7 @@ public class CbGeoUtils { * {@link android.provider.Telephony.CellBroadcasts#GEOMETRIES}. * @param geometry the geometry object need to be encoded. * @return the encoded string. + * @hide */ @NonNull private static String encodeGeometryToString(@NonNull Geometry geometry) { @@ -351,6 +377,7 @@ public class CbGeoUtils { * * @param str encoded lat/lng string. * @Return {@link LatLng} object. + * @hide */ @NonNull public static LatLng parseLatLngFromString(@NonNull String str) { @@ -361,6 +388,7 @@ public class CbGeoUtils { /** * @Return the sign of the given value {@code value} with the specified tolerance. Return 1 * means the sign is positive, -1 means negative, 0 means the value will be treated as 0. + * @hide */ public static int sign(double value) { if (value > EPS) return 1; diff --git a/telephony/java/android/telephony/DisconnectCause.java b/telephony/java/android/telephony/DisconnectCause.java index 85110c22aa18..04ec4b6949af 100644 --- a/telephony/java/android/telephony/DisconnectCause.java +++ b/telephony/java/android/telephony/DisconnectCause.java @@ -354,6 +354,12 @@ public final class DisconnectCause { */ public static final int WFC_SERVICE_NOT_AVAILABLE_IN_THIS_LOCATION = 79; + /** + * Indicates that an emergency call was placed, which caused the existing connection to be + * hung up. + */ + public static final int OUTGOING_EMERGENCY_CALL_PLACED = 80; + //********************************************************************************************* // When adding a disconnect type: // 1) Update toString() with the newly added disconnect type. @@ -528,6 +534,8 @@ public final class DisconnectCause { return "EMERGENCY_CALL_OVER_WFC_NOT_AVAILABLE"; case WFC_SERVICE_NOT_AVAILABLE_IN_THIS_LOCATION: return "WFC_SERVICE_NOT_AVAILABLE_IN_THIS_LOCATION"; + case OUTGOING_EMERGENCY_CALL_PLACED: + return "OUTGOING_EMERGENCY_CALL_PLACED"; default: return "INVALID: " + cause; } diff --git a/telephony/java/android/telephony/SmsCbMessage.java b/telephony/java/android/telephony/SmsCbMessage.java index dc991b9a3ea7..3e044e52d28b 100644 --- a/telephony/java/android/telephony/SmsCbMessage.java +++ b/telephony/java/android/telephony/SmsCbMessage.java @@ -25,9 +25,7 @@ import android.database.Cursor; import android.os.Parcel; import android.os.Parcelable; import android.provider.Telephony.CellBroadcasts; - -import com.android.internal.telephony.CbGeoUtils; -import com.android.internal.telephony.CbGeoUtils.Geometry; +import android.telephony.CbGeoUtils.Geometry; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; @@ -414,9 +412,8 @@ public final class SmsCbMessage implements Parcelable { /** * Get the Geo-Fencing Maximum Wait Time. * @return the time in second. - * @hide */ - public int getMaximumWaitingTime() { + public int getMaximumWaitingDuration() { return mMaximumWaitTimeSec; } diff --git a/telephony/java/android/telephony/SubscriptionManager.java b/telephony/java/android/telephony/SubscriptionManager.java index 6e0fedf28465..8818cb3b10e2 100644 --- a/telephony/java/android/telephony/SubscriptionManager.java +++ b/telephony/java/android/telephony/SubscriptionManager.java @@ -49,6 +49,7 @@ import android.net.Uri; import android.os.Binder; import android.os.Build; import android.os.Handler; +import android.os.HandlerExecutor; import android.os.Looper; import android.os.Message; import android.os.ParcelUuid; @@ -61,10 +62,8 @@ import android.telephony.ims.ImsMmTelManager; import android.util.DisplayMetrics; import android.util.Log; -import com.android.internal.telephony.IOnSubscriptionsChangedListener; import com.android.internal.telephony.ISetOpportunisticDataCallback; import com.android.internal.telephony.ISub; -import com.android.internal.telephony.ITelephonyRegistry; import com.android.internal.telephony.PhoneConstants; import com.android.internal.util.Preconditions; @@ -931,20 +930,24 @@ public class SubscriptionManager { OnSubscriptionsChangedListenerHandler(Looper looper) { super(looper); } - - @Override - public void handleMessage(Message msg) { - if (DBG) { - log("handleMessage: invoke the overriden onSubscriptionsChanged()"); - } - OnSubscriptionsChangedListener.this.onSubscriptionsChanged(); - } } - private final Handler mHandler; + /** + * Posted executor callback on the handler associated with a given looper. + * The looper can be the calling thread's looper or the looper passed from the + * constructor {@link #OnSubscriptionsChangedListener(Looper)}. + */ + private final HandlerExecutor mExecutor; + + /** + * @hide + */ + public HandlerExecutor getHandlerExecutor() { + return mExecutor; + } public OnSubscriptionsChangedListener() { - mHandler = new OnSubscriptionsChangedListenerHandler(); + mExecutor = new HandlerExecutor(new OnSubscriptionsChangedListenerHandler()); } /** @@ -953,7 +956,7 @@ public class SubscriptionManager { * @hide */ public OnSubscriptionsChangedListener(Looper looper) { - mHandler = new OnSubscriptionsChangedListenerHandler(looper); + mExecutor = new HandlerExecutor(new OnSubscriptionsChangedListenerHandler(looper)); } /** @@ -965,18 +968,6 @@ public class SubscriptionManager { if (DBG) log("onSubscriptionsChanged: NOT OVERRIDDEN"); } - /** - * The callback methods need to be called on the handler thread where - * this object was created. If the binder did that for us it'd be nice. - */ - IOnSubscriptionsChangedListener callback = new IOnSubscriptionsChangedListener.Stub() { - @Override - public void onSubscriptionsChanged() { - if (DBG) log("callback: received, sendEmptyMessage(0) to handler"); - mHandler.sendEmptyMessage(0); - } - }; - private void log(String s) { Rlog.d(LOG_TAG, s); } @@ -1018,21 +1009,19 @@ public class SubscriptionManager { * onSubscriptionsChanged overridden. */ public void addOnSubscriptionsChangedListener(OnSubscriptionsChangedListener listener) { + if (listener == null) return; String pkgName = mContext != null ? mContext.getOpPackageName() : "<unknown>"; if (DBG) { logd("register OnSubscriptionsChangedListener pkgName=" + pkgName + " listener=" + listener); } - try { - // We use the TelephonyRegistry as it runs in the system and thus is always - // available. Where as SubscriptionController could crash and not be available - ITelephonyRegistry tr = ITelephonyRegistry.Stub.asInterface(ServiceManager.getService( - "telephony.registry")); - if (tr != null) { - tr.addOnSubscriptionsChangedListener(pkgName, listener.callback); - } - } catch (RemoteException ex) { - Log.e(LOG_TAG, "Remote exception ITelephonyRegistry " + ex); + // We use the TelephonyRegistry as it runs in the system and thus is always + // available. Where as SubscriptionController could crash and not be available + TelephonyRegistryManager telephonyRegistryManager = (TelephonyRegistryManager) + mContext.getSystemService(Context.TELEPHONY_REGISTRY_SERVICE); + if (telephonyRegistryManager != null) { + telephonyRegistryManager.addOnSubscriptionsChangedListener(listener, + listener.mExecutor); } } @@ -1044,21 +1033,18 @@ public class SubscriptionManager { * @param listener that is to be unregistered. */ public void removeOnSubscriptionsChangedListener(OnSubscriptionsChangedListener listener) { + if (listener == null) return; String pkgForDebug = mContext != null ? mContext.getOpPackageName() : "<unknown>"; if (DBG) { logd("unregister OnSubscriptionsChangedListener pkgForDebug=" + pkgForDebug + " listener=" + listener); } - try { - // We use the TelephonyRegistry as it runs in the system and thus is always - // available where as SubscriptionController could crash and not be available - ITelephonyRegistry tr = ITelephonyRegistry.Stub.asInterface(ServiceManager.getService( - "telephony.registry")); - if (tr != null) { - tr.removeOnSubscriptionsChangedListener(pkgForDebug, listener.callback); - } - } catch (RemoteException ex) { - Log.e(LOG_TAG, "Remote exception ITelephonyRegistry " + ex); + // We use the TelephonyRegistry as it runs in the system and thus is always + // available where as SubscriptionController could crash and not be available + TelephonyRegistryManager telephonyRegistryManager = (TelephonyRegistryManager) + mContext.getSystemService(Context.TELEPHONY_REGISTRY_SERVICE); + if (telephonyRegistryManager != null) { + telephonyRegistryManager.removeOnSubscriptionsChangedListener(listener); } } @@ -1077,7 +1063,6 @@ public class SubscriptionManager { * for #onOpportunisticSubscriptionsChanged to be invoked. */ public static class OnOpportunisticSubscriptionsChangedListener { - private Executor mExecutor; /** * Callback invoked when there is any change to any SubscriptionInfo. Typically * this method would invoke {@link #getActiveSubscriptionInfoList} @@ -1086,27 +1071,6 @@ public class SubscriptionManager { if (DBG) log("onOpportunisticSubscriptionsChanged: NOT OVERRIDDEN"); } - private void setExecutor(Executor executor) { - mExecutor = executor; - } - - /** - * The callback methods need to be called on the handler thread where - * this object was created. If the binder did that for us it'd be nice. - */ - IOnSubscriptionsChangedListener callback = new IOnSubscriptionsChangedListener.Stub() { - @Override - public void onSubscriptionsChanged() { - final long identity = Binder.clearCallingIdentity(); - try { - if (DBG) log("onOpportunisticSubscriptionsChanged callback received."); - mExecutor.execute(() -> onOpportunisticSubscriptionsChanged()); - } finally { - Binder.restoreCallingIdentity(identity); - } - } - }; - private void log(String s) { Rlog.d(LOG_TAG, s); } @@ -1133,18 +1097,13 @@ public class SubscriptionManager { + " listener=" + listener); } - listener.setExecutor(executor); - - try { - // We use the TelephonyRegistry as it runs in the system and thus is always - // available. Where as SubscriptionController could crash and not be available - ITelephonyRegistry tr = ITelephonyRegistry.Stub.asInterface(ServiceManager.getService( - "telephony.registry")); - if (tr != null) { - tr.addOnOpportunisticSubscriptionsChangedListener(pkgName, listener.callback); - } - } catch (RemoteException ex) { - Log.e(LOG_TAG, "Remote exception ITelephonyRegistry " + ex); + // We use the TelephonyRegistry as it runs in the system and thus is always + // available where as SubscriptionController could crash and not be available + TelephonyRegistryManager telephonyRegistryManager = (TelephonyRegistryManager) + mContext.getSystemService(Context.TELEPHONY_REGISTRY_SERVICE); + if (telephonyRegistryManager != null) { + telephonyRegistryManager.addOnOpportunisticSubscriptionsChangedListener( + listener, executor); } } @@ -1164,16 +1123,10 @@ public class SubscriptionManager { logd("unregister OnOpportunisticSubscriptionsChangedListener pkgForDebug=" + pkgForDebug + " listener=" + listener); } - try { - // We use the TelephonyRegistry as it runs in the system and thus is always - // available where as SubscriptionController could crash and not be available - ITelephonyRegistry tr = ITelephonyRegistry.Stub.asInterface(ServiceManager.getService( - "telephony.registry")); - if (tr != null) { - tr.removeOnSubscriptionsChangedListener(pkgForDebug, listener.callback); - } - } catch (RemoteException ex) { - Log.e(LOG_TAG, "Remote exception ITelephonyRegistry " + ex); + TelephonyRegistryManager telephonyRegistryManager = (TelephonyRegistryManager) + mContext.getSystemService(Context.TELEPHONY_REGISTRY_SERVICE); + if (telephonyRegistryManager != null) { + telephonyRegistryManager.removeOnOpportunisticSubscriptionsChangedListener(listener); } } diff --git a/telephony/java/android/telephony/TelephonyManager.java b/telephony/java/android/telephony/TelephonyManager.java index 8a1a6f913b68..e21922aaf5c7 100644 --- a/telephony/java/android/telephony/TelephonyManager.java +++ b/telephony/java/android/telephony/TelephonyManager.java @@ -7674,8 +7674,8 @@ public class TelephonyManager { */ @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) @SystemApi - public boolean isTetherApnRequired() { - return isTetherApnRequired(getSubId(SubscriptionManager.getActiveDataSubscriptionId())); + public boolean isTetheringApnRequired() { + return isTetheringApnRequired(getSubId(SubscriptionManager.getActiveDataSubscriptionId())); } /** @@ -7685,11 +7685,11 @@ public class TelephonyManager { * @return {@code true} if DUN APN is required for tethering. * @hide */ - public boolean isTetherApnRequired(int subId) { + public boolean isTetheringApnRequired(int subId) { try { ITelephony telephony = getITelephony(); if (telephony != null) - return telephony.isTetherApnRequiredForSubscriber(subId); + return telephony.isTetheringApnRequiredForSubscriber(subId); } catch (RemoteException ex) { Rlog.e(TAG, "hasMatchedTetherApnSetting RemoteException", ex); } catch (NullPointerException ex) { diff --git a/telephony/java/android/telephony/ims/ImsMmTelManager.java b/telephony/java/android/telephony/ims/ImsMmTelManager.java index eb0e2f7b8786..5fd0af564d34 100644 --- a/telephony/java/android/telephony/ims/ImsMmTelManager.java +++ b/telephony/java/android/telephony/ims/ImsMmTelManager.java @@ -979,25 +979,25 @@ public class ImsMmTelManager implements RegistrationManager { /** * Get the status of the MmTel Feature registered on this subscription. + * @param executor The executor that will be used to call the callback. * @param callback A callback containing an Integer describing the current state of the * MmTel feature, Which will be one of the following: * {@link ImsFeature#STATE_UNAVAILABLE}, * {@link ImsFeature#STATE_INITIALIZING}, * {@link ImsFeature#STATE_READY}. Will be called using the executor * specified when the service state has been retrieved from the IMS service. - * @param executor The executor that will be used to call the callback. * @throws ImsException if the IMS service associated with this subscription is not available or * the IMS service is not available. */ @RequiresPermission(Manifest.permission.READ_PRIVILEGED_PHONE_STATE) - public void getFeatureState(@NonNull @ImsFeature.ImsState Consumer<Integer> callback, - @NonNull @CallbackExecutor Executor executor) throws ImsException { - if (callback == null) { - throw new IllegalArgumentException("Must include a non-null Consumer."); - } + public void getFeatureState(@NonNull @CallbackExecutor Executor executor, + @NonNull @ImsFeature.ImsState Consumer<Integer> callback) throws ImsException { if (executor == null) { throw new IllegalArgumentException("Must include a non-null Executor."); } + if (callback == null) { + throw new IllegalArgumentException("Must include a non-null Consumer."); + } try { getITelephony().getImsMmTelFeatureState(mSubId, new IIntegerConsumer.Stub() { @Override diff --git a/telephony/java/android/telephony/ims/feature/RcsFeature.java b/telephony/java/android/telephony/ims/feature/RcsFeature.java index 119f890bcb7b..e96d082ca953 100644 --- a/telephony/java/android/telephony/ims/feature/RcsFeature.java +++ b/telephony/java/android/telephony/ims/feature/RcsFeature.java @@ -199,12 +199,19 @@ public class RcsFeature extends ImsFeature { /** @hide*/ @Retention(RetentionPolicy.SOURCE) @IntDef(prefix = "CAPABILITY_TYPE_", flag = true, value = { + CAPABILITY_TYPE_NONE, CAPABILITY_TYPE_OPTIONS_UCE, CAPABILITY_TYPE_PRESENCE_UCE }) public @interface RcsImsCapabilityFlag {} /** + * Undefined capability type for initialization + * @hide + */ + public static final int CAPABILITY_TYPE_NONE = 0; + + /** * This carrier supports User Capability Exchange using SIP OPTIONS as defined by the * framework. If set, the RcsFeature should support capability exchange using SIP OPTIONS. * If not set, this RcsFeature should not service capability requests. diff --git a/telephony/java/com/android/internal/telephony/ITelephony.aidl b/telephony/java/com/android/internal/telephony/ITelephony.aidl index b502ee711107..2f18049e7e71 100644 --- a/telephony/java/com/android/internal/telephony/ITelephony.aidl +++ b/telephony/java/com/android/internal/telephony/ITelephony.aidl @@ -819,7 +819,7 @@ interface ITelephony { * @return {@code true} if DUN APN is required for tethering. * @hide */ - boolean isTetherApnRequiredForSubscriber(int subId); + boolean isTetheringApnRequiredForSubscriber(int subId); /** * Enables framework IMS and triggers IMS Registration. diff --git a/telephony/java/com/android/internal/telephony/gsm/GsmSmsCbMessage.java b/telephony/java/com/android/internal/telephony/gsm/GsmSmsCbMessage.java index c15a8004b18c..5ce42fd3a7aa 100644 --- a/telephony/java/com/android/internal/telephony/gsm/GsmSmsCbMessage.java +++ b/telephony/java/com/android/internal/telephony/gsm/GsmSmsCbMessage.java @@ -25,17 +25,17 @@ import static android.telephony.SmsCbEtwsInfo.ETWS_WARNING_TYPE_TSUNAMI; import android.annotation.NonNull; import android.content.Context; import android.content.res.Resources; +import android.telephony.CbGeoUtils; +import android.telephony.CbGeoUtils.Circle; +import android.telephony.CbGeoUtils.Geometry; +import android.telephony.CbGeoUtils.LatLng; +import android.telephony.CbGeoUtils.Polygon; import android.telephony.SmsCbLocation; import android.telephony.SmsCbMessage; import android.util.Pair; import android.util.Slog; import com.android.internal.R; -import com.android.internal.telephony.CbGeoUtils; -import com.android.internal.telephony.CbGeoUtils.Circle; -import com.android.internal.telephony.CbGeoUtils.Geometry; -import com.android.internal.telephony.CbGeoUtils.LatLng; -import com.android.internal.telephony.CbGeoUtils.Polygon; import com.android.internal.telephony.GsmAlphabet; import com.android.internal.telephony.SmsConstants; import com.android.internal.telephony.gsm.GsmSmsCbMessage.GeoFencingTriggerMessage.CellBroadcastIdentity; diff --git a/tests/PlatformCompatGating/Android.bp b/tests/PlatformCompatGating/Android.bp new file mode 100644 index 000000000000..5e9ef8efc402 --- /dev/null +++ b/tests/PlatformCompatGating/Android.bp @@ -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. +// + +android_test { + name: "PlatformCompatGating", + // Only compile source java files in this apk. + srcs: ["src/**/*.java"], + certificate: "platform", + libs: [ + "android.test.runner", + "android.test.base", + ], + static_libs: [ + "junit", + "android-support-test", + "mockito-target-minus-junit4", + "truth-prebuilt", + "platform-compat-test-rules" + ], +} diff --git a/tests/PlatformCompatGating/AndroidManifest.xml b/tests/PlatformCompatGating/AndroidManifest.xml new file mode 100644 index 000000000000..7f14b83fbc75 --- /dev/null +++ b/tests/PlatformCompatGating/AndroidManifest.xml @@ -0,0 +1,11 @@ +<?xml version="1.0" encoding="utf-8"?> + +<manifest xmlns:android="http://schemas.android.com/apk/res/android" + package="com.android.tests.gating"> + <application android:label="GatingTest"> + <uses-library android:name="android.test.runner" /> + </application> + + <instrumentation android:name="android.support.test.runner.AndroidJUnitRunner" + android:targetPackage="com.android.tests.gating"/> +</manifest> diff --git a/tests/PlatformCompatGating/AndroidTest.xml b/tests/PlatformCompatGating/AndroidTest.xml new file mode 100644 index 000000000000..c62684837332 --- /dev/null +++ b/tests/PlatformCompatGating/AndroidTest.xml @@ -0,0 +1,30 @@ +<!-- Copyright (C) 2018 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="Test compatibility change gating."> + <target_preparer class="com.android.tradefed.targetprep.TestFilePushSetup"/> + <target_preparer class="com.android.tradefed.targetprep.TestAppInstallSetup"> + <option name="test-file-name" value="PlatformCompatGating.apk"/> + </target_preparer> + <target_preparer class="com.android.tradefed.targetprep.PushFilePreparer"/> + <target_preparer class="com.android.tradefed.targetprep.RunCommandTargetPreparer"/> + <option name="test-suite-tag" value="apct"/> + <option name="test-tag" value="Gating"/> + + <test class="com.android.tradefed.testtype.AndroidJUnitTest"> + <option name="package" value="com.android.tests.gating"/> + <option name="runner" value="android.support.test.runner.AndroidJUnitRunner"/> + <option name="hidden-api-checks" value="false"/> + </test> +</configuration> diff --git a/tests/PlatformCompatGating/src/com/android/compat/testing/DummyApi.java b/tests/PlatformCompatGating/src/com/android/compat/testing/DummyApi.java new file mode 100644 index 000000000000..731be8e3d9f0 --- /dev/null +++ b/tests/PlatformCompatGating/src/com/android/compat/testing/DummyApi.java @@ -0,0 +1,87 @@ +/* + * Copyright (C) 2016 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.compat.testing; + +import android.compat.Compatibility; +import android.content.Context; +import android.os.RemoteException; +import android.os.ServiceManager; + +import com.android.internal.compat.IPlatformCompat; + +/** + * This is a dummy API to test gating + * + * @hide + */ +public class DummyApi { + + public static final long CHANGE_ID = 666013; + public static final long CHANGE_ID_1 = 666014; + public static final long CHANGE_ID_2 = 666015; + public static final long CHANGE_SYSTEM_SERVER = 666016; + + /** + * Dummy method + * @return "A" if change is enabled, "B" otherwise. + */ + public static String dummyFunc() { + if (Compatibility.isChangeEnabled(CHANGE_ID)) { + return "A"; + } + return "B"; + } + + /** + * Dummy combined method + * @return "0" if {@link CHANGE_ID_1} is disabled and {@link CHANGE_ID_2} is disabled, + "1" if {@link CHANGE_ID_1} is disabled and {@link CHANGE_ID_2} is enabled, + "2" if {@link CHANGE_ID_1} is enabled and {@link CHANGE_ID_2} is disabled, + "3" if {@link CHANGE_ID_1} is enabled and {@link CHANGE_ID_2} is enabled. + */ + public static String dummyCombinedFunc() { + if (!Compatibility.isChangeEnabled(CHANGE_ID_1) + && !Compatibility.isChangeEnabled(CHANGE_ID_2)) { + return "0"; + } else if (!Compatibility.isChangeEnabled(CHANGE_ID_1) + && Compatibility.isChangeEnabled(CHANGE_ID_2)) { + return "1"; + } else if (Compatibility.isChangeEnabled(CHANGE_ID_1) + && !Compatibility.isChangeEnabled(CHANGE_ID_2)) { + return "2"; + } + return "3"; + } + + /** + * Dummy api using system server API. + */ + public static boolean dummySystemServer(Context context) { + IPlatformCompat platformCompat = IPlatformCompat.Stub + .asInterface(ServiceManager.getService(Context.PLATFORM_COMPAT_SERVICE)); + if (platformCompat == null) { + throw new RuntimeException("Could not obtain IPlatformCompat instance!"); + } + String packageName = context.getPackageName(); + try { + return platformCompat.isChangeEnabledByPackageName(CHANGE_SYSTEM_SERVER, packageName, + context.getUserId()); + } catch (RemoteException e) { + throw new RuntimeException("Could not get change value!", e); + } + } +} diff --git a/tests/PlatformCompatGating/src/com/android/tests/gating/PlatformCompatGatingTest.java b/tests/PlatformCompatGating/src/com/android/tests/gating/PlatformCompatGatingTest.java new file mode 100644 index 000000000000..dc317f1941c7 --- /dev/null +++ b/tests/PlatformCompatGating/src/com/android/tests/gating/PlatformCompatGatingTest.java @@ -0,0 +1,94 @@ +/* + * Copyright (C) 2018 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.tests.gating; + +import static com.google.common.truth.Truth.assertThat; + +import android.compat.testing.PlatformCompatChangeRule; +import android.support.test.InstrumentationRegistry; +import android.support.test.runner.AndroidJUnit4; + +import com.android.compat.testing.DummyApi; + +import libcore.junit.util.compat.CoreCompatChangeRule.DisableCompatChanges; +import libcore.junit.util.compat.CoreCompatChangeRule.EnableCompatChanges; + +import org.junit.Rule; +import org.junit.Test; +import org.junit.rules.TestRule; +import org.junit.runner.RunWith; + +/** + * Tests for platform compatibility change gating. + */ +@RunWith(AndroidJUnit4.class) +public class PlatformCompatGatingTest { + + @Rule + public TestRule compatChangeRule = new PlatformCompatChangeRule(); + + @Test + @EnableCompatChanges({DummyApi.CHANGE_ID}) + public void testDummyGatingPositive() { + assertThat(DummyApi.dummyFunc()).isEqualTo("A"); + } + + @Test + @DisableCompatChanges({DummyApi.CHANGE_ID}) + public void testDummyGatingNegative() { + assertThat(DummyApi.dummyFunc()).isEqualTo("B"); + } + + @Test + @DisableCompatChanges({DummyApi.CHANGE_ID_1, DummyApi.CHANGE_ID_2}) + public void testDummyGatingCombined0() { + assertThat(DummyApi.dummyCombinedFunc()).isEqualTo("0"); + } + + @Test + @DisableCompatChanges({DummyApi.CHANGE_ID_1}) + @EnableCompatChanges({DummyApi.CHANGE_ID_2}) + public void testDummyGatingCombined1() { + assertThat(DummyApi.dummyCombinedFunc()).isEqualTo("1"); + } + + @Test + @EnableCompatChanges({DummyApi.CHANGE_ID_1}) + @DisableCompatChanges({DummyApi.CHANGE_ID_2}) + public void testDummyGatingCombined2() { + assertThat(DummyApi.dummyCombinedFunc()).isEqualTo("2"); + } + + @Test + @EnableCompatChanges({DummyApi.CHANGE_ID_1, DummyApi.CHANGE_ID_2}) + public void testDummyGatingCombined3() { + assertThat(DummyApi.dummyCombinedFunc()).isEqualTo("3"); + } + + @Test + @EnableCompatChanges({DummyApi.CHANGE_SYSTEM_SERVER}) + public void testDummyGatingPositiveSystemServer() { + assertThat( + DummyApi.dummySystemServer(InstrumentationRegistry.getTargetContext())).isTrue(); + } + + @Test + @DisableCompatChanges({DummyApi.CHANGE_SYSTEM_SERVER}) + public void testDummyGatingNegativeSystemServer() { + assertThat( + DummyApi.dummySystemServer(InstrumentationRegistry.getTargetContext())).isFalse(); + } +} diff --git a/tests/PlatformCompatGating/test-rules/Android.bp b/tests/PlatformCompatGating/test-rules/Android.bp new file mode 100644 index 000000000000..8211ef523ee7 --- /dev/null +++ b/tests/PlatformCompatGating/test-rules/Android.bp @@ -0,0 +1,26 @@ +// +// 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. +// + +java_library { + name: "platform-compat-test-rules", + srcs: ["src/**/*.java"], + static_libs: [ + "junit", + "android-support-test", + "truth-prebuilt", + "core-compat-test-rules" + ], +}
\ No newline at end of file diff --git a/tests/PlatformCompatGating/test-rules/src/android/compat/testing/PlatformCompatChangeRule.java b/tests/PlatformCompatGating/test-rules/src/android/compat/testing/PlatformCompatChangeRule.java new file mode 100644 index 000000000000..932ec643d478 --- /dev/null +++ b/tests/PlatformCompatGating/test-rules/src/android/compat/testing/PlatformCompatChangeRule.java @@ -0,0 +1,108 @@ +/* + * 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.compat.testing; + +import android.app.Instrumentation; +import android.compat.Compatibility; +import android.compat.Compatibility.ChangeConfig; +import android.content.Context; +import android.os.RemoteException; +import android.os.ServiceManager; +import android.support.test.InstrumentationRegistry; + +import com.android.internal.compat.CompatibilityChangeConfig; +import com.android.internal.compat.IPlatformCompat; + +import libcore.junit.util.compat.CoreCompatChangeRule; + +import org.junit.runners.model.Statement; + +/** + * Allows tests to specify the which change to disable. + * + * <p>To use add the following to the test class. It will only change the behavior of a test method + * if it is annotated with + * {@link libcore.junit.util.compat.CoreCompatChangeRule.EnableCompatChanges} and/or + * {@link libcore.junit.util.compat.CoreCompatChangeRule.DisableCompatChanges}. + * </p> + * <pre> + * @Rule + * public TestRule compatChangeRule = new PlatformCompatChangeRule(); + * </pre> + * + * <p>Each test method that needs to disable a specific change needs to be annotated + * with {@link libcore.junit.util.compat.CoreCompatChangeRule.EnableCompatChanges} and/or + * {@link libcore.junit.util.compat.CoreCompatChangeRule.DisableCompatChanges} specifying the change + * id. e.g.: + * </p> + * <pre> + * @Test + * @DisableCompatChanges({42}) + * public void testAsIfChange42Disabled() { + * // check behavior + * } + * + * @Test + * @EnableCompatChanges({42}) + * public void testAsIfChange42Enabled() { + * // check behavior + * + * </pre> + */ +public class PlatformCompatChangeRule extends CoreCompatChangeRule { + + @Override + protected Statement createStatementForConfig(final Statement statement, ChangeConfig config) { + return new CompatChangeStatement(statement, config); + } + + + private static class CompatChangeStatement extends Statement { + private final Statement mTestStatement; + private final ChangeConfig mConfig; + + private CompatChangeStatement(Statement testStatement, ChangeConfig config) { + this.mTestStatement = testStatement; + this.mConfig = config; + } + + @Override + public void evaluate() throws Throwable { + Instrumentation instrumentation = InstrumentationRegistry.getInstrumentation(); + String packageName = instrumentation.getTargetContext().getPackageName(); + IPlatformCompat platformCompat = IPlatformCompat.Stub + .asInterface(ServiceManager.getService(Context.PLATFORM_COMPAT_SERVICE)); + if (platformCompat == null) { + throw new IllegalStateException("Could not get IPlatformCompat service!"); + } + Compatibility.setOverrides(mConfig); + try { + platformCompat.setOverridesForTest(new CompatibilityChangeConfig(mConfig), + packageName); + try { + mTestStatement.evaluate(); + } finally { + platformCompat.clearOverridesForTest(packageName); + } + } catch (RemoteException e) { + throw new RuntimeException("Could not call IPlatformCompat binder method!", e); + } finally { + Compatibility.clearOverrides(); + } + } + } +} diff --git a/tests/net/java/com/android/server/ConnectivityServiceTest.java b/tests/net/java/com/android/server/ConnectivityServiceTest.java index 61f37fd6c7e2..aad2f3da40f2 100644 --- a/tests/net/java/com/android/server/ConnectivityServiceTest.java +++ b/tests/net/java/com/android/server/ConnectivityServiceTest.java @@ -20,6 +20,9 @@ import static android.content.pm.PackageManager.GET_PERMISSIONS; import static android.content.pm.PackageManager.MATCH_ANY_USER; import static android.net.ConnectivityManager.ACTION_CAPTIVE_PORTAL_SIGN_IN; import static android.net.ConnectivityManager.CONNECTIVITY_ACTION; +import static android.net.ConnectivityManager.CONNECTIVITY_ACTION_SUPL; +import static android.net.ConnectivityManager.EXTRA_NETWORK_INFO; +import static android.net.ConnectivityManager.EXTRA_NETWORK_TYPE; import static android.net.ConnectivityManager.NETID_UNSET; import static android.net.ConnectivityManager.PRIVATE_DNS_MODE_OFF; import static android.net.ConnectivityManager.PRIVATE_DNS_MODE_OPPORTUNISTIC; @@ -28,6 +31,7 @@ import static android.net.ConnectivityManager.TYPE_ETHERNET; import static android.net.ConnectivityManager.TYPE_MOBILE; import static android.net.ConnectivityManager.TYPE_MOBILE_FOTA; import static android.net.ConnectivityManager.TYPE_MOBILE_MMS; +import static android.net.ConnectivityManager.TYPE_MOBILE_SUPL; import static android.net.ConnectivityManager.TYPE_WIFI; import static android.net.INetworkMonitor.NETWORK_VALIDATION_PROBE_DNS; import static android.net.INetworkMonitor.NETWORK_VALIDATION_PROBE_FALLBACK; @@ -145,6 +149,7 @@ import android.net.MatchAllNetworkSpecifier; import android.net.Network; import android.net.NetworkCapabilities; import android.net.NetworkFactory; +import android.net.NetworkInfo; import android.net.NetworkRequest; import android.net.NetworkSpecifier; import android.net.NetworkStack; @@ -244,6 +249,7 @@ import java.util.concurrent.Executors; import java.util.concurrent.LinkedBlockingQueue; import java.util.concurrent.TimeUnit; import java.util.concurrent.atomic.AtomicBoolean; +import java.util.function.Predicate; import kotlin.reflect.KClass; @@ -330,6 +336,9 @@ public class ConnectivityServiceTest { private class MockContext extends BroadcastInterceptingContext { private final MockContentResolver mContentResolver; + // Contains all registered receivers since this object was created. Useful to clear + // them when needed, as BroadcastInterceptingContext does not provide this facility. + private final List<BroadcastReceiver> mRegisteredReceivers = new ArrayList<>(); @Spy private Resources mResources; private final LinkedBlockingQueue<Intent> mStartedActivities = new LinkedBlockingQueue<>(); @@ -343,6 +352,7 @@ public class ConnectivityServiceTest { "wifi,1,1,1,-1,true", "mobile,0,0,0,-1,true", "mobile_mms,2,0,2,60000,true", + "mobile_supl,3,0,2,60000,true", }); when(mResources.getStringArray( @@ -410,6 +420,19 @@ public class ConnectivityServiceTest { // make sure the code does not rely on unexpected permissions. super.enforceCallingOrSelfPermission(permission, message); } + + @Override + public Intent registerReceiver(BroadcastReceiver receiver, IntentFilter filter) { + mRegisteredReceivers.add(receiver); + return super.registerReceiver(receiver, filter); + } + + public void clearRegisteredReceivers() { + // super.unregisterReceiver is a no-op for receivers that are not registered (because + // they haven't been registered or because they have already been unregistered). + // For the same reason, don't bother clearing mRegisteredReceivers. + for (final BroadcastReceiver rcv : mRegisteredReceivers) unregisterReceiver(rcv); + } } private void waitForIdle() { @@ -438,7 +461,7 @@ public class ConnectivityServiceTest { } // Bring up a network that we can use to send messages to ConnectivityService. - ConditionVariable cv = waitForConnectivityBroadcasts(1); + ConditionVariable cv = registerConnectivityBroadcast(1); mWiFiNetworkAgent = new TestNetworkAgentWrapper(TRANSPORT_WIFI); mWiFiNetworkAgent.connect(false); waitFor(cv); @@ -458,7 +481,7 @@ public class ConnectivityServiceTest { @Ignore public void verifyThatNotWaitingForIdleCausesRaceConditions() throws Exception { // Bring up a network that we can use to send messages to ConnectivityService. - ConditionVariable cv = waitForConnectivityBroadcasts(1); + ConditionVariable cv = registerConnectivityBroadcast(1); mWiFiNetworkAgent = new TestNetworkAgentWrapper(TRANSPORT_WIFI); mWiFiNetworkAgent.connect(false); waitFor(cv); @@ -1227,17 +1250,26 @@ public class ConnectivityServiceTest { * Return a ConditionVariable that opens when {@code count} numbers of CONNECTIVITY_ACTION * broadcasts are received. */ - private ConditionVariable waitForConnectivityBroadcasts(final int count) { + private ConditionVariable registerConnectivityBroadcast(final int count) { + return registerConnectivityBroadcastThat(count, intent -> true); + } + + private ConditionVariable registerConnectivityBroadcastThat(final int count, + @NonNull final Predicate<Intent> filter) { final ConditionVariable cv = new ConditionVariable(); - mServiceContext.registerReceiver(new BroadcastReceiver() { + final IntentFilter intentFilter = new IntentFilter(CONNECTIVITY_ACTION); + intentFilter.addAction(CONNECTIVITY_ACTION_SUPL); + final BroadcastReceiver receiver = new BroadcastReceiver() { private int remaining = count; public void onReceive(Context context, Intent intent) { + if (!filter.test(intent)) return; if (--remaining == 0) { cv.open(); mServiceContext.unregisterReceiver(this); } } - }, new IntentFilter(CONNECTIVITY_ACTION)); + }; + mServiceContext.registerReceiver(receiver, intentFilter); return cv; } @@ -1258,6 +1290,75 @@ public class ConnectivityServiceTest { } @Test + public void testNetworkFeature() throws Exception { + // Connect the cell agent and wait for the connected broadcast. + mCellNetworkAgent = new TestNetworkAgentWrapper(TRANSPORT_CELLULAR); + mCellNetworkAgent.addCapability(NET_CAPABILITY_SUPL); + final ConditionVariable cv1 = registerConnectivityBroadcastThat(1, + intent -> intent.getIntExtra(EXTRA_NETWORK_TYPE, -1) == TYPE_MOBILE); + mCellNetworkAgent.connect(true); + waitFor(cv1); + + // Build legacy request for SUPL. + final NetworkCapabilities legacyCaps = new NetworkCapabilities(); + legacyCaps.addTransportType(TRANSPORT_CELLULAR); + legacyCaps.addCapability(NET_CAPABILITY_SUPL); + final NetworkRequest legacyRequest = new NetworkRequest(legacyCaps, TYPE_MOBILE_SUPL, + ConnectivityManager.REQUEST_ID_UNSET, NetworkRequest.Type.REQUEST); + + // Send request and check that the legacy broadcast for SUPL is sent correctly. + final TestNetworkCallback callback = new TestNetworkCallback(); + final ConditionVariable cv2 = registerConnectivityBroadcastThat(1, + intent -> intent.getIntExtra(EXTRA_NETWORK_TYPE, -1) == TYPE_MOBILE_SUPL); + mCm.requestNetwork(legacyRequest, callback); + callback.expectCallback(CallbackEntry.AVAILABLE, mCellNetworkAgent); + waitFor(cv2); + + // File another request, withdraw it and make sure no broadcast is sent + final ConditionVariable cv3 = registerConnectivityBroadcast(1); + final TestNetworkCallback callback2 = new TestNetworkCallback(); + mCm.requestNetwork(legacyRequest, callback2); + callback2.expectCallback(CallbackEntry.AVAILABLE, mCellNetworkAgent); + mCm.unregisterNetworkCallback(callback2); + assertFalse(cv3.block(800)); // 800ms long enough to at least flake if this is sent + // As the broadcast did not fire, the receiver was not unregistered. Do this now. + mServiceContext.clearRegisteredReceivers(); + + // Withdraw the request and check that the broadcast for disconnection is sent. + final ConditionVariable cv4 = registerConnectivityBroadcastThat(1, intent -> + !((NetworkInfo) intent.getExtra(EXTRA_NETWORK_INFO, -1)).isConnected() + && intent.getIntExtra(EXTRA_NETWORK_TYPE, -1) == TYPE_MOBILE_SUPL); + mCm.unregisterNetworkCallback(callback); + waitFor(cv4); + + // Re-file the request and expect the connected broadcast again + final ConditionVariable cv5 = registerConnectivityBroadcastThat(1, + intent -> intent.getIntExtra(EXTRA_NETWORK_TYPE, -1) == TYPE_MOBILE_SUPL); + final TestNetworkCallback callback3 = new TestNetworkCallback(); + mCm.requestNetwork(legacyRequest, callback3); + callback3.expectCallback(CallbackEntry.AVAILABLE, mCellNetworkAgent); + waitFor(cv5); + + // Disconnect the network and expect two disconnected broadcasts, one for SUPL and one + // for mobile. Use a small hack to check that both have been sent, but the order is + // not contractual. + final AtomicBoolean vanillaAction = new AtomicBoolean(false); + final AtomicBoolean suplAction = new AtomicBoolean(false); + final ConditionVariable cv6 = registerConnectivityBroadcastThat(2, intent -> { + if (intent.getAction().equals(CONNECTIVITY_ACTION)) { + vanillaAction.set(true); + } else if (intent.getAction().equals(CONNECTIVITY_ACTION_SUPL)) { + suplAction.set(true); + } + return !((NetworkInfo) intent.getExtra(EXTRA_NETWORK_INFO, -1)).isConnected(); + }); + mCellNetworkAgent.disconnect(); + waitFor(cv6); + assertTrue(vanillaAction.get()); + assertTrue(suplAction.get()); + } + + @Test public void testLingering() throws Exception { verifyNoNetwork(); mCellNetworkAgent = new TestNetworkAgentWrapper(TRANSPORT_CELLULAR); @@ -1265,7 +1366,7 @@ public class ConnectivityServiceTest { assertNull(mCm.getActiveNetworkInfo()); assertNull(mCm.getActiveNetwork()); // Test bringing up validated cellular. - ConditionVariable cv = waitForConnectivityBroadcasts(1); + ConditionVariable cv = registerConnectivityBroadcast(1); mCellNetworkAgent.connect(true); waitFor(cv); verifyActiveNetwork(TRANSPORT_CELLULAR); @@ -1275,7 +1376,7 @@ public class ConnectivityServiceTest { assertTrue(mCm.getAllNetworks()[0].equals(mWiFiNetworkAgent.getNetwork()) || mCm.getAllNetworks()[1].equals(mWiFiNetworkAgent.getNetwork())); // Test bringing up validated WiFi. - cv = waitForConnectivityBroadcasts(2); + cv = registerConnectivityBroadcast(2); mWiFiNetworkAgent.connect(true); waitFor(cv); verifyActiveNetwork(TRANSPORT_WIFI); @@ -1292,7 +1393,7 @@ public class ConnectivityServiceTest { assertLength(1, mCm.getAllNetworks()); assertEquals(mCm.getAllNetworks()[0], mCm.getActiveNetwork()); // Test WiFi disconnect. - cv = waitForConnectivityBroadcasts(1); + cv = registerConnectivityBroadcast(1); mWiFiNetworkAgent.disconnect(); waitFor(cv); verifyNoNetwork(); @@ -1302,7 +1403,7 @@ public class ConnectivityServiceTest { public void testValidatedCellularOutscoresUnvalidatedWiFi() throws Exception { // Test bringing up unvalidated WiFi mWiFiNetworkAgent = new TestNetworkAgentWrapper(TRANSPORT_WIFI); - ConditionVariable cv = waitForConnectivityBroadcasts(1); + ConditionVariable cv = registerConnectivityBroadcast(1); mWiFiNetworkAgent.connect(false); waitFor(cv); verifyActiveNetwork(TRANSPORT_WIFI); @@ -1317,17 +1418,17 @@ public class ConnectivityServiceTest { verifyActiveNetwork(TRANSPORT_WIFI); // Test bringing up validated cellular mCellNetworkAgent = new TestNetworkAgentWrapper(TRANSPORT_CELLULAR); - cv = waitForConnectivityBroadcasts(2); + cv = registerConnectivityBroadcast(2); mCellNetworkAgent.connect(true); waitFor(cv); verifyActiveNetwork(TRANSPORT_CELLULAR); // Test cellular disconnect. - cv = waitForConnectivityBroadcasts(2); + cv = registerConnectivityBroadcast(2); mCellNetworkAgent.disconnect(); waitFor(cv); verifyActiveNetwork(TRANSPORT_WIFI); // Test WiFi disconnect. - cv = waitForConnectivityBroadcasts(1); + cv = registerConnectivityBroadcast(1); mWiFiNetworkAgent.disconnect(); waitFor(cv); verifyNoNetwork(); @@ -1337,23 +1438,23 @@ public class ConnectivityServiceTest { public void testUnvalidatedWifiOutscoresUnvalidatedCellular() throws Exception { // Test bringing up unvalidated cellular. mCellNetworkAgent = new TestNetworkAgentWrapper(TRANSPORT_CELLULAR); - ConditionVariable cv = waitForConnectivityBroadcasts(1); + ConditionVariable cv = registerConnectivityBroadcast(1); mCellNetworkAgent.connect(false); waitFor(cv); verifyActiveNetwork(TRANSPORT_CELLULAR); // Test bringing up unvalidated WiFi. mWiFiNetworkAgent = new TestNetworkAgentWrapper(TRANSPORT_WIFI); - cv = waitForConnectivityBroadcasts(2); + cv = registerConnectivityBroadcast(2); mWiFiNetworkAgent.connect(false); waitFor(cv); verifyActiveNetwork(TRANSPORT_WIFI); // Test WiFi disconnect. - cv = waitForConnectivityBroadcasts(2); + cv = registerConnectivityBroadcast(2); mWiFiNetworkAgent.disconnect(); waitFor(cv); verifyActiveNetwork(TRANSPORT_CELLULAR); // Test cellular disconnect. - cv = waitForConnectivityBroadcasts(1); + cv = registerConnectivityBroadcast(1); mCellNetworkAgent.disconnect(); waitFor(cv); verifyNoNetwork(); @@ -1363,7 +1464,7 @@ public class ConnectivityServiceTest { public void testUnlingeringDoesNotValidate() throws Exception { // Test bringing up unvalidated WiFi. mWiFiNetworkAgent = new TestNetworkAgentWrapper(TRANSPORT_WIFI); - ConditionVariable cv = waitForConnectivityBroadcasts(1); + ConditionVariable cv = registerConnectivityBroadcast(1); mWiFiNetworkAgent.connect(false); waitFor(cv); verifyActiveNetwork(TRANSPORT_WIFI); @@ -1371,14 +1472,14 @@ public class ConnectivityServiceTest { NET_CAPABILITY_VALIDATED)); // Test bringing up validated cellular. mCellNetworkAgent = new TestNetworkAgentWrapper(TRANSPORT_CELLULAR); - cv = waitForConnectivityBroadcasts(2); + cv = registerConnectivityBroadcast(2); mCellNetworkAgent.connect(true); waitFor(cv); verifyActiveNetwork(TRANSPORT_CELLULAR); assertFalse(mCm.getNetworkCapabilities(mWiFiNetworkAgent.getNetwork()).hasCapability( NET_CAPABILITY_VALIDATED)); // Test cellular disconnect. - cv = waitForConnectivityBroadcasts(2); + cv = registerConnectivityBroadcast(2); mCellNetworkAgent.disconnect(); waitFor(cv); verifyActiveNetwork(TRANSPORT_WIFI); @@ -1391,23 +1492,23 @@ public class ConnectivityServiceTest { public void testCellularOutscoresWeakWifi() throws Exception { // Test bringing up validated cellular. mCellNetworkAgent = new TestNetworkAgentWrapper(TRANSPORT_CELLULAR); - ConditionVariable cv = waitForConnectivityBroadcasts(1); + ConditionVariable cv = registerConnectivityBroadcast(1); mCellNetworkAgent.connect(true); waitFor(cv); verifyActiveNetwork(TRANSPORT_CELLULAR); // Test bringing up validated WiFi. mWiFiNetworkAgent = new TestNetworkAgentWrapper(TRANSPORT_WIFI); - cv = waitForConnectivityBroadcasts(2); + cv = registerConnectivityBroadcast(2); mWiFiNetworkAgent.connect(true); waitFor(cv); verifyActiveNetwork(TRANSPORT_WIFI); // Test WiFi getting really weak. - cv = waitForConnectivityBroadcasts(2); + cv = registerConnectivityBroadcast(2); mWiFiNetworkAgent.adjustScore(-11); waitFor(cv); verifyActiveNetwork(TRANSPORT_CELLULAR); // Test WiFi restoring signal strength. - cv = waitForConnectivityBroadcasts(2); + cv = registerConnectivityBroadcast(2); mWiFiNetworkAgent.adjustScore(11); waitFor(cv); verifyActiveNetwork(TRANSPORT_WIFI); @@ -1427,7 +1528,7 @@ public class ConnectivityServiceTest { mCellNetworkAgent.expectDisconnected(); // Test bringing up validated WiFi. mWiFiNetworkAgent = new TestNetworkAgentWrapper(TRANSPORT_WIFI); - final ConditionVariable cv = waitForConnectivityBroadcasts(1); + final ConditionVariable cv = registerConnectivityBroadcast(1); mWiFiNetworkAgent.connect(true); waitFor(cv); verifyActiveNetwork(TRANSPORT_WIFI); @@ -1446,18 +1547,18 @@ public class ConnectivityServiceTest { public void testCellularFallback() throws Exception { // Test bringing up validated cellular. mCellNetworkAgent = new TestNetworkAgentWrapper(TRANSPORT_CELLULAR); - ConditionVariable cv = waitForConnectivityBroadcasts(1); + ConditionVariable cv = registerConnectivityBroadcast(1); mCellNetworkAgent.connect(true); waitFor(cv); verifyActiveNetwork(TRANSPORT_CELLULAR); // Test bringing up validated WiFi. mWiFiNetworkAgent = new TestNetworkAgentWrapper(TRANSPORT_WIFI); - cv = waitForConnectivityBroadcasts(2); + cv = registerConnectivityBroadcast(2); mWiFiNetworkAgent.connect(true); waitFor(cv); verifyActiveNetwork(TRANSPORT_WIFI); // Reevaluate WiFi (it'll instantly fail DNS). - cv = waitForConnectivityBroadcasts(2); + cv = registerConnectivityBroadcast(2); assertTrue(mCm.getNetworkCapabilities(mWiFiNetworkAgent.getNetwork()).hasCapability( NET_CAPABILITY_VALIDATED)); mCm.reportBadNetwork(mWiFiNetworkAgent.getNetwork()); @@ -1467,7 +1568,7 @@ public class ConnectivityServiceTest { NET_CAPABILITY_VALIDATED)); verifyActiveNetwork(TRANSPORT_CELLULAR); // Reevaluate cellular (it'll instantly fail DNS). - cv = waitForConnectivityBroadcasts(2); + cv = registerConnectivityBroadcast(2); assertTrue(mCm.getNetworkCapabilities(mCellNetworkAgent.getNetwork()).hasCapability( NET_CAPABILITY_VALIDATED)); mCm.reportBadNetwork(mCellNetworkAgent.getNetwork()); @@ -1484,18 +1585,18 @@ public class ConnectivityServiceTest { public void testWiFiFallback() throws Exception { // Test bringing up unvalidated WiFi. mWiFiNetworkAgent = new TestNetworkAgentWrapper(TRANSPORT_WIFI); - ConditionVariable cv = waitForConnectivityBroadcasts(1); + ConditionVariable cv = registerConnectivityBroadcast(1); mWiFiNetworkAgent.connect(false); waitFor(cv); verifyActiveNetwork(TRANSPORT_WIFI); // Test bringing up validated cellular. mCellNetworkAgent = new TestNetworkAgentWrapper(TRANSPORT_CELLULAR); - cv = waitForConnectivityBroadcasts(2); + cv = registerConnectivityBroadcast(2); mCellNetworkAgent.connect(true); waitFor(cv); verifyActiveNetwork(TRANSPORT_CELLULAR); // Reevaluate cellular (it'll instantly fail DNS). - cv = waitForConnectivityBroadcasts(2); + cv = registerConnectivityBroadcast(2); assertTrue(mCm.getNetworkCapabilities(mCellNetworkAgent.getNetwork()).hasCapability( NET_CAPABILITY_VALIDATED)); mCm.reportBadNetwork(mCellNetworkAgent.getNetwork()); @@ -1570,7 +1671,7 @@ public class ConnectivityServiceTest { mCm.registerNetworkCallback(cellRequest, cellNetworkCallback); // Test unvalidated networks - ConditionVariable cv = waitForConnectivityBroadcasts(1); + ConditionVariable cv = registerConnectivityBroadcast(1); mCellNetworkAgent = new TestNetworkAgentWrapper(TRANSPORT_CELLULAR); mCellNetworkAgent.connect(false); genericNetworkCallback.expectAvailableCallbacksUnvalidated(mCellNetworkAgent); @@ -1585,7 +1686,7 @@ public class ConnectivityServiceTest { assertNoCallbacks(genericNetworkCallback, wifiNetworkCallback, cellNetworkCallback); assertEquals(mCellNetworkAgent.getNetwork(), mCm.getActiveNetwork()); - cv = waitForConnectivityBroadcasts(2); + cv = registerConnectivityBroadcast(2); mWiFiNetworkAgent = new TestNetworkAgentWrapper(TRANSPORT_WIFI); mWiFiNetworkAgent.connect(false); genericNetworkCallback.expectAvailableCallbacksUnvalidated(mWiFiNetworkAgent); @@ -1594,7 +1695,7 @@ public class ConnectivityServiceTest { waitFor(cv); assertNoCallbacks(genericNetworkCallback, wifiNetworkCallback, cellNetworkCallback); - cv = waitForConnectivityBroadcasts(2); + cv = registerConnectivityBroadcast(2); mWiFiNetworkAgent.disconnect(); genericNetworkCallback.expectCallback(CallbackEntry.LOST, mWiFiNetworkAgent); wifiNetworkCallback.expectCallback(CallbackEntry.LOST, mWiFiNetworkAgent); @@ -1602,7 +1703,7 @@ public class ConnectivityServiceTest { waitFor(cv); assertNoCallbacks(genericNetworkCallback, wifiNetworkCallback, cellNetworkCallback); - cv = waitForConnectivityBroadcasts(1); + cv = registerConnectivityBroadcast(1); mCellNetworkAgent.disconnect(); genericNetworkCallback.expectCallback(CallbackEntry.LOST, mCellNetworkAgent); cellNetworkCallback.expectCallback(CallbackEntry.LOST, mCellNetworkAgent); @@ -2192,7 +2293,7 @@ public class ConnectivityServiceTest { // Test bringing up validated WiFi. mWiFiNetworkAgent = new TestNetworkAgentWrapper(TRANSPORT_WIFI); - final ConditionVariable cv = waitForConnectivityBroadcasts(1); + final ConditionVariable cv = registerConnectivityBroadcast(1); mWiFiNetworkAgent.connect(true); waitFor(cv); verifyActiveNetwork(TRANSPORT_WIFI); @@ -2220,7 +2321,7 @@ public class ConnectivityServiceTest { public void testMMSonCell() throws Exception { // Test bringing up cellular without MMS mCellNetworkAgent = new TestNetworkAgentWrapper(TRANSPORT_CELLULAR); - ConditionVariable cv = waitForConnectivityBroadcasts(1); + ConditionVariable cv = registerConnectivityBroadcast(1); mCellNetworkAgent.connect(false); waitFor(cv); verifyActiveNetwork(TRANSPORT_CELLULAR); @@ -3679,7 +3780,7 @@ public class ConnectivityServiceTest { } mWiFiNetworkAgent = new TestNetworkAgentWrapper(TRANSPORT_WIFI); - ConditionVariable cv = waitForConnectivityBroadcasts(1); + ConditionVariable cv = registerConnectivityBroadcast(1); mWiFiNetworkAgent.connect(true); waitFor(cv); verifyActiveNetwork(TRANSPORT_WIFI); @@ -4212,7 +4313,7 @@ public class ConnectivityServiceTest { assertNotPinnedToWifi(); // Disconnect cell and wifi. - ConditionVariable cv = waitForConnectivityBroadcasts(3); // cell down, wifi up, wifi down. + ConditionVariable cv = registerConnectivityBroadcast(3); // cell down, wifi up, wifi down. mCellNetworkAgent.disconnect(); mWiFiNetworkAgent.disconnect(); waitFor(cv); @@ -4225,7 +4326,7 @@ public class ConnectivityServiceTest { assertPinnedToWifiWithWifiDefault(); // ... and is maintained even when that network is no longer the default. - cv = waitForConnectivityBroadcasts(1); + cv = registerConnectivityBroadcast(1); mCellNetworkAgent = new TestNetworkAgentWrapper(TRANSPORT_WIFI); mCellNetworkAgent.connect(true); waitFor(cv); @@ -4326,7 +4427,7 @@ public class ConnectivityServiceTest { @Test public void testNetworkInfoOfTypeNone() throws Exception { - ConditionVariable broadcastCV = waitForConnectivityBroadcasts(1); + ConditionVariable broadcastCV = registerConnectivityBroadcast(1); verifyNoNetwork(); TestNetworkAgentWrapper wifiAware = new TestNetworkAgentWrapper(TRANSPORT_WIFI_AWARE); @@ -5778,7 +5879,7 @@ public class ConnectivityServiceTest { .destroyNetworkCache(eq(mCellNetworkAgent.getNetwork().netId)); // Disconnect wifi - ConditionVariable cv = waitForConnectivityBroadcasts(1); + ConditionVariable cv = registerConnectivityBroadcast(1); reset(mNetworkManagementService); mWiFiNetworkAgent.disconnect(); waitFor(cv); diff --git a/tests/net/java/com/android/server/LegacyTypeTrackerTest.kt b/tests/net/java/com/android/server/LegacyTypeTrackerTest.kt index f045369459c9..42d4cf3c382b 100644 --- a/tests/net/java/com/android/server/LegacyTypeTrackerTest.kt +++ b/tests/net/java/com/android/server/LegacyTypeTrackerTest.kt @@ -18,6 +18,7 @@ package com.android.server import android.net.ConnectivityManager.TYPE_ETHERNET import android.net.ConnectivityManager.TYPE_MOBILE +import android.net.ConnectivityManager.TYPE_MOBILE_SUPL import android.net.ConnectivityManager.TYPE_WIFI import android.net.ConnectivityManager.TYPE_WIMAX import android.net.NetworkInfo.DetailedState.CONNECTED @@ -46,7 +47,7 @@ const val UNSUPPORTED_TYPE = TYPE_WIMAX @RunWith(AndroidJUnit4::class) @SmallTest class LegacyTypeTrackerTest { - private val supportedTypes = arrayOf(TYPE_MOBILE, TYPE_WIFI, TYPE_ETHERNET) + private val supportedTypes = arrayOf(TYPE_MOBILE, TYPE_WIFI, TYPE_ETHERNET, TYPE_MOBILE_SUPL) private val mMockService = mock(ConnectivityService::class.java).apply { doReturn(false).`when`(this).isDefaultNetwork(any()) @@ -70,6 +71,26 @@ class LegacyTypeTrackerTest { } @Test + fun testSupl() { + val mobileNai = mock(NetworkAgentInfo::class.java) + mTracker.add(TYPE_MOBILE, mobileNai) + verify(mMockService).sendLegacyNetworkBroadcast(mobileNai, CONNECTED, TYPE_MOBILE) + reset(mMockService) + mTracker.add(TYPE_MOBILE_SUPL, mobileNai) + verify(mMockService).sendLegacyNetworkBroadcast(mobileNai, CONNECTED, TYPE_MOBILE_SUPL) + reset(mMockService) + mTracker.remove(TYPE_MOBILE_SUPL, mobileNai, false /* wasDefault */) + verify(mMockService).sendLegacyNetworkBroadcast(mobileNai, DISCONNECTED, TYPE_MOBILE_SUPL) + reset(mMockService) + mTracker.add(TYPE_MOBILE_SUPL, mobileNai) + verify(mMockService).sendLegacyNetworkBroadcast(mobileNai, CONNECTED, TYPE_MOBILE_SUPL) + reset(mMockService) + mTracker.remove(mobileNai, false) + verify(mMockService).sendLegacyNetworkBroadcast(mobileNai, DISCONNECTED, TYPE_MOBILE_SUPL) + verify(mMockService).sendLegacyNetworkBroadcast(mobileNai, DISCONNECTED, TYPE_MOBILE) + } + + @Test fun testAddNetwork() { val mobileNai = mock(NetworkAgentInfo::class.java) val wifiNai = mock(NetworkAgentInfo::class.java) diff --git a/tools/genprotos.sh b/tools/genprotos.sh deleted file mode 100755 index f901c9f588b6..000000000000 --- a/tools/genprotos.sh +++ /dev/null @@ -1,24 +0,0 @@ -#!/bin/bash - -# TODO This should not be needed. If you set a custom OUT_DIR or OUT_DIR_COMMON_BASE you can -# end up with a command that is extremely long, potentially going passed MAX_ARG_STRLEN due to -# the way sbox rewrites the command. See b/70221552. - -set -e - -location_aprotoc=$1 -location_protoc=$2 -location_soong_zip=$3 -genDir=$4 -depfile=$5 -in=$6 -out=$7 - -mkdir -p ${genDir}/${in} && \ - ${location_aprotoc} --plugin=${location_protoc} \ - --dependency_out=${depfile} \ - --javastream_out=${genDir}/${in} \ - -Iexternal/protobuf/src \ - -I . \ - ${in} && \ - ${location_soong_zip} -jar -o ${out} -C ${genDir}/${in} -D ${genDir}/${in} diff --git a/tools/processors/unsupportedappusage/Android.bp b/tools/processors/unsupportedappusage/Android.bp index 0e33fddcde07..1e96234543c8 100644 --- a/tools/processors/unsupportedappusage/Android.bp +++ b/tools/processors/unsupportedappusage/Android.bp @@ -1,11 +1,6 @@ -java_plugin { - name: "unsupportedappusage-annotation-processor", - processor_class: "android.processor.unsupportedappusage.UnsupportedAppUsageProcessor", - - java_resources: [ - "META-INF/**/*", - ], +java_library_host { + name: "unsupportedappusage-annotation-processor-lib", srcs: [ "src/**/*.java", ], @@ -22,6 +17,18 @@ java_plugin { "--add-exports jdk.compiler/com.sun.tools.javac.util=ALL-UNNAMED", ], }, +} + +java_plugin { + name: "unsupportedappusage-annotation-processor", + processor_class: "android.processor.unsupportedappusage.UnsupportedAppUsageProcessor", + + java_resources: [ + "META-INF/**/*", + ], + static_libs: [ + "unsupportedappusage-annotation-processor-lib" + ], use_tools_jar: true, } diff --git a/tools/processors/unsupportedappusage/src/android/processor/unsupportedappusage/SignatureBuilder.java b/tools/processors/unsupportedappusage/src/android/processor/unsupportedappusage/SignatureBuilder.java index 5a5703ed520c..65fc733fa364 100644 --- a/tools/processors/unsupportedappusage/src/android/processor/unsupportedappusage/SignatureBuilder.java +++ b/tools/processors/unsupportedappusage/src/android/processor/unsupportedappusage/SignatureBuilder.java @@ -101,14 +101,20 @@ public class SignatureBuilder { private String getClassSignature(TypeElement clazz) { StringBuilder sb = new StringBuilder("L"); for (Element enclosing : getEnclosingElements(clazz)) { - if (enclosing.getKind() == PACKAGE) { - sb.append(((PackageElement) enclosing) - .getQualifiedName() - .toString() - .replace('.', '/')); - sb.append('/'); - } else { - sb.append(enclosing.getSimpleName()).append('$'); + switch (enclosing.getKind()) { + case MODULE: + // ignore this. + break; + case PACKAGE: + sb.append(((PackageElement) enclosing) + .getQualifiedName() + .toString() + .replace('.', '/')); + sb.append('/'); + break; + default: + sb.append(enclosing.getSimpleName()).append('$'); + break; } } diff --git a/tools/processors/unsupportedappusage/test/Android.bp b/tools/processors/unsupportedappusage/test/Android.bp new file mode 100644 index 000000000000..49ea3d4bbc96 --- /dev/null +++ b/tools/processors/unsupportedappusage/test/Android.bp @@ -0,0 +1,28 @@ +// 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. + +java_test_host { + name: "unsupportedappusage-processor-test", + + srcs: ["src/**/*.java"], + + static_libs: [ + "libjavac", + "unsupportedappusage-annotation-processor-lib", + "truth-host-prebuilt", + "mockito-host", + "junit-host", + "objenesis", + ], +} diff --git a/tools/processors/unsupportedappusage/test/src/android/processor/unsupportedappusage/CsvReader.java b/tools/processors/unsupportedappusage/test/src/android/processor/unsupportedappusage/CsvReader.java new file mode 100644 index 000000000000..23db99e81194 --- /dev/null +++ b/tools/processors/unsupportedappusage/test/src/android/processor/unsupportedappusage/CsvReader.java @@ -0,0 +1,61 @@ +/* + * 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.processor.unsupportedappusage; + +import com.google.common.base.Splitter; + +import java.io.BufferedReader; +import java.io.IOException; +import java.io.InputStream; +import java.io.InputStreamReader; +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +public class CsvReader { + + private final Splitter mSplitter; + private final List<String> mColumns; + private final List<Map<String, String>> mContents; + + public CsvReader(InputStream in) throws IOException { + mSplitter = Splitter.on(","); + BufferedReader br = new BufferedReader(new InputStreamReader(in)); + mColumns = mSplitter.splitToList(br.readLine()); + mContents = new ArrayList<>(); + String line = br.readLine(); + while (line != null) { + List<String> contents = mSplitter.splitToList(line); + Map<String, String> contentMap = new HashMap<>(); + for (int i = 0; i < Math.min(contents.size(), mColumns.size()); ++i) { + contentMap.put(mColumns.get(i), contents.get(i)); + } + mContents.add(contentMap); + line = br.readLine(); + } + br.close(); + } + + public List<String> getColumns() { + return mColumns; + } + + public List<Map<String, String>> getContents() { + return mContents; + } +} diff --git a/tools/processors/unsupportedappusage/test/src/android/processor/unsupportedappusage/UnsupportedAppUsageProcessorTest.java b/tools/processors/unsupportedappusage/test/src/android/processor/unsupportedappusage/UnsupportedAppUsageProcessorTest.java new file mode 100644 index 000000000000..012e88f17924 --- /dev/null +++ b/tools/processors/unsupportedappusage/test/src/android/processor/unsupportedappusage/UnsupportedAppUsageProcessorTest.java @@ -0,0 +1,96 @@ +/* + * 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.processor.unsupportedappusage; + +import static com.google.common.truth.Truth.assertThat; + +import com.android.javac.Javac; + +import com.google.common.base.Joiner; + +import org.junit.Before; +import org.junit.Test; + +import java.io.IOException; +import java.util.Map; + +public class UnsupportedAppUsageProcessorTest { + + private Javac mJavac; + + @Before + public void setup() throws IOException { + mJavac = new Javac(); + mJavac.addSource("dalvik.annotation.compat.UnsupportedAppUsage", Joiner.on('\n').join( + "package dalvik.annotation.compat;", + "public @interface UnsupportedAppUsage {", + " String expectedSignature() default \"\";\n", + " String someProperty() default \"\";", + "}")); + } + + private CsvReader compileAndReadCsv() throws IOException { + mJavac.compileWithAnnotationProcessor(new UnsupportedAppUsageProcessor()); + return new CsvReader( + mJavac.getOutputFile("unsupportedappusage/unsupportedappusage_index.csv")); + } + + @Test + public void testSignatureFormat() throws Exception { + mJavac.addSource("a.b.Class", Joiner.on('\n').join( + "package a.b;", + "import dalvik.annotation.compat.UnsupportedAppUsage;", + "public class Class {", + " @UnsupportedAppUsage", + " public void method() {}", + "}")); + assertThat(compileAndReadCsv().getContents().get(0)).containsEntry( + "signature", "La/b/Class;->method()V" + ); + } + + @Test + public void testSourcePosition() throws Exception { + mJavac.addSource("a.b.Class", Joiner.on('\n').join( + "package a.b;", // 1 + "import dalvik.annotation.compat.UnsupportedAppUsage;", // 2 + "public class Class {", // 3 + " @UnsupportedAppUsage", // 4 + " public void method() {}", // 5 + "}")); + Map<String, String> row = compileAndReadCsv().getContents().get(0); + assertThat(row).containsEntry("startline", "4"); + assertThat(row).containsEntry("startcol", "3"); + assertThat(row).containsEntry("endline", "4"); + assertThat(row).containsEntry("endcol", "23"); + } + + @Test + public void testAnnotationProperties() throws Exception { + mJavac.addSource("a.b.Class", Joiner.on('\n').join( + "package a.b;", // 1 + "import dalvik.annotation.compat.UnsupportedAppUsage;", // 2 + "public class Class {", // 3 + " @UnsupportedAppUsage(someProperty=\"value\")", // 4 + " public void method() {}", // 5 + "}")); + assertThat(compileAndReadCsv().getContents().get(0)).containsEntry( + "properties", "someProperty=%22value%22"); + } + + +} |