summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--Android.bp28
-rw-r--r--api/current.txt14
-rw-r--r--api/system-current.txt51
-rw-r--r--api/test-current.txt3
-rw-r--r--core/java/android/accounts/AbstractAccountAuthenticator.java2
-rw-r--r--core/java/android/accounts/AccountAuthenticatorActivity.java4
-rw-r--r--core/java/android/app/IUiModeManager.aidl8
-rw-r--r--core/java/android/app/SystemServiceRegistry.java6
-rw-r--r--core/java/android/app/UiModeManager.java164
-rw-r--r--core/java/android/app/contentsuggestions/ContentSuggestionsManager.java13
-rw-r--r--core/java/android/bluetooth/BluetoothAdapter.java50
-rw-r--r--core/java/android/bluetooth/BluetoothDevice.java18
-rw-r--r--core/java/android/bluetooth/BluetoothProfile.java50
-rw-r--r--core/java/android/content/Context.java3
-rw-r--r--core/java/android/content/pm/PackageManagerInternal.java11
-rw-r--r--core/java/android/os/Handler.java34
-rw-r--r--core/java/android/provider/CallLog.java16
-rw-r--r--core/java/android/service/carrier/CarrierService.java2
-rw-r--r--core/java/android/service/contentsuggestions/ContentSuggestionsService.java19
-rw-r--r--core/java/android/telephony/PhoneStateListener.java (renamed from telephony/java/android/telephony/PhoneStateListener.java)2
-rw-r--r--core/java/android/telephony/Rlog.java (renamed from telephony/java/android/telephony/Rlog.java)0
-rw-r--r--core/java/android/telephony/TelephonyRegistryManager.java (renamed from core/java/android/os/telephony/TelephonyRegistryManager.java)179
-rw-r--r--core/java/com/android/internal/app/AbstractResolverComparator.java43
-rw-r--r--core/java/com/android/internal/app/ChooserActivity.java43
-rw-r--r--core/java/com/android/internal/app/DisableCarModeActivity.java4
-rw-r--r--core/java/com/android/internal/app/ResolverActivity.java29
-rw-r--r--core/java/com/android/internal/app/ResolverListController.java11
-rw-r--r--core/java/com/android/internal/app/ResolverTargetActionsDialogFragment.java27
-rw-r--r--core/java/com/android/internal/compat/IPlatformCompat.aidl9
-rw-r--r--core/java/com/android/internal/config/sysui/SystemUiDeviceConfigFlags.java14
-rw-r--r--core/java/com/android/internal/telephony/IOnSubscriptionsChangedListener.aidl (renamed from telephony/java/com/android/internal/telephony/IOnSubscriptionsChangedListener.aidl)0
-rw-r--r--core/java/com/android/internal/telephony/IPhoneStateListener.aidl (renamed from telephony/java/com/android/internal/telephony/IPhoneStateListener.aidl)3
-rw-r--r--core/java/com/android/internal/telephony/ITelephonyRegistry.aidl (renamed from telephony/java/com/android/internal/telephony/ITelephonyRegistry.aidl)0
-rw-r--r--core/jni/AndroidRuntime.cpp6
-rw-r--r--core/jni/core_jni_helpers.h12
-rw-r--r--core/res/AndroidManifest.xml17
-rw-r--r--core/res/res/values/arrays.xml10
-rw-r--r--core/res/res/values/config.xml2
-rw-r--r--core/res/res/values/strings.xml4
-rw-r--r--core/res/res/values/symbols.xml3
-rw-r--r--core/tests/coretests/src/com/android/internal/app/AbstractResolverComparatorTest.java105
-rw-r--r--data/etc/privapp-permissions-platform.xml3
-rw-r--r--keystore/java/android/security/KeyStore.java29
-rw-r--r--keystore/java/android/security/keystore/AndroidKeyStoreAuthenticatedAESCipherSpi.java2
-rw-r--r--keystore/java/android/security/keystore/AndroidKeyStoreKeyGeneratorSpi.java8
-rw-r--r--keystore/java/android/security/keystore/KeyStoreCryptoOperationChunkedStreamer.java9
-rw-r--r--media/jni/android_media_MediaDataSource.cpp5
-rw-r--r--media/jni/android_media_MediaDataSource.h1
-rw-r--r--packages/Shell/AndroidManifest.xml3
-rw-r--r--packages/SystemUI/AndroidManifest.xml3
-rw-r--r--packages/SystemUI/src/com/android/keyguard/KeyguardSecurityContainer.java10
-rw-r--r--packages/SystemUI/src/com/android/systemui/SystemUIFactory.java10
-rw-r--r--packages/SystemUI/src/com/android/systemui/screenshot/GlobalScreenshot.java110
-rw-r--r--packages/SystemUI/src/com/android/systemui/screenshot/ScreenshotNotificationSmartActionsProvider.java58
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/screenshot/ScreenshotNotificationSmartActionsTest.java143
-rw-r--r--packages/Tethering/src/com/android/server/connectivity/tethering/TetheringConfiguration.java2
-rw-r--r--packages/Tethering/tests/unit/src/com/android/server/connectivity/tethering/TetheringConfigurationTest.java10
-rw-r--r--services/contentsuggestions/java/com/android/server/contentsuggestions/ContentSuggestionsManagerService.java3
-rw-r--r--services/contentsuggestions/java/com/android/server/contentsuggestions/ContentSuggestionsPerUserService.java19
-rw-r--r--services/core/Android.bp2
-rw-r--r--services/core/java/com/android/server/BinderCallsStatsService.java10
-rw-r--r--services/core/java/com/android/server/UiModeManagerService.java212
-rw-r--r--services/core/java/com/android/server/VibratorService.java4
-rw-r--r--services/core/java/com/android/server/Watchdog.java1
-rw-r--r--services/core/java/com/android/server/compat/PlatformCompat.java6
-rw-r--r--services/core/java/com/android/server/pm/InstructionSets.java13
-rw-r--r--services/core/java/com/android/server/pm/PackageAbiHelper.java135
-rw-r--r--services/core/java/com/android/server/pm/PackageAbiHelperImpl.java528
-rw-r--r--services/core/java/com/android/server/pm/PackageManagerService.java659
-rw-r--r--services/core/java/com/android/server/pm/PackageSettingBase.java15
-rw-r--r--services/core/java/com/android/server/pm/Settings.java3
-rw-r--r--services/core/java/com/android/server/pm/TEST_MAPPING14
-rw-r--r--services/core/jni/Android.bp1
-rw-r--r--services/core/jni/com_android_server_VibratorService.cpp473
-rw-r--r--services/net/java/android/net/NetworkStackClient.java2
-rw-r--r--services/tests/servicestests/Android.bp1
-rw-r--r--services/tests/servicestests/src/com/android/server/contentsuggestions/ContentSuggestionsPerUserServiceTest.java90
-rw-r--r--services/tests/servicestests/src/com/android/server/pm/PackageBuilder.java122
-rw-r--r--services/tests/servicestests/src/com/android/server/pm/PackageSettingBuilder.java154
-rw-r--r--services/tests/servicestests/src/com/android/server/pm/ScanRequestBuilder.java105
-rw-r--r--services/tests/servicestests/src/com/android/server/pm/ScanTests.java551
-rw-r--r--telecomm/java/android/telecom/DisconnectCause.java8
-rw-r--r--telephony/common/com/android/internal/telephony/GsmAlphabet.java (renamed from telephony/java/com/android/internal/telephony/GsmAlphabet.java)0
-rw-r--r--telephony/java/android/telephony/CarrierConfigManager.java12
-rw-r--r--telephony/java/android/telephony/CbGeoUtils.java (renamed from telephony/java/com/android/internal/telephony/CbGeoUtils.java)46
-rw-r--r--telephony/java/android/telephony/DisconnectCause.java8
-rw-r--r--telephony/java/android/telephony/SmsCbMessage.java7
-rw-r--r--telephony/java/android/telephony/SubscriptionManager.java131
-rw-r--r--telephony/java/android/telephony/TelephonyManager.java8
-rw-r--r--telephony/java/android/telephony/ims/ImsMmTelManager.java12
-rw-r--r--telephony/java/android/telephony/ims/feature/RcsFeature.java7
-rw-r--r--telephony/java/com/android/internal/telephony/ITelephony.aidl2
-rw-r--r--telephony/java/com/android/internal/telephony/gsm/GsmSmsCbMessage.java10
-rw-r--r--tests/PlatformCompatGating/Android.bp33
-rw-r--r--tests/PlatformCompatGating/AndroidManifest.xml11
-rw-r--r--tests/PlatformCompatGating/AndroidTest.xml30
-rw-r--r--tests/PlatformCompatGating/src/com/android/compat/testing/DummyApi.java87
-rw-r--r--tests/PlatformCompatGating/src/com/android/tests/gating/PlatformCompatGatingTest.java94
-rw-r--r--tests/PlatformCompatGating/test-rules/Android.bp26
-rw-r--r--tests/PlatformCompatGating/test-rules/src/android/compat/testing/PlatformCompatChangeRule.java108
-rw-r--r--tests/net/java/com/android/server/ConnectivityServiceTest.java185
-rw-r--r--tests/net/java/com/android/server/LegacyTypeTrackerTest.kt23
-rwxr-xr-xtools/genprotos.sh24
-rw-r--r--tools/processors/unsupportedappusage/Android.bp21
-rw-r--r--tools/processors/unsupportedappusage/src/android/processor/unsupportedappusage/SignatureBuilder.java22
-rw-r--r--tools/processors/unsupportedappusage/test/Android.bp28
-rw-r--r--tools/processors/unsupportedappusage/test/src/android/processor/unsupportedappusage/CsvReader.java61
-rw-r--r--tools/processors/unsupportedappusage/test/src/android/processor/unsupportedappusage/UnsupportedAppUsageProcessorTest.java96
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");
+ }
+
+
+}