summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--api/current.txt17
-rw-r--r--api/system-current.txt18
-rw-r--r--api/test-current.txt19
-rw-r--r--compiled-classes-phone4
-rw-r--r--core/java/android/app/ActivityThread.java6
-rw-r--r--core/java/android/app/ApplicationPackageManager.java15
-rw-r--r--core/java/android/app/admin/DeviceAdminReceiver.java114
-rw-r--r--core/java/android/app/admin/DevicePolicyManager.java68
-rw-r--r--core/java/android/content/Intent.java46
-rw-r--r--core/java/android/content/pm/IPackageManager.aidl4
-rw-r--r--core/java/android/content/pm/PackageInstaller.java8
-rw-r--r--core/java/android/content/pm/PackageManager.java36
-rw-r--r--core/java/android/content/pm/PackageParser.java4
-rw-r--r--core/java/android/content/pm/PackageUserState.java6
-rw-r--r--core/java/android/os/GraphicsEnvironment.java95
-rw-r--r--core/java/android/view/inputmethod/InputMethodInfo.java41
-rw-r--r--core/java/com/android/internal/app/ChooserActivity.java19
-rw-r--r--core/java/com/android/internal/app/ResolverActivity.java4
-rw-r--r--core/java/com/android/internal/app/ResolverComparator.java35
-rw-r--r--core/java/com/android/internal/app/ResolverListController.java6
-rw-r--r--core/java/com/android/internal/os/ZygoteInit.java10
-rw-r--r--core/java/com/android/internal/widget/PasswordEntryKeyboard.java267
-rw-r--r--core/java/com/android/internal/widget/PasswordEntryKeyboardHelper.java340
-rw-r--r--core/java/com/android/internal/widget/PasswordEntryKeyboardView.java55
-rw-r--r--core/java/com/android/internal/widget/RotarySelector.java780
-rw-r--r--core/jni/Android.mk1
-rw-r--r--core/jni/AndroidRuntime.cpp2
-rw-r--r--core/jni/android_os_GraphicsEnvironment.cpp44
-rw-r--r--core/jni/android_os_HwBinder.h2
-rw-r--r--core/res/res/values/attrs.xml19
-rw-r--r--core/res/res/values/public.xml1
-rw-r--r--core/tests/coretests/res/xml/ime_meta.xml27
-rw-r--r--core/tests/coretests/res/xml/ime_meta_dismiss.xml28
-rw-r--r--core/tests/coretests/res/xml/ime_meta_sw_next.xml28
-rw-r--r--core/tests/coretests/src/android/view/inputmethod/InputMethodInfoTest.java111
-rw-r--r--core/tests/coretests/src/com/android/internal/app/ChooserActivityTest.java10
-rw-r--r--core/tests/coretests/src/com/android/internal/inputmethod/InputMethodSubtypeSwitchingControllerTest.java3
-rw-r--r--location/java/android/location/GnssMeasurement.java140
-rw-r--r--location/java/android/location/GnssStatus.java30
-rw-r--r--location/java/android/location/IGnssStatusListener.aidl3
-rw-r--r--location/java/android/location/Location.java247
-rw-r--r--location/java/android/location/LocationManager.java5
-rw-r--r--media/java/android/media/AudioManager.java9
-rw-r--r--media/java/android/media/AudioPlaybackConfiguration.java102
-rw-r--r--media/java/android/media/AudioTrack.java21
-rw-r--r--media/java/android/media/IPlayer.aidl1
-rw-r--r--media/java/android/media/MediaPlayer.java17
-rw-r--r--media/java/android/media/PlayerBase.java54
-rw-r--r--media/java/android/media/SoundPool.java17
-rw-r--r--packages/SettingsLib/res/drawable/ic_bt_cellphone.xml2
-rw-r--r--packages/SettingsLib/res/drawable/ic_bt_headphones_a2dp.xml2
-rw-r--r--packages/SettingsLib/res/drawable/ic_bt_headset_hfp.xml2
-rw-r--r--packages/SettingsLib/res/drawable/ic_bt_misc_hid.xml2
-rw-r--r--packages/SettingsLib/res/drawable/ic_bt_network_pan.xml2
-rw-r--r--packages/SettingsLib/res/drawable/ic_bt_pointing_hid.xml2
-rw-r--r--packages/SettingsLib/src/com/android/settingslib/users/AppRestrictionsHelper.java3
-rw-r--r--packages/SettingsLib/tests/integ/src/com/android/settingslib/users/AppRestrictionsHelperTest.java7
-rw-r--r--packages/SystemUI/src/com/android/systemui/recents/views/TaskViewThumbnail.java25
-rw-r--r--services/core/java/com/android/server/audio/PlaybackActivityMonitor.java17
-rw-r--r--services/core/java/com/android/server/connectivity/NetworkNotificationManager.java2
-rw-r--r--services/core/java/com/android/server/location/FlpHardwareProvider.java495
-rw-r--r--services/core/java/com/android/server/location/GnssLocationProvider.java107
-rw-r--r--services/core/java/com/android/server/location/GnssStatusListenerHelper.java6
-rw-r--r--services/core/java/com/android/server/pm/PackageManagerService.java163
-rw-r--r--services/core/java/com/android/server/pm/PackageSettingBase.java11
-rw-r--r--services/core/java/com/android/server/pm/Settings.java15
-rw-r--r--services/core/java/com/android/server/wm/AppTransition.java17
-rw-r--r--services/core/java/com/android/server/wm/AppWindowContainerController.java2
-rw-r--r--services/core/java/com/android/server/wm/DisplayContent.java100
-rw-r--r--services/core/java/com/android/server/wm/TaskSnapshotController.java12
-rw-r--r--services/core/java/com/android/server/wm/WindowManagerService.java11
-rw-r--r--services/core/jni/Android.mk1
-rw-r--r--services/core/jni/com_android_server_location_FlpHardwareProvider.cpp1101
-rw-r--r--services/core/jni/com_android_server_location_GnssLocationProvider.cpp39
-rw-r--r--services/core/jni/onload.cpp2
-rw-r--r--services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java333
-rw-r--r--services/devicepolicy/java/com/android/server/devicepolicy/NetworkLogger.java41
-rw-r--r--services/devicepolicy/java/com/android/server/devicepolicy/NetworkLoggingHandler.java76
-rw-r--r--services/devicepolicy/java/com/android/server/devicepolicy/SecurityLogMonitor.java95
-rw-r--r--services/java/com/android/server/SystemServer.java9
-rw-r--r--services/retaildemo/java/com/android/server/retaildemo/PreloadAppsInstaller.java3
-rw-r--r--services/tests/servicestests/src/com/android/server/devicepolicy/DevicePolicyManagerTest.java104
-rw-r--r--services/tests/servicestests/src/com/android/server/devicepolicy/DpmMockContext.java24
-rw-r--r--services/tests/servicestests/src/com/android/server/retaildemo/PreloadAppsInstallerTest.java6
-rw-r--r--services/usage/java/com/android/server/usage/UserUsageStatsService.java1
-rw-r--r--test-runner/src/android/test/mock/MockPackageManager.java7
-rw-r--r--tests/net/java/com/android/server/connectivity/IpConnectivityEventBuilderTest.java22
-rw-r--r--tests/net/java/com/android/server/connectivity/IpConnectivityMetricsTest.java14
-rw-r--r--tests/net/java/com/android/server/connectivity/NetdEventListenerServiceTest.java2
-rw-r--r--tests/net/java/com/android/server/connectivity/NetworkNotificationManagerTest.java1
-rw-r--r--tools/layoutlib/bridge/src/android/animation/AnimationThread.java1
-rw-r--r--tools/layoutlib/bridge/src/android/content/res/Resources_Delegate.java47
-rw-r--r--tools/layoutlib/bridge/src/android/graphics/BidiRenderer.java11
-rw-r--r--tools/layoutlib/bridge/src/android/graphics/Canvas_Delegate.java27
-rw-r--r--tools/layoutlib/bridge/src/android/graphics/FontFamily_Delegate.java24
-rw-r--r--tools/layoutlib/bridge/src/android/graphics/Typeface_Delegate.java3
-rw-r--r--tools/layoutlib/bridge/src/android/view/PointerIcon_Delegate.java33
-rw-r--r--tools/layoutlib/bridge/src/com/android/layoutlib/bridge/BridgeRenderSession.java17
-rw-r--r--tools/layoutlib/bridge/src/com/android/layoutlib/bridge/android/BridgeContext.java72
-rw-r--r--tools/layoutlib/bridge/src/com/android/layoutlib/bridge/android/BridgePackageManager.java5
-rw-r--r--tools/layoutlib/bridge/src/com/android/layoutlib/bridge/android/view/WindowManagerImpl.java4
-rw-r--r--tools/layoutlib/bridge/src/com/android/layoutlib/bridge/impl/RenderSessionImpl.java66
-rw-r--r--tools/layoutlib/bridge/src/libcore/util/NativeAllocationRegistry_Delegate.java12
-rw-r--r--tools/layoutlib/bridge/tests/res/testApp/MyApplication/golden/simple_activity-old-theme.pngbin0 -> 1749 bytes
-rw-r--r--tools/layoutlib/bridge/tests/res/testApp/MyApplication/src/main/res/layout/allwidgets.xml3
-rw-r--r--tools/layoutlib/bridge/tests/res/testApp/MyApplication/src/main/res/values/arrays.xml1
-rw-r--r--tools/layoutlib/bridge/tests/res/testApp/MyApplication/src/main/res/values/styles.xml1
-rw-r--r--tools/layoutlib/bridge/tests/src/com/android/layoutlib/bridge/intensive/Main.java72
-rw-r--r--tools/layoutlib/create/src/com/android/tools/layoutlib/create/CreateInfo.java6
109 files changed, 2596 insertions, 3634 deletions
diff --git a/api/current.txt b/api/current.txt
index ec0faf495053..6da09e35fe5e 100644
--- a/api/current.txt
+++ b/api/current.txt
@@ -1234,6 +1234,7 @@ package android {
field public static final int summaryOff = 16843248; // 0x10101f0
field public static final int summaryOn = 16843247; // 0x10101ef
field public static final int supportsAssist = 16844016; // 0x10104f0
+ field public static final int supportsDismissingWindow = 16844104; // 0x1010548
field public static final int supportsLaunchVoiceAssistFromKeyguard = 16844017; // 0x10104f1
field public static final int supportsLocalInteraction = 16844047; // 0x101050f
field public static final int supportsPictureInPicture = 16844023; // 0x10104f7
@@ -6024,10 +6025,14 @@ package android.app.admin {
method public void onLockTaskModeEntering(android.content.Context, android.content.Intent, java.lang.String);
method public void onLockTaskModeExiting(android.content.Context, android.content.Intent);
method public void onNetworkLogsAvailable(android.content.Context, android.content.Intent, long, int);
- method public void onPasswordChanged(android.content.Context, android.content.Intent);
- method public void onPasswordExpiring(android.content.Context, android.content.Intent);
- method public void onPasswordFailed(android.content.Context, android.content.Intent);
- method public void onPasswordSucceeded(android.content.Context, android.content.Intent);
+ method public deprecated void onPasswordChanged(android.content.Context, android.content.Intent);
+ method public void onPasswordChanged(android.content.Context, android.content.Intent, android.os.UserHandle);
+ method public deprecated void onPasswordExpiring(android.content.Context, android.content.Intent);
+ method public void onPasswordExpiring(android.content.Context, android.content.Intent, android.os.UserHandle);
+ method public deprecated void onPasswordFailed(android.content.Context, android.content.Intent);
+ method public void onPasswordFailed(android.content.Context, android.content.Intent, android.os.UserHandle);
+ method public deprecated void onPasswordSucceeded(android.content.Context, android.content.Intent);
+ method public void onPasswordSucceeded(android.content.Context, android.content.Intent, android.os.UserHandle);
method public void onProfileProvisioningComplete(android.content.Context, android.content.Intent);
method public deprecated void onReadyForUserInitialization(android.content.Context, android.content.Intent);
method public void onReceive(android.content.Context, android.content.Intent);
@@ -8955,6 +8960,7 @@ package android.content {
field public static final java.lang.String EXTRA_CHOOSER_TARGETS = "android.intent.extra.CHOOSER_TARGETS";
field public static final java.lang.String EXTRA_CHOSEN_COMPONENT = "android.intent.extra.CHOSEN_COMPONENT";
field public static final java.lang.String EXTRA_CHOSEN_COMPONENT_INTENT_SENDER = "android.intent.extra.CHOSEN_COMPONENT_INTENT_SENDER";
+ field public static final java.lang.String EXTRA_CONTENT_ANNOTATIONS = "android.intent.extra.CONTENT_ANNOTATIONS";
field public static final java.lang.String EXTRA_DATA_REMOVED = "android.intent.extra.DATA_REMOVED";
field public static final java.lang.String EXTRA_DOCK_STATE = "android.intent.extra.DOCK_STATE";
field public static final int EXTRA_DOCK_STATE_CAR = 2; // 0x2
@@ -9944,6 +9950,7 @@ package android.content.pm {
method public void setAppLabel(java.lang.CharSequence);
method public void setAppPackageName(java.lang.String);
method public void setInstallLocation(int);
+ method public void setInstallReason(int);
method public void setOriginatingUid(int);
method public void setOriginatingUri(android.net.Uri);
method public void setReferrerUri(android.net.Uri);
@@ -10177,6 +10184,8 @@ package android.content.pm {
field public static final int GET_SIGNATURES = 64; // 0x40
field public static final deprecated int GET_UNINSTALLED_PACKAGES = 8192; // 0x2000
field public static final int GET_URI_PERMISSION_PATTERNS = 2048; // 0x800
+ field public static final int INSTALL_REASON_POLICY = 1; // 0x1
+ field public static final int INSTALL_REASON_UNKNOWN = 0; // 0x0
field public static final int MATCH_ALL = 131072; // 0x20000
field public static final int MATCH_DEFAULT_ONLY = 65536; // 0x10000
field public static final int MATCH_DIRECT_BOOT_AWARE = 524288; // 0x80000
diff --git a/api/system-current.txt b/api/system-current.txt
index 882727969a1d..4109a1fbdddd 100644
--- a/api/system-current.txt
+++ b/api/system-current.txt
@@ -1347,6 +1347,7 @@ package android {
field public static final int summaryOff = 16843248; // 0x10101f0
field public static final int summaryOn = 16843247; // 0x10101ef
field public static final int supportsAssist = 16844016; // 0x10104f0
+ field public static final int supportsDismissingWindow = 16844104; // 0x1010548
field public static final int supportsLaunchVoiceAssistFromKeyguard = 16844017; // 0x10104f1
field public static final int supportsLocalInteraction = 16844047; // 0x101050f
field public static final int supportsPictureInPicture = 16844023; // 0x10104f7
@@ -6203,10 +6204,14 @@ package android.app.admin {
method public void onLockTaskModeEntering(android.content.Context, android.content.Intent, java.lang.String);
method public void onLockTaskModeExiting(android.content.Context, android.content.Intent);
method public void onNetworkLogsAvailable(android.content.Context, android.content.Intent, long, int);
- method public void onPasswordChanged(android.content.Context, android.content.Intent);
- method public void onPasswordExpiring(android.content.Context, android.content.Intent);
- method public void onPasswordFailed(android.content.Context, android.content.Intent);
- method public void onPasswordSucceeded(android.content.Context, android.content.Intent);
+ method public deprecated void onPasswordChanged(android.content.Context, android.content.Intent);
+ method public void onPasswordChanged(android.content.Context, android.content.Intent, android.os.UserHandle);
+ method public deprecated void onPasswordExpiring(android.content.Context, android.content.Intent);
+ method public void onPasswordExpiring(android.content.Context, android.content.Intent, android.os.UserHandle);
+ method public deprecated void onPasswordFailed(android.content.Context, android.content.Intent);
+ method public void onPasswordFailed(android.content.Context, android.content.Intent, android.os.UserHandle);
+ method public deprecated void onPasswordSucceeded(android.content.Context, android.content.Intent);
+ method public void onPasswordSucceeded(android.content.Context, android.content.Intent, android.os.UserHandle);
method public void onProfileProvisioningComplete(android.content.Context, android.content.Intent);
method public deprecated void onReadyForUserInitialization(android.content.Context, android.content.Intent);
method public void onReceive(android.content.Context, android.content.Intent);
@@ -9331,6 +9336,7 @@ package android.content {
field public static final java.lang.String EXTRA_CHOOSER_TARGETS = "android.intent.extra.CHOOSER_TARGETS";
field public static final java.lang.String EXTRA_CHOSEN_COMPONENT = "android.intent.extra.CHOSEN_COMPONENT";
field public static final java.lang.String EXTRA_CHOSEN_COMPONENT_INTENT_SENDER = "android.intent.extra.CHOSEN_COMPONENT_INTENT_SENDER";
+ field public static final java.lang.String EXTRA_CONTENT_ANNOTATIONS = "android.intent.extra.CONTENT_ANNOTATIONS";
field public static final java.lang.String EXTRA_DATA_REMOVED = "android.intent.extra.DATA_REMOVED";
field public static final java.lang.String EXTRA_DOCK_STATE = "android.intent.extra.DOCK_STATE";
field public static final int EXTRA_DOCK_STATE_CAR = 2; // 0x2
@@ -10365,6 +10371,7 @@ package android.content.pm {
method public void setGrantedRuntimePermissions(java.lang.String[]);
method public void setInstallAsInstantApp(boolean);
method public void setInstallLocation(int);
+ method public void setInstallReason(int);
method public void setOriginatingUid(int);
method public void setOriginatingUri(android.net.Uri);
method public void setReferrerUri(android.net.Uri);
@@ -10653,6 +10660,8 @@ package android.content.pm {
field public static final int INSTALL_PARSE_FAILED_NOT_APK = -100; // 0xffffff9c
field public static final int INSTALL_PARSE_FAILED_NO_CERTIFICATES = -103; // 0xffffff99
field public static final int INSTALL_PARSE_FAILED_UNEXPECTED_EXCEPTION = -102; // 0xffffff9a
+ field public static final int INSTALL_REASON_POLICY = 1; // 0x1
+ field public static final int INSTALL_REASON_UNKNOWN = 0; // 0x0
field public static final int INSTALL_SUCCEEDED = 1; // 0x1
field public static final int INTENT_FILTER_VERIFICATION_FAILURE = -1; // 0xffffffff
field public static final int INTENT_FILTER_VERIFICATION_SUCCESS = 1; // 0x1
@@ -21841,6 +21850,7 @@ package android.media {
method public android.media.AudioAttributes getAudioAttributes();
method public int getClientPid();
method public int getClientUid();
+ method public int getPlayerInterfaceId();
method public int getPlayerState();
method public int getPlayerType();
method public void writeToParcel(android.os.Parcel, int);
diff --git a/api/test-current.txt b/api/test-current.txt
index dcc693ad989d..714cac9fc559 100644
--- a/api/test-current.txt
+++ b/api/test-current.txt
@@ -1234,6 +1234,7 @@ package android {
field public static final int summaryOff = 16843248; // 0x10101f0
field public static final int summaryOn = 16843247; // 0x10101ef
field public static final int supportsAssist = 16844016; // 0x10104f0
+ field public static final int supportsDismissingWindow = 16844104; // 0x1010548
field public static final int supportsLaunchVoiceAssistFromKeyguard = 16844017; // 0x10104f1
field public static final int supportsLocalInteraction = 16844047; // 0x101050f
field public static final int supportsPictureInPicture = 16844023; // 0x10104f7
@@ -6041,10 +6042,14 @@ package android.app.admin {
method public void onLockTaskModeEntering(android.content.Context, android.content.Intent, java.lang.String);
method public void onLockTaskModeExiting(android.content.Context, android.content.Intent);
method public void onNetworkLogsAvailable(android.content.Context, android.content.Intent, long, int);
- method public void onPasswordChanged(android.content.Context, android.content.Intent);
- method public void onPasswordExpiring(android.content.Context, android.content.Intent);
- method public void onPasswordFailed(android.content.Context, android.content.Intent);
- method public void onPasswordSucceeded(android.content.Context, android.content.Intent);
+ method public deprecated void onPasswordChanged(android.content.Context, android.content.Intent);
+ method public void onPasswordChanged(android.content.Context, android.content.Intent, android.os.UserHandle);
+ method public deprecated void onPasswordExpiring(android.content.Context, android.content.Intent);
+ method public void onPasswordExpiring(android.content.Context, android.content.Intent, android.os.UserHandle);
+ method public deprecated void onPasswordFailed(android.content.Context, android.content.Intent);
+ method public void onPasswordFailed(android.content.Context, android.content.Intent, android.os.UserHandle);
+ method public deprecated void onPasswordSucceeded(android.content.Context, android.content.Intent);
+ method public void onPasswordSucceeded(android.content.Context, android.content.Intent, android.os.UserHandle);
method public void onProfileProvisioningComplete(android.content.Context, android.content.Intent);
method public deprecated void onReadyForUserInitialization(android.content.Context, android.content.Intent);
method public void onReceive(android.content.Context, android.content.Intent);
@@ -8980,6 +8985,7 @@ package android.content {
field public static final java.lang.String EXTRA_CHOOSER_TARGETS = "android.intent.extra.CHOOSER_TARGETS";
field public static final java.lang.String EXTRA_CHOSEN_COMPONENT = "android.intent.extra.CHOSEN_COMPONENT";
field public static final java.lang.String EXTRA_CHOSEN_COMPONENT_INTENT_SENDER = "android.intent.extra.CHOSEN_COMPONENT_INTENT_SENDER";
+ field public static final java.lang.String EXTRA_CONTENT_ANNOTATIONS = "android.intent.extra.CONTENT_ANNOTATIONS";
field public static final java.lang.String EXTRA_DATA_REMOVED = "android.intent.extra.DATA_REMOVED";
field public static final java.lang.String EXTRA_DOCK_STATE = "android.intent.extra.DOCK_STATE";
field public static final int EXTRA_DOCK_STATE_CAR = 2; // 0x2
@@ -9972,6 +9978,7 @@ package android.content.pm {
method public void setAppLabel(java.lang.CharSequence);
method public void setAppPackageName(java.lang.String);
method public void setInstallLocation(int);
+ method public void setInstallReason(int);
method public void setOriginatingUid(int);
method public void setOriginatingUri(android.net.Uri);
method public void setReferrerUri(android.net.Uri);
@@ -10044,6 +10051,7 @@ package android.content.pm {
method public abstract android.graphics.drawable.Drawable getDefaultActivityIcon();
method public abstract java.lang.String getDefaultBrowserPackageNameAsUser(int);
method public abstract android.graphics.drawable.Drawable getDrawable(java.lang.String, int, android.content.pm.ApplicationInfo);
+ method public abstract int getInstallReason(java.lang.String, android.os.UserHandle);
method public abstract java.util.List<android.content.pm.ApplicationInfo> getInstalledApplications(int);
method public abstract java.util.List<android.content.pm.PackageInfo> getInstalledPackages(int);
method public abstract java.lang.String getInstallerPackageName(java.lang.String);
@@ -10206,6 +10214,8 @@ package android.content.pm {
field public static final int GET_SIGNATURES = 64; // 0x40
field public static final deprecated int GET_UNINSTALLED_PACKAGES = 8192; // 0x2000
field public static final int GET_URI_PERMISSION_PATTERNS = 2048; // 0x800
+ field public static final int INSTALL_REASON_POLICY = 1; // 0x1
+ field public static final int INSTALL_REASON_UNKNOWN = 0; // 0x0
field public static final int MATCH_ALL = 131072; // 0x20000
field public static final int MATCH_DEFAULT_ONLY = 65536; // 0x10000
field public static final int MATCH_DIRECT_BOOT_AWARE = 524288; // 0x80000
@@ -39123,6 +39133,7 @@ package android.test.mock {
method public android.graphics.drawable.Drawable getDefaultActivityIcon();
method public java.lang.String getDefaultBrowserPackageNameAsUser(int);
method public android.graphics.drawable.Drawable getDrawable(java.lang.String, int, android.content.pm.ApplicationInfo);
+ method public int getInstallReason(java.lang.String, android.os.UserHandle);
method public java.util.List<android.content.pm.ApplicationInfo> getInstalledApplications(int);
method public java.util.List<android.content.pm.PackageInfo> getInstalledPackages(int);
method public java.lang.String getInstallerPackageName(java.lang.String);
diff --git a/compiled-classes-phone b/compiled-classes-phone
index 7239a7f664c8..ebc54f2128e9 100644
--- a/compiled-classes-phone
+++ b/compiled-classes-phone
@@ -5998,10 +5998,6 @@ com.android.internal.widget.LockPatternUtils$StrongAuthTracker$H
com.android.internal.widget.MediaNotificationView
com.android.internal.widget.NotificationActionListLayout
com.android.internal.widget.NotificationActionListLayout$-void__clinit___LambdaImpl0
-com.android.internal.widget.PasswordEntryKeyboard
-com.android.internal.widget.PasswordEntryKeyboard$LatinKey
-com.android.internal.widget.PasswordEntryKeyboardHelper
-com.android.internal.widget.PasswordEntryKeyboardView
com.android.internal.widget.PreferenceImageView
com.android.internal.widget.ResolverDrawerLayout
com.android.internal.widget.ResolverDrawerLayout$1
diff --git a/core/java/android/app/ActivityThread.java b/core/java/android/app/ActivityThread.java
index 45d325a859fa..0b3ae3a989d5 100644
--- a/core/java/android/app/ActivityThread.java
+++ b/core/java/android/app/ActivityThread.java
@@ -62,6 +62,7 @@ import android.os.Bundle;
import android.os.Debug;
import android.os.DropBoxManager;
import android.os.Environment;
+import android.os.GraphicsEnvironment;
import android.os.Handler;
import android.os.IBinder;
import android.os.LocaleList;
@@ -5106,7 +5107,7 @@ public final class ActivityThread {
WindowManagerGlobal.getInstance().trimMemory(level);
}
- private void setupGraphicsSupport(LoadedApk info, File cacheDir) {
+ private void setupGraphicsSupport(Context context, File cacheDir) {
if (Process.isIsolated()) {
// Isolated processes aren't going to do UI.
return;
@@ -5119,6 +5120,7 @@ public final class ActivityThread {
if (packages != null) {
ThreadedRenderer.setupDiskCache(cacheDir);
RenderScriptCacheDir.setupDiskCache(cacheDir);
+ GraphicsEnvironment.setupGraphicsEnvironment(context);
}
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
@@ -5422,7 +5424,7 @@ public final class ActivityThread {
final Context deviceContext = appContext.createDeviceProtectedStorageContext();
final File codeCacheDir = deviceContext.getCodeCacheDir();
if (codeCacheDir != null) {
- setupGraphicsSupport(data.info, codeCacheDir);
+ setupGraphicsSupport(appContext, codeCacheDir);
} else {
Log.e(TAG, "Unable to setupGraphicsSupport due to missing code-cache directory");
}
diff --git a/core/java/android/app/ApplicationPackageManager.java b/core/java/android/app/ApplicationPackageManager.java
index aedcdce980a2..f3185a8d3a6b 100644
--- a/core/java/android/app/ApplicationPackageManager.java
+++ b/core/java/android/app/ApplicationPackageManager.java
@@ -1635,7 +1635,8 @@ public class ApplicationPackageManager extends PackageManager {
public int installExistingPackageAsUser(String packageName, int userId)
throws NameNotFoundException {
try {
- int res = mPM.installExistingPackageAsUser(packageName, userId);
+ int res = mPM.installExistingPackageAsUser(packageName, userId,
+ PackageManager.INSTALL_REASON_UNKNOWN);
if (res == INSTALL_FAILED_INVALID_URI) {
throw new NameNotFoundException("Package " + packageName + " doesn't exist");
}
@@ -2411,6 +2412,18 @@ public class ApplicationPackageManager extends PackageManager {
return getUserManager().isManagedProfile(userId);
}
+ /**
+ * @hide
+ */
+ @Override
+ public int getInstallReason(String packageName, UserHandle user) {
+ try {
+ return mPM.getInstallReason(packageName, user.getIdentifier());
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
+ }
+ }
+
/** {@hide} */
private static class MoveCallbackDelegate extends IPackageMoveObserver.Stub implements
Handler.Callback {
diff --git a/core/java/android/app/admin/DeviceAdminReceiver.java b/core/java/android/app/admin/DeviceAdminReceiver.java
index aae80ed65035..a248bce2a6ca 100644
--- a/core/java/android/app/admin/DeviceAdminReceiver.java
+++ b/core/java/android/app/admin/DeviceAdminReceiver.java
@@ -28,6 +28,7 @@ import android.content.Context;
import android.content.Intent;
import android.net.Uri;
import android.os.Bundle;
+import android.os.Process;
import android.os.UserHandle;
import android.security.KeyChain;
@@ -123,7 +124,7 @@ public class DeviceAdminReceiver extends BroadcastReceiver {
* of the new password with {@link DevicePolicyManager#isActivePasswordSufficient()
* DevicePolicyManager.isActivePasswordSufficient()}.
* You will generally
- * handle this in {@link DeviceAdminReceiver#onPasswordChanged}.
+ * handle this in {@link DeviceAdminReceiver#onPasswordChanged(Context, Intent, UserHandle)}.
*
* <p>The calling device admin must have requested
* {@link DeviceAdminInfo#USES_POLICY_LIMIT_PASSWORD} to receive
@@ -139,7 +140,7 @@ public class DeviceAdminReceiver extends BroadcastReceiver {
* number of failed password attempts there have been with
* {@link DevicePolicyManager#getCurrentFailedPasswordAttempts
* DevicePolicyManager.getCurrentFailedPasswordAttempts()}. You will generally
- * handle this in {@link DeviceAdminReceiver#onPasswordFailed}.
+ * handle this in {@link DeviceAdminReceiver#onPasswordFailed(Context, Intent, UserHandle)}.
*
* <p>The calling device admin must have requested
* {@link DeviceAdminInfo#USES_POLICY_WATCH_LOGIN} to receive
@@ -152,7 +153,7 @@ public class DeviceAdminReceiver extends BroadcastReceiver {
/**
* Action sent to a device administrator when the user has successfully entered their device
* or profile challenge password, after failing one or more times. You will generally
- * handle this in {@link DeviceAdminReceiver#onPasswordSucceeded}.
+ * handle this in {@link DeviceAdminReceiver#onPasswordSucceeded(Context, Intent, UserHandle)}.
*
* <p>The calling device admin must have requested
* {@link DeviceAdminInfo#USES_POLICY_WATCH_LOGIN} to receive
@@ -165,7 +166,7 @@ public class DeviceAdminReceiver extends BroadcastReceiver {
/**
* Action periodically sent to a device administrator when the device or profile challenge
* password is expiring. You will generally
- * handle this in {@link DeviceAdminReceiver#onPasswordExpiring}.
+ * handle this in {@link DeviceAdminReceiver#onPasswordExpiring(Context, Intent, UserHandle)}.
*
* <p>The calling device admin must have requested
* {@link DeviceAdminInfo#USES_POLICY_EXPIRE_PASSWORD} to receive
@@ -497,33 +498,90 @@ public class DeviceAdminReceiver extends BroadcastReceiver {
* to retrieve the active password characteristics.
* @param context The running context as per {@link #onReceive}.
* @param intent The received intent as per {@link #onReceive}.
+ *
+ * @deprecated From {@link android.os.Build.VERSION_CODES#O}, use
+ * {@link #onPasswordChanged(Context, Intent, UserHandle)} instead.
*/
+ @Deprecated
public void onPasswordChanged(Context context, Intent intent) {
}
/**
+ * Called after the user has changed their device or profile challenge password, as a result of
+ * receiving {@link #ACTION_PASSWORD_CHANGED}. At this point you
+ * can use {@link DevicePolicyManager#getPasswordQuality(android.content.ComponentName)}
+ * to retrieve the active password characteristics.
+ * @param context The running context as per {@link #onReceive}.
+ * @param intent The received intent as per {@link #onReceive}.
+ * @param user The user or profile for whom the password changed. To see whether this
+ * user is the current profile or a parent user, check for equality with
+ * {@link Process#myUserHandle}.
+ */
+ public void onPasswordChanged(Context context, Intent intent, UserHandle user) {
+ onPasswordChanged(context, intent);
+ }
+
+ /**
* Called after the user has failed at entering their device or profile challenge password,
* as a result of receiving {@link #ACTION_PASSWORD_FAILED}. At this point you can use
* {@link DevicePolicyManager#getCurrentFailedPasswordAttempts()} to retrieve the number of
* failed password attempts.
* @param context The running context as per {@link #onReceive}.
* @param intent The received intent as per {@link #onReceive}.
+ *
+ * @deprecated From {@link android.os.Build.VERSION_CODES#O}, use
+ * {@link #onPasswordFailed(Context, Intent, UserHandle)} instead.
*/
+ @Deprecated
public void onPasswordFailed(Context context, Intent intent) {
}
/**
+ * Called after the user has failed at entering their device or profile challenge password,
+ * as a result of receiving {@link #ACTION_PASSWORD_FAILED}. At this point you can use
+ * {@link DevicePolicyManager#getCurrentFailedPasswordAttempts()} to retrieve the number of
+ * failed password attempts.
+ * @param context The running context as per {@link #onReceive}.
+ * @param intent The received intent as per {@link #onReceive}.
+ * @param user The user or profile for whom the password check failed. To see whether this
+ * user is the current profile or a parent user, check for equality with
+ * {@link Process#myUserHandle}.
+ */
+ public void onPasswordFailed(Context context, Intent intent, UserHandle user) {
+ onPasswordFailed(context, intent);
+ }
+
+ /**
* Called after the user has succeeded at entering their device or profile challenge password,
* as a result of receiving {@link #ACTION_PASSWORD_SUCCEEDED}. This will
* only be received the first time they succeed after having previously
* failed.
* @param context The running context as per {@link #onReceive}.
* @param intent The received intent as per {@link #onReceive}.
+ *
+ * @deprecated From {@link android.os.Build.VERSION_CODES#O}, use
+ * {@link #onPasswordSucceeded(Context, Intent, UserHandle)} instead.
*/
+ @Deprecated
public void onPasswordSucceeded(Context context, Intent intent) {
}
/**
+ * Called after the user has succeeded at entering their device or profile challenge password,
+ * as a result of receiving {@link #ACTION_PASSWORD_SUCCEEDED}. This will
+ * only be received the first time they succeed after having previously
+ * failed.
+ * @param context The running context as per {@link #onReceive}.
+ * @param intent The received intent as per {@link #onReceive}.
+ * @param user The user of profile for whom the password check succeeded. To see whether this
+ * user is the current profile or a parent user, check for equality with
+ * {@link Process#myUserHandle}.
+ */
+ public void onPasswordSucceeded(Context context, Intent intent, UserHandle user) {
+ onPasswordSucceeded(context, intent);
+ }
+
+ /**
* Called periodically when the device or profile challenge password is about to expire
* or has expired. It will typically be called at these times: on device boot, once per day
* before the password expires, and at the time when the password expires.
@@ -540,11 +598,40 @@ public class DeviceAdminReceiver extends BroadcastReceiver {
*
* @param context The running context as per {@link #onReceive}.
* @param intent The received intent as per {@link #onReceive}.
+ *
+ * @deprecated From {@link android.os.Build.VERSION_CODES#O}, use
+ * {@link #onPasswordExpiring(Context, Intent, UserHandle)} instead.
*/
+ @Deprecated
public void onPasswordExpiring(Context context, Intent intent) {
}
/**
+ * Called periodically when the device or profile challenge password is about to expire
+ * or has expired. It will typically be called at these times: on device boot, once per day
+ * before the password expires, and at the time when the password expires.
+ *
+ * <p>If the password is not updated by the user, this method will continue to be called
+ * once per day until the password is changed or the device admin disables password expiration.
+ *
+ * <p>The admin will typically post a notification requesting the user to change their password
+ * in response to this call. The actual password expiration time can be obtained by calling
+ * {@link DevicePolicyManager#getPasswordExpiration(ComponentName) }
+ *
+ * <p>The admin should be sure to take down any notifications it posted in response to this call
+ * when it receives {@link DeviceAdminReceiver#onPasswordChanged(Context, Intent, UserHandle) }.
+ *
+ * @param context The running context as per {@link #onReceive}.
+ * @param intent The received intent as per {@link #onReceive}.
+ * @param user The user or profile for whom the password is expiring. To see whether this
+ * user is the current profile or a parent user, check for equality with
+ * {@link Process#myUserHandle}.
+ */
+ public void onPasswordExpiring(Context context, Intent intent, UserHandle user) {
+ onPasswordExpiring(context, intent);
+ }
+
+ /**
* Called when provisioning of a managed profile or managed device has completed successfully.
*
* <p> As a prerequisite for the execution of this callback the {@link DeviceAdminReceiver} has
@@ -681,6 +768,10 @@ public class DeviceAdminReceiver extends BroadcastReceiver {
/**
* Called when a new batch of security logs can be retrieved.
*
+ * <p>If a secondary user or profile is created, this callback won't be received until all users
+ * become affiliated again (even if security logging is enabled).
+ * See {@link DevicePolicyManager#setAffiliationIds}
+ *
* <p>This callback is only applicable to device owners.
*
* @param context The running context as per {@link #onReceive}.
@@ -695,13 +786,18 @@ public class DeviceAdminReceiver extends BroadcastReceiver {
* ever be called when network logging is enabled. The logs can only be retrieved while network
* logging is enabled.
*
+ * <p>If a secondary user or profile is created, this callback won't be received until all users
+ * become affiliated again (even if network logging is enabled). It will also no longer be
+ * possible to retrieve the network logs batch with the most recent {@code batchToken} provided
+ * by this callback. See {@link DevicePolicyManager#setAffiliationIds}.
+ *
* <p>This callback is only applicable to device owners.
*
* @param context The running context as per {@link #onReceive}.
* @param intent The received intent as per {@link #onReceive}.
* @param batchToken The token representing the current batch of network logs.
* @param networkLogsCount The total count of events in the current batch of network logs.
- * @see DevicePolicyManager#retrieveNetworkLogs(ComponentName)
+ * @see DevicePolicyManager#retrieveNetworkLogs
*/
public void onNetworkLogsAvailable(Context context, Intent intent, long batchToken,
int networkLogsCount) {
@@ -741,11 +837,11 @@ public class DeviceAdminReceiver extends BroadcastReceiver {
String action = intent.getAction();
if (ACTION_PASSWORD_CHANGED.equals(action)) {
- onPasswordChanged(context, intent);
+ onPasswordChanged(context, intent, intent.getParcelableExtra(Intent.EXTRA_USER));
} else if (ACTION_PASSWORD_FAILED.equals(action)) {
- onPasswordFailed(context, intent);
+ onPasswordFailed(context, intent, intent.getParcelableExtra(Intent.EXTRA_USER));
} else if (ACTION_PASSWORD_SUCCEEDED.equals(action)) {
- onPasswordSucceeded(context, intent);
+ onPasswordSucceeded(context, intent, intent.getParcelableExtra(Intent.EXTRA_USER));
} else if (ACTION_DEVICE_ADMIN_ENABLED.equals(action)) {
onEnabled(context, intent);
} else if (ACTION_DEVICE_ADMIN_DISABLE_REQUESTED.equals(action)) {
@@ -757,7 +853,7 @@ public class DeviceAdminReceiver extends BroadcastReceiver {
} else if (ACTION_DEVICE_ADMIN_DISABLED.equals(action)) {
onDisabled(context, intent);
} else if (ACTION_PASSWORD_EXPIRING.equals(action)) {
- onPasswordExpiring(context, intent);
+ onPasswordExpiring(context, intent, intent.getParcelableExtra(Intent.EXTRA_USER));
} else if (ACTION_PROFILE_PROVISIONING_COMPLETE.equals(action)) {
onProfileProvisioningComplete(context, intent);
} else if (ACTION_CHOOSE_PRIVATE_KEY_ALIAS.equals(action)) {
diff --git a/core/java/android/app/admin/DevicePolicyManager.java b/core/java/android/app/admin/DevicePolicyManager.java
index 074326fd3531..c95e0113e801 100644
--- a/core/java/android/app/admin/DevicePolicyManager.java
+++ b/core/java/android/app/admin/DevicePolicyManager.java
@@ -3651,15 +3651,16 @@ public class DevicePolicyManager {
/**
* Called by a device owner to request a bugreport.
* <p>
- * There must be only one user on the device, managed by the device owner. Otherwise a
- * {@link SecurityException} will be thrown.
+ * If the device contains secondary users or profiles, they must be affiliated with the device
+ * owner user. Otherwise a {@link SecurityException} will be thrown. See
+ * {@link #setAffiliationIds}.
*
* @param admin Which {@link DeviceAdminReceiver} this request is associated with.
* @return {@code true} if the bugreport collection started successfully, or {@code false} if it
* wasn't triggered because a previous bugreport operation is still active (either the
* bugreport is still running or waiting for the user to share or decline)
- * @throws SecurityException if {@code admin} is not a device owner, or if there are users other
- * than the one managed by the device owner.
+ * @throws SecurityException if {@code admin} is not a device owner, or there is at least one
+ * profile or secondary user that is not affiliated with the device owner user.
*/
public boolean requestBugreport(@NonNull ComponentName admin) {
throwIfParentInstance("requestBugreport");
@@ -6631,14 +6632,16 @@ public class DevicePolicyManager {
}
/**
- * Called by device owner to control the security logging feature. Logging can only be
- * enabled on single user devices where the sole user is managed by the device owner.
+ * Called by device owner to control the security logging feature.
*
* <p> Security logs contain various information intended for security auditing purposes.
* See {@link SecurityEvent} for details.
*
- * <p>There must be only one user on the device, managed by the device owner.
- * Otherwise a {@link SecurityException} will be thrown.
+ * <p><strong>Note:</strong> The device owner won't be able to retrieve security logs if there
+ * are unaffiliated secondary users or profiles on the device, regardless of whether the
+ * feature is enabled. Logs will be discarded if the internal buffer fills up while waiting for
+ * all users to become affiliated. Therefore it's recommended that affiliation ids are set for
+ * new users as soon as possible after provisioning via {@link #setAffiliationIds}.
*
* @param admin Which device owner this request is associated with.
* @param enabled whether security logging should be enabled or not.
@@ -6680,13 +6683,16 @@ public class DevicePolicyManager {
* <p> Access to the logs is rate limited and it will only return new logs after the device
* owner has been notified via {@link DeviceAdminReceiver#onSecurityLogsAvailable}.
*
- * <p>There must be only one user on the device, managed by the device owner.
- * Otherwise a {@link SecurityException} will be thrown.
+ * <p>If there is any other user or profile on the device, it must be affiliated with the
+ * device owner. Otherwise a {@link SecurityException} will be thrown. See
+ * {@link #setAffiliationIds}
*
* @param admin Which device owner this request is associated with.
* @return the new batch of security logs which is a list of {@link SecurityEvent},
* or {@code null} if rate limitation is exceeded or if logging is currently disabled.
- * @throws SecurityException if {@code admin} is not a device owner.
+ * @throws SecurityException if {@code admin} is not a device owner, or there is at least one
+ * profile or secondary user that is not affiliated with the device owner user.
+ * @see DeviceAdminReceiver#onSecurityLogsAvailable
*/
public @Nullable List<SecurityEvent> retrieveSecurityLogs(@NonNull ComponentName admin) {
throwIfParentInstance("retrieveSecurityLogs");
@@ -6726,14 +6732,17 @@ public class DevicePolicyManager {
* will result in {@code null} being returned. The device logs are retrieved from a RAM region
* which is not guaranteed to be corruption-free during power cycles, as a result be cautious
* about data corruption when parsing. </strong>
- * <p>
- * There must be only one user on the device, managed by the device owner. Otherwise a
- * {@link SecurityException} will be thrown.
+ *
+ * <p>If there is any other user or profile on the device, it must be affiliated with the
+ * device owner. Otherwise a {@link SecurityException} will be thrown. See
+ * {@link #setAffiliationIds}
*
* @param admin Which device owner this request is associated with.
* @return Device logs from before the latest reboot of the system, or {@code null} if this API
* is not supported on the device.
- * @throws SecurityException if {@code admin} is not a device owner.
+ * @throws SecurityException if {@code admin} is not a device owner, or there is at least one
+ * profile or secondary user that is not affiliated with the device owner user.
+ * @see #retrieveSecurityLogs
*/
public @Nullable List<SecurityEvent> retrievePreRebootSecurityLogs(
@NonNull ComponentName admin) {
@@ -6939,6 +6948,12 @@ public class DevicePolicyManager {
* Indicates the entity that controls the device or profile owner. Two users/profiles are
* affiliated if the set of ids set by their device or profile owners intersect.
*
+ * <p><strong>Note:</strong> Features that depend on user affiliation (such as security logging
+ * or {@link #bindDeviceAdminServiceAsUser}) won't be available when a secondary user or profile
+ * is created, until it becomes affiliated. Therefore it is recommended that the appropriate
+ * affiliation ids are set by its profile owner as soon as possible after the user/profile is
+ * created.
+ *
* @param admin Which profile or device owner this request is associated with.
* @param ids A list of opaque non-empty affiliation ids. Duplicate elements will be ignored.
*
@@ -7138,15 +7153,19 @@ public class DevicePolicyManager {
}
/**
- * Called by a device owner to control the network logging feature. Logging can only be
- * enabled on single user devices where the sole user is managed by the device owner. If a new
- * user is added on the device, logging is disabled.
+ * Called by a device owner to control the network logging feature.
*
* <p> Network logs contain DNS lookup and connect() library call events.
*
+ * <p><strong>Note:</strong> The device owner won't be able to retrieve network logs if there
+ * are unaffiliated secondary users or profiles on the device, regardless of whether the
+ * feature is enabled. Logs will be discarded if the internal buffer fills up while waiting for
+ * all users to become affiliated. Therefore it's recommended that affiliation ids are set for
+ * new users as soon as possible after provisioning via {@link #setAffiliationIds}.
+ *
* @param admin Which {@link DeviceAdminReceiver} this request is associated with.
* @param enabled whether network logging should be enabled or not.
- * @throws {@link SecurityException} if {@code admin} is not a device owner.
+ * @throws SecurityException if {@code admin} is not a device owner.
* @see #retrieveNetworkLogs
*/
public void setNetworkLoggingEnabled(@NonNull ComponentName admin, boolean enabled) {
@@ -7164,7 +7183,7 @@ public class DevicePolicyManager {
* @param admin Which {@link DeviceAdminReceiver} this request is associated with. Can only
* be {@code null} if the caller has MANAGE_USERS permission.
* @return {@code true} if network logging is enabled by device owner, {@code false} otherwise.
- * @throws {@link SecurityException} if {@code admin} is not a device owner and caller has
+ * @throws SecurityException if {@code admin} is not a device owner and caller has
* no MANAGE_USERS permission
*/
public boolean isNetworkLoggingEnabled(@Nullable ComponentName admin) {
@@ -7190,12 +7209,19 @@ public class DevicePolicyManager {
* after the device device owner has been notified via
* {@link DeviceAdminReceiver#onNetworkLogsAvailable}.
*
+ * <p>If a secondary user or profile is created, calling this method will throw a
+ * {@link SecurityException} until all users become affiliated again. It will also no longer be
+ * possible to retrieve the network logs batch with the most recent batchToken provided
+ * by {@link DeviceAdminReceiver#onNetworkLogsAvailable}. See
+ * {@link DevicePolicyManager#setAffiliationIds}.
+ *
* @param admin Which {@link DeviceAdminReceiver} this request is associated with.
* @param batchToken A token of the batch to retrieve
* @return A new batch of network logs which is a list of {@link NetworkEvent}. Returns
* {@code null} if the batch represented by batchToken is no longer available or if
* logging is disabled.
- * @throws {@link SecurityException} if {@code admin} is not a device owner.
+ * @throws SecurityException if {@code admin} is not a device owner, or there is at least one
+ * profile or secondary user that is not affiliated with the device owner user.
* @see DeviceAdminReceiver#onNetworkLogsAvailable
*/
public @Nullable List<NetworkEvent> retrieveNetworkLogs(@NonNull ComponentName admin,
diff --git a/core/java/android/content/Intent.java b/core/java/android/content/Intent.java
index b05ceaa8b723..d8358f9883e8 100644
--- a/core/java/android/content/Intent.java
+++ b/core/java/android/content/Intent.java
@@ -3843,6 +3843,52 @@ public class Intent implements Parcelable, Cloneable {
= "android.intent.extra.CHOOSER_REFINEMENT_INTENT_SENDER";
/**
+ * An {@code ArrayList} of {@code String} annotations describing content for
+ * {@link #ACTION_CHOOSER}.
+ *
+ * <p>If {@link #EXTRA_CONTENT_ANNOTATIONS} is present in an intent used to start a
+ * {@link #ACTION_CHOOSER} activity, the first three annotations will be used to rank apps.</p>
+ *
+ * <p>Annotations should describe the major components or topics of the content. It is up to
+ * apps initiating {@link #ACTION_CHOOSER} to learn and add annotations. Annotations should be
+ * learned in advance, e.g., when creating or saving content, to avoid increasing latency to
+ * start {@link #ACTION_CHOOSER}. Performance on customized annotations can suffer, if they are
+ * rarely used for {@link #ACTION_CHOOSER} in the past 14 days. Therefore, it is recommended to
+ * use the following annotations when applicable:</p>
+ * <ul>
+ * <li>"product": represents that the topic of the content is mainly about products, e.g.,
+ * health & beauty, and office supplies.</li>
+ * <li>"emotion": represents that the topic of the content is mainly about emotions, e.g.,
+ * happy, and sad.</li>
+ * <li>"person": represents that the topic of the content is mainly about persons, e.g.,
+ * face, finger, standing, and walking.</li>
+ * <li>"child": represents that the topic of the content is mainly about children, e.g.,
+ * child, and baby.</li>
+ * <li>"selfie": represents that the topic of the content is mainly about selfies.</li>
+ * <li>"crowd": represents that the topic of the content is mainly about crowds.</li>
+ * <li>"party": represents that the topic of the content is mainly about parties.</li>
+ * <li>"animal": represent that the topic of the content is mainly about animals.</li>
+ * <li>"plant": represents that the topic of the content is mainly about plants, e.g.,
+ * flowers.</li>
+ * <li>"vacation": represents that the topic of the content is mainly about vacations.</li>
+ * <li>"fashion": represents that the topic of the content is mainly about fashion, e.g.
+ * sunglasses, jewelry, handbags and clothing.</li>
+ * <li>"material": represents that the topic of the content is mainly about materials, e.g.,
+ * paper, and silk.</li>
+ * <li>"vehicle": represents that the topic of the content is mainly about vehicles, like
+ * cars, and boats.</li>
+ * <li>"document": represents that the topic of the content is mainly about documents, e.g.
+ * posters.</li>
+ * <li>"design": represents that the topic of the content is mainly about design, e.g. arts
+ * and designs of houses.</li>
+ * <li>"holiday": represents that the topic of the content is mainly about holidays, e.g.,
+ * Christmas and Thanksgiving.</li>
+ * </ul>
+ */
+ public static final String EXTRA_CONTENT_ANNOTATIONS
+ = "android.intent.extra.CONTENT_ANNOTATIONS";
+
+ /**
* A {@link ResultReceiver} used to return data back to the sender.
*
* <p>Used to complete an app-specific
diff --git a/core/java/android/content/pm/IPackageManager.aidl b/core/java/android/content/pm/IPackageManager.aidl
index eb0ca2ed10be..19cca8ed1f66 100644
--- a/core/java/android/content/pm/IPackageManager.aidl
+++ b/core/java/android/content/pm/IPackageManager.aidl
@@ -523,7 +523,7 @@ interface IPackageManager {
boolean setInstallLocation(int loc);
int getInstallLocation();
- int installExistingPackageAsUser(String packageName, int userId);
+ int installExistingPackageAsUser(String packageName, int userId, int installReason);
void verifyPendingInstall(int id, int verificationCode);
void extendVerificationTimeout(int id, int verificationCodeAtTimeout, long millisecondsToDelay);
@@ -584,4 +584,6 @@ interface IPackageManager {
boolean isPackageDeviceAdminOnAnyUser(String packageName);
List<String> getPreviousCodePaths(in String packageName);
+
+ int getInstallReason(String packageName, int userId);
}
diff --git a/core/java/android/content/pm/PackageInstaller.java b/core/java/android/content/pm/PackageInstaller.java
index 646bd3c8e78b..db3f63708be4 100644
--- a/core/java/android/content/pm/PackageInstaller.java
+++ b/core/java/android/content/pm/PackageInstaller.java
@@ -881,6 +881,8 @@ public class PackageInstaller {
/** {@hide} */
public int installLocation = PackageInfo.INSTALL_LOCATION_INTERNAL_ONLY;
/** {@hide} */
+ public int installReason = PackageManager.INSTALL_REASON_UNKNOWN;
+ /** {@hide} */
public long sizeBytes = -1;
/** {@hide} */
public String appPackageName;
@@ -919,6 +921,7 @@ public class PackageInstaller {
mode = source.readInt();
installFlags = source.readInt();
installLocation = source.readInt();
+ installReason = source.readInt();
sizeBytes = source.readLong();
appPackageName = source.readString();
appIcon = source.readParcelable(null);
@@ -1076,6 +1079,10 @@ public class PackageInstaller {
}
}
+ public void setInstallReason(int installReason) {
+ this.installReason = installReason;
+ }
+
/** {@hide} */
public void dump(IndentingPrintWriter pw) {
pw.printPair("mode", mode);
@@ -1104,6 +1111,7 @@ public class PackageInstaller {
dest.writeInt(mode);
dest.writeInt(installFlags);
dest.writeInt(installLocation);
+ dest.writeInt(installReason);
dest.writeLong(sizeBytes);
dest.writeString(appPackageName);
dest.writeParcelable(appIcon, flags);
diff --git a/core/java/android/content/pm/PackageManager.java b/core/java/android/content/pm/PackageManager.java
index 0cd67b793dc1..04e649cba092 100644
--- a/core/java/android/content/pm/PackageManager.java
+++ b/core/java/android/content/pm/PackageManager.java
@@ -740,6 +740,21 @@ public abstract class PackageManager {
*/
public static final int DONT_KILL_APP = 0x00000001;
+ /** @hide */
+ @IntDef({INSTALL_REASON_UNKNOWN, INSTALL_REASON_POLICY})
+ @Retention(RetentionPolicy.SOURCE)
+ public @interface InstallReason {}
+
+ /**
+ * Code indicating that the reason for installing this package is unknown.
+ */
+ public static final int INSTALL_REASON_UNKNOWN = 0;
+
+ /**
+ * Code indicating that this package was installed due to enterprise policy.
+ */
+ public static final int INSTALL_REASON_POLICY = 1;
+
/**
* Installation return code: this is passed to the
* {@link IPackageInstallObserver} on success.
@@ -5881,4 +5896,25 @@ public abstract class PackageManager {
}
}
}
+
+ /**
+ * Return the install reason that was recorded when a package was first installed for a specific
+ * user. Requesting the install reason for another user will require the permission
+ * INTERACT_ACROSS_USERS_FULL.
+ *
+ * @param packageName The package for which to retrieve the install reason
+ * @param user The user for whom to retrieve the install reason
+ *
+ * @return The install reason, currently one of {@code INSTALL_REASON_UNKNOWN} and
+ * {@code INSTALL_REASON_POLICY}. If the package is not installed for the given user,
+ * {@code INSTALL_REASON_UNKNOWN} is returned.
+ *
+ * @see #INSTALL_REASON_UNKNOWN
+ * @see #INSTALL_REASON_POLICY
+ *
+ * @hide
+ */
+ @TestApi
+ public abstract @InstallReason int getInstallReason(String packageName,
+ @NonNull UserHandle user);
}
diff --git a/core/java/android/content/pm/PackageParser.java b/core/java/android/content/pm/PackageParser.java
index a48762bd36fa..083e4cc6676b 100644
--- a/core/java/android/content/pm/PackageParser.java
+++ b/core/java/android/content/pm/PackageParser.java
@@ -950,8 +950,8 @@ public class PackageParser {
try {
final byte[] bytes = IoUtils.readFileAsByteArray(cacheFile.getAbsolutePath());
return fromCacheEntry(bytes);
- } catch (IOException ioe) {
- Slog.w(TAG, "Error reading package cache: ", ioe);
+ } catch (Exception e) {
+ Slog.w(TAG, "Error reading package cache: ", e);
// If something went wrong while reading the cache entry, delete the cache file
// so that we regenerate it the next time.
diff --git a/core/java/android/content/pm/PackageUserState.java b/core/java/android/content/pm/PackageUserState.java
index eb3f27570970..e19aa99599ef 100644
--- a/core/java/android/content/pm/PackageUserState.java
+++ b/core/java/android/content/pm/PackageUserState.java
@@ -48,6 +48,7 @@ public class PackageUserState {
public int domainVerificationStatus;
public int appLinkGeneration;
public int categoryHint = ApplicationInfo.CATEGORY_UNDEFINED;
+ public int installReason;
public ArraySet<String> disabledComponents;
public ArraySet<String> enabledComponents;
@@ -59,6 +60,7 @@ public class PackageUserState {
enabled = COMPONENT_ENABLED_STATE_DEFAULT;
domainVerificationStatus =
PackageManager.INTENT_FILTER_DOMAIN_VERIFICATION_STATUS_UNDEFINED;
+ installReason = PackageManager.INSTALL_REASON_UNKNOWN;
}
public PackageUserState(PackageUserState o) {
@@ -74,6 +76,7 @@ public class PackageUserState {
domainVerificationStatus = o.domainVerificationStatus;
appLinkGeneration = o.appLinkGeneration;
categoryHint = o.categoryHint;
+ installReason = o.installReason;
disabledComponents = ArrayUtils.cloneOrNull(o.disabledComponents);
enabledComponents = ArrayUtils.cloneOrNull(o.enabledComponents);
}
@@ -202,6 +205,9 @@ public class PackageUserState {
if (categoryHint != oldState.categoryHint) {
return false;
}
+ if (installReason != oldState.installReason) {
+ return false;
+ }
if ((disabledComponents == null && oldState.disabledComponents != null)
|| (disabledComponents != null && oldState.disabledComponents == null)) {
return false;
diff --git a/core/java/android/os/GraphicsEnvironment.java b/core/java/android/os/GraphicsEnvironment.java
new file mode 100644
index 000000000000..4b130ed1adc8
--- /dev/null
+++ b/core/java/android/os/GraphicsEnvironment.java
@@ -0,0 +1,95 @@
+/*
+ * Copyright 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 android.os;
+
+import android.content.Context;
+import android.content.pm.ApplicationInfo;
+import android.content.pm.PackageManager;
+import android.os.SystemProperties;
+import android.util.Log;
+
+import dalvik.system.VMRuntime;
+
+import java.io.File;
+
+/** @hide */
+public final class GraphicsEnvironment {
+
+ private static final boolean DEBUG = false;
+ private static final String TAG = "GraphicsEnvironment";
+ private static final String PROPERTY_GFX_DRIVER = "ro.gfx.driver.0";
+
+ public static void setupGraphicsEnvironment(Context context) {
+ String driverPackageName = SystemProperties.get(PROPERTY_GFX_DRIVER);
+ if (driverPackageName == null || driverPackageName.isEmpty()) {
+ return;
+ }
+ // To minimize risk of driver updates crippling the device beyond user repair, never use an
+ // updated driver for privileged or non-updated system apps. Presumably pre-installed apps
+ // were tested thoroughly with the pre-installed driver.
+ ApplicationInfo ai = context.getApplicationInfo();
+ if (ai.isPrivilegedApp() || (ai.isSystemApp() && !ai.isUpdatedSystemApp())) {
+ if (DEBUG) Log.v(TAG, "ignoring driver package for privileged/non-updated system app");
+ return;
+ }
+ ApplicationInfo driverInfo;
+ try {
+ driverInfo = context.getPackageManager().getApplicationInfo(driverPackageName,
+ PackageManager.MATCH_SYSTEM_ONLY);
+ } catch (PackageManager.NameNotFoundException e) {
+ Log.w(TAG, "driver package '" + driverPackageName + "' not installed");
+ return;
+ }
+ String abi = chooseAbi(driverInfo);
+ if (abi == null) {
+ if (DEBUG) {
+ // This is the normal case for the pre-installed empty driver package, don't spam
+ if (driverInfo.isUpdatedSystemApp()) {
+ Log.w(TAG, "updated driver package has no compatible native libraries");
+ }
+ }
+ return;
+ }
+
+ StringBuilder sb = new StringBuilder();
+ sb.append(driverInfo.nativeLibraryDir)
+ .append(File.pathSeparator);
+ sb.append(driverInfo.sourceDir)
+ .append("!/lib/")
+ .append(abi);
+ String paths = sb.toString();
+
+ if (DEBUG) Log.v(TAG, "gfx driver package libs: " + paths);
+ setDriverPath(paths);
+ }
+
+ private static String chooseAbi(ApplicationInfo ai) {
+ String isa = VMRuntime.getCurrentInstructionSet();
+ if (ai.primaryCpuAbi != null &&
+ isa.equals(VMRuntime.getInstructionSet(ai.primaryCpuAbi))) {
+ return ai.primaryCpuAbi;
+ }
+ if (ai.secondaryCpuAbi != null &&
+ isa.equals(VMRuntime.getInstructionSet(ai.secondaryCpuAbi))) {
+ return ai.secondaryCpuAbi;
+ }
+ return null;
+ }
+
+ private static native void setDriverPath(String path);
+
+}
diff --git a/core/java/android/view/inputmethod/InputMethodInfo.java b/core/java/android/view/inputmethod/InputMethodInfo.java
index 5c8e6dc39960..b6da1d8c9de3 100644
--- a/core/java/android/view/inputmethod/InputMethodInfo.java
+++ b/core/java/android/view/inputmethod/InputMethodInfo.java
@@ -57,6 +57,7 @@ import java.util.Map;
* @attr ref android.R.styleable#InputMethod_settingsActivity
* @attr ref android.R.styleable#InputMethod_isDefault
* @attr ref android.R.styleable#InputMethod_supportsSwitchingToNextInputMethod
+ * @attr ref android.R.styleable#InputMethod_supportsDismissingWindow
*/
public final class InputMethodInfo implements Parcelable {
static final String TAG = "InputMethodInfo";
@@ -104,6 +105,11 @@ public final class InputMethodInfo implements Parcelable {
private final boolean mSupportsSwitchingToNextInputMethod;
/**
+ * The flag whether this IME supports ways to dismiss its window (e.g. dismiss button.)
+ */
+ private final boolean mSupportsDismissingWindow;
+
+ /**
* Constructor.
*
* @param context The Context in which we are parsing the input method.
@@ -132,6 +138,7 @@ public final class InputMethodInfo implements Parcelable {
mId = new ComponentName(si.packageName, si.name).flattenToShortString();
boolean isAuxIme = true;
boolean supportsSwitchingToNextInputMethod = false; // false as default
+ boolean supportsDismissingWindow = false; // false as default
mForceDefault = false;
PackageManager pm = context.getPackageManager();
@@ -171,6 +178,8 @@ public final class InputMethodInfo implements Parcelable {
supportsSwitchingToNextInputMethod = sa.getBoolean(
com.android.internal.R.styleable.InputMethod_supportsSwitchingToNextInputMethod,
false);
+ supportsDismissingWindow = sa.getBoolean(
+ com.android.internal.R.styleable.InputMethod_supportsDismissingWindow, false);
sa.recycle();
final int depth = parser.getDepth();
@@ -242,6 +251,7 @@ public final class InputMethodInfo implements Parcelable {
mIsDefaultResId = isDefaultResId;
mIsAuxIme = isAuxIme;
mSupportsSwitchingToNextInputMethod = supportsSwitchingToNextInputMethod;
+ mSupportsDismissingWindow = supportsDismissingWindow;
}
InputMethodInfo(Parcel source) {
@@ -250,6 +260,7 @@ public final class InputMethodInfo implements Parcelable {
mIsDefaultResId = source.readInt();
mIsAuxIme = source.readInt() == 1;
mSupportsSwitchingToNextInputMethod = source.readInt() == 1;
+ mSupportsDismissingWindow = source.readInt() == 1;
mService = ResolveInfo.CREATOR.createFromParcel(source);
mSubtypes = new InputMethodSubtypeArray(source);
mForceDefault = false;
@@ -260,8 +271,10 @@ public final class InputMethodInfo implements Parcelable {
*/
public InputMethodInfo(String packageName, String className,
CharSequence label, String settingsActivity) {
- this(buildDummyResolveInfo(packageName, className, label), false, settingsActivity, null,
- 0, false /* forceDefault */, true /* supportsSwitchingToNextInputMethod */);
+ this(buildDummyResolveInfo(packageName, className, label), false /* isAuxIme */,
+ settingsActivity, null /* subtypes */, 0 /* isDefaultResId */,
+ false /* forceDefault */, true /* supportsSwitchingToNextInputMethod */,
+ true /* supportsDismissingWindow */);
}
/**
@@ -271,17 +284,18 @@ public final class InputMethodInfo implements Parcelable {
public InputMethodInfo(ResolveInfo ri, boolean isAuxIme,
String settingsActivity, List<InputMethodSubtype> subtypes, int isDefaultResId,
boolean forceDefault) {
- this(ri, isAuxIme, settingsActivity, subtypes, isDefaultResId,
- forceDefault, true /* supportsSwitchingToNextInputMethod */);
+ this(ri, isAuxIme, settingsActivity, subtypes, isDefaultResId, forceDefault,
+ true /* supportsSwitchingToNextInputMethod */,
+ true /* supportsDismissingWindow */);
}
/**
* Temporary API for creating a built-in input method for test.
* @hide
*/
- public InputMethodInfo(ResolveInfo ri, boolean isAuxIme,
- String settingsActivity, List<InputMethodSubtype> subtypes, int isDefaultResId,
- boolean forceDefault, boolean supportsSwitchingToNextInputMethod) {
+ public InputMethodInfo(ResolveInfo ri, boolean isAuxIme, String settingsActivity,
+ List<InputMethodSubtype> subtypes, int isDefaultResId, boolean forceDefault,
+ boolean supportsSwitchingToNextInputMethod, boolean supportsDismissingWindow) {
final ServiceInfo si = ri.serviceInfo;
mService = ri;
mId = new ComponentName(si.packageName, si.name).flattenToShortString();
@@ -291,6 +305,7 @@ public final class InputMethodInfo implements Parcelable {
mSubtypes = new InputMethodSubtypeArray(subtypes);
mForceDefault = forceDefault;
mSupportsSwitchingToNextInputMethod = supportsSwitchingToNextInputMethod;
+ mSupportsDismissingWindow = supportsDismissingWindow;
}
private static ResolveInfo buildDummyResolveInfo(String packageName, String className,
@@ -431,7 +446,8 @@ public final class InputMethodInfo implements Parcelable {
public void dump(Printer pw, String prefix) {
pw.println(prefix + "mId=" + mId
+ " mSettingsActivityName=" + mSettingsActivityName
- + " mSupportsSwitchingToNextInputMethod=" + mSupportsSwitchingToNextInputMethod);
+ + " mSupportsSwitchingToNextInputMethod=" + mSupportsSwitchingToNextInputMethod
+ + " mSupportsDismissingWindow=" + mSupportsDismissingWindow);
pw.println(prefix + "mIsDefaultResId=0x"
+ Integer.toHexString(mIsDefaultResId));
pw.println(prefix + "Service:");
@@ -484,6 +500,14 @@ public final class InputMethodInfo implements Parcelable {
}
/**
+ * @return true if this input method supports ways to dismiss its window.
+ * @hide
+ */
+ public boolean supportsDismissingWindow() {
+ return mSupportsDismissingWindow;
+ }
+
+ /**
* Used to package this object into a {@link Parcel}.
*
* @param dest The {@link Parcel} to be written.
@@ -496,6 +520,7 @@ public final class InputMethodInfo implements Parcelable {
dest.writeInt(mIsDefaultResId);
dest.writeInt(mIsAuxIme ? 1 : 0);
dest.writeInt(mSupportsSwitchingToNextInputMethod ? 1 : 0);
+ dest.writeInt(mSupportsDismissingWindow ? 1 : 0);
mService.writeToParcel(dest, flags);
mSubtypes.writeToParcel(dest);
}
diff --git a/core/java/com/android/internal/app/ChooserActivity.java b/core/java/com/android/internal/app/ChooserActivity.java
index 787861126692..d4baa18f7100 100644
--- a/core/java/com/android/internal/app/ChooserActivity.java
+++ b/core/java/com/android/internal/app/ChooserActivity.java
@@ -418,7 +418,7 @@ public class ChooserActivity extends ResolverActivity {
}
}
}
- updateChooserCounts(target);
+ updateModelAndChooserCounts(target);
return super.onTargetSelected(target, alwaysCheck);
}
@@ -575,27 +575,18 @@ public class ChooserActivity extends ResolverActivity {
// Do nothing. We'll send the voice stuff ourselves.
}
- void updateChooserCounts(TargetInfo info) {
+ void updateModelAndChooserCounts(TargetInfo info) {
if (info != null) {
- UsageStatsManager usageStatsManager =
- (UsageStatsManager) getSystemService(Context.USAGE_STATS_SERVICE);
- if (usageStatsManager == null) {
- if (DEBUG) {
- Log.d(TAG, "Can not start UsageStatsManager");
- }
- return;
- }
final ResolveInfo ri = info.getResolveInfo();
Intent targetIntent = getTargetIntent();
if (ri != null && ri.activityInfo != null && targetIntent != null) {
- usageStatsManager.reportChooserSelection(ri.activityInfo.packageName, getUserId(),
- targetIntent.getType(), null, targetIntent.getAction());
if (mAdapter != null) {
mAdapter.updateModel(info.getResolvedComponentName());
+ mAdapter.updateChooserCounts(ri.activityInfo.packageName, getUserId(),
+ targetIntent.getAction());
}
if (DEBUG) {
Log.d(TAG, "ResolveInfo Package is " + ri.activityInfo.packageName);
- Log.d(TAG, "Annotation to be updated is " + targetIntent.getType());
Log.d(TAG, "Action to be updated is " + targetIntent.getAction());
}
} else if(DEBUG) {
@@ -618,7 +609,7 @@ public class ChooserActivity extends ResolverActivity {
} else {
TargetInfo clonedTarget = selectedTarget.cloneFilledIn(matchingIntent, 0);
if (super.onTargetSelected(clonedTarget, false)) {
- updateChooserCounts(clonedTarget);
+ updateModelAndChooserCounts(clonedTarget);
finish();
return;
}
diff --git a/core/java/com/android/internal/app/ResolverActivity.java b/core/java/com/android/internal/app/ResolverActivity.java
index 095063099395..d734d17b2dc9 100644
--- a/core/java/com/android/internal/app/ResolverActivity.java
+++ b/core/java/com/android/internal/app/ResolverActivity.java
@@ -1299,6 +1299,10 @@ public class ResolverActivity extends Activity {
mResolverListController.updateModel(componentName);
}
+ public void updateChooserCounts(String packageName, int userId, String action) {
+ mResolverListController.updateChooserCounts(packageName, userId, action);
+ }
+
/**
* Rebuild the list of resolvers. In some cases some parts will need some asynchronous work
* to complete.
diff --git a/core/java/com/android/internal/app/ResolverComparator.java b/core/java/com/android/internal/app/ResolverComparator.java
index 45fad97f6422..d9ab47e780d9 100644
--- a/core/java/com/android/internal/app/ResolverComparator.java
+++ b/core/java/com/android/internal/app/ResolverComparator.java
@@ -52,6 +52,8 @@ class ResolverComparator implements Comparator<ResolvedComponentInfo> {
private static final boolean DEBUG = false;
+ private static final int NUM_OF_TOP_ANNOTATIONS_TO_USE = 3;
+
// One week
private static final long USAGE_STATS_PERIOD = 1000 * 60 * 60 * 24 * 7;
@@ -74,7 +76,8 @@ class ResolverComparator implements Comparator<ResolvedComponentInfo> {
private final long mSinceTime;
private final LinkedHashMap<ComponentName, ScoredTarget> mScoredTargets = new LinkedHashMap<>();
private final String mReferrerPackage;
- public String mContentType;
+ private String mContentType;
+ private String[] mAnnotations;
private String mAction;
private LogisticRegressionAppRanker mRanker;
@@ -91,10 +94,26 @@ class ResolverComparator implements Comparator<ResolvedComponentInfo> {
mSinceTime = mCurrentTime - USAGE_STATS_PERIOD;
mStats = mUsm.queryAndAggregateUsageStats(mSinceTime, mCurrentTime);
mContentType = intent.getType();
+ getContentAnnotations(intent);
mAction = intent.getAction();
mRanker = new LogisticRegressionAppRanker(context);
}
+ public void getContentAnnotations(Intent intent) {
+ ArrayList<String> annotations = intent.getStringArrayListExtra(
+ Intent.EXTRA_CONTENT_ANNOTATIONS);
+ if (annotations != null) {
+ int size = annotations.size();
+ if (size > NUM_OF_TOP_ANNOTATIONS_TO_USE) {
+ size = NUM_OF_TOP_ANNOTATIONS_TO_USE;
+ }
+ mAnnotations = new String[size];
+ for (int i = 0; i < size; i++) {
+ mAnnotations[i] = annotations.get(i);
+ }
+ }
+ }
+
public void compute(List<ResolvedComponentInfo> targets) {
mScoredTargets.clear();
@@ -132,12 +151,18 @@ class ResolverComparator implements Comparator<ResolvedComponentInfo> {
if (launched > mostLaunched) {
mostLaunched = launched;
}
- // TODO(kanlig): get and combine counts of categories.
int selected = 0;
if (pkStats.mChooserCounts != null && mAction != null
&& pkStats.mChooserCounts.get(mAction) != null) {
selected = pkStats.mChooserCounts.get(mAction).getOrDefault(mContentType, 0);
+ if (mAnnotations != null) {
+ final int size = mAnnotations.length;
+ for (int i = 0; i < size; i++) {
+ selected += pkStats.mChooserCounts.get(mAction)
+ .getOrDefault(mAnnotations[i], 0);
+ }
+ }
}
if (DEBUG) {
if (mAction == null) {
@@ -288,6 +313,12 @@ class ResolverComparator implements Comparator<ResolvedComponentInfo> {
}
}
+ public void updateChooserCounts(String packageName, int userId, String action) {
+ if (mUsm != null) {
+ mUsm.reportChooserSelection(packageName, userId, mContentType, mAnnotations, action);
+ }
+ }
+
public void updateModel(ComponentName componentName) {
if (mScoredTargets == null || componentName == null ||
!mScoredTargets.containsKey(componentName)) {
diff --git a/core/java/com/android/internal/app/ResolverListController.java b/core/java/com/android/internal/app/ResolverListController.java
index d864a310e91b..f88f6f9a07e0 100644
--- a/core/java/com/android/internal/app/ResolverListController.java
+++ b/core/java/com/android/internal/app/ResolverListController.java
@@ -224,4 +224,10 @@ public class ResolverListController {
mResolverComparator.updateModel(componentName);
}
}
+
+ public void updateChooserCounts(String packageName, int userId, String action) {
+ if (mResolverComparator != null) {
+ mResolverComparator.updateChooserCounts(packageName, userId, action);
+ }
+ }
}
diff --git a/core/java/com/android/internal/os/ZygoteInit.java b/core/java/com/android/internal/os/ZygoteInit.java
index 8eb75c06b871..b36843759449 100644
--- a/core/java/com/android/internal/os/ZygoteInit.java
+++ b/core/java/com/android/internal/os/ZygoteInit.java
@@ -80,7 +80,6 @@ import java.security.Provider;
public class ZygoteInit {
private static final String TAG = "Zygote";
- private static final String PROPERTY_DISABLE_OPENGL_PRELOADING = "ro.zygote.disable_gl_preload";
private static final String PROPERTY_RUNNING_IN_CONTAINER = "ro.boot.container";
private static final int LOG_BOOT_PROGRESS_PRELOAD_START = 3020;
@@ -125,9 +124,6 @@ public class ZygoteInit {
bootTimingsTraceLog.traceBegin("PreloadResources");
preloadResources();
bootTimingsTraceLog.traceEnd(); // PreloadResources
- bootTimingsTraceLog.traceBegin("PreloadOpenGL");
- preloadOpenGL();
- bootTimingsTraceLog.traceEnd(); // PreloadOpenGL
preloadSharedLibraries();
preloadTextResources();
// Ask the WebViewFactory to do any initialization that must run in the zygote process,
@@ -177,12 +173,6 @@ public class ZygoteInit {
System.loadLibrary("jnigraphics");
}
- private static void preloadOpenGL() {
- if (!SystemProperties.getBoolean(PROPERTY_DISABLE_OPENGL_PRELOADING, false)) {
- EGL14.eglGetDisplay(EGL14.EGL_DEFAULT_DISPLAY);
- }
- }
-
private static void preloadTextResources() {
Hyphenator.init();
TextView.preloadFontCache();
diff --git a/core/java/com/android/internal/widget/PasswordEntryKeyboard.java b/core/java/com/android/internal/widget/PasswordEntryKeyboard.java
deleted file mode 100644
index 7483e7584b87..000000000000
--- a/core/java/com/android/internal/widget/PasswordEntryKeyboard.java
+++ /dev/null
@@ -1,267 +0,0 @@
-/*
- * Copyright (C) 2010 Google Inc.
- *
- * 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.widget;
-
-import android.content.Context;
-import android.content.res.Resources;
-import android.content.res.XmlResourceParser;
-import android.graphics.drawable.Drawable;
-import android.inputmethodservice.Keyboard;
-import android.inputmethodservice.KeyboardView;
-import com.android.internal.R;
-
-/**
- * A basic, embed-able keyboard designed for password entry. Allows entry of all Latin-1 characters.
- *
- * It has two modes: alpha and numeric. In alpha mode, it allows all Latin-1 characters and enables
- * an additional keyboard with symbols. In numeric mode, it shows a 12-key DTMF dialer-like
- * keypad with alpha characters hints.
- */
-public class PasswordEntryKeyboard extends Keyboard {
- private static final int SHIFT_OFF = 0;
- private static final int SHIFT_ON = 1;
- private static final int SHIFT_LOCKED = 2;
- public static final int KEYCODE_SPACE = ' ';
-
- private Drawable mShiftIcon;
- private Drawable mShiftLockIcon;
-
- // These two arrays must be the same length
- private Drawable[] mOldShiftIcons = { null, null };
- private Key[] mShiftKeys = { null, null };
-
- private Key mEnterKey;
- private Key mF1Key;
- private Key mSpaceKey;
- private int mShiftState = SHIFT_OFF;
-
- static int sSpacebarVerticalCorrection;
-
- public PasswordEntryKeyboard(Context context, int xmlLayoutResId) {
- this(context, xmlLayoutResId, 0);
- }
-
- public PasswordEntryKeyboard(Context context, int xmlLayoutResId, int width, int height) {
- this(context, xmlLayoutResId, 0, width, height);
- }
-
- public PasswordEntryKeyboard(Context context, int xmlLayoutResId, int mode) {
- super(context, xmlLayoutResId, mode);
- init(context);
- }
-
- public PasswordEntryKeyboard(Context context, int xmlLayoutResId, int mode,
- int width, int height) {
- super(context, xmlLayoutResId, mode, width, height);
- init(context);
- }
-
- private void init(Context context) {
- final Resources res = context.getResources();
- mShiftIcon = context.getDrawable(R.drawable.sym_keyboard_shift);
- mShiftLockIcon = context.getDrawable(R.drawable.sym_keyboard_shift_locked);
- sSpacebarVerticalCorrection = res.getDimensionPixelOffset(
- R.dimen.password_keyboard_spacebar_vertical_correction);
- }
-
- public PasswordEntryKeyboard(Context context, int layoutTemplateResId,
- CharSequence characters, int columns, int horizontalPadding) {
- super(context, layoutTemplateResId, characters, columns, horizontalPadding);
- }
-
- @Override
- protected Key createKeyFromXml(Resources res, Row parent, int x, int y,
- XmlResourceParser parser) {
- LatinKey key = new LatinKey(res, parent, x, y, parser);
- final int code = key.codes[0];
- if (code >=0 && code != '\n' && (code < 32 || code > 127)) {
- // Log.w(TAG, "Key code for " + key.label + " is not latin-1");
- key.label = " ";
- key.setEnabled(false);
- }
- switch (key.codes[0]) {
- case 10:
- mEnterKey = key;
- break;
- case PasswordEntryKeyboardView.KEYCODE_F1:
- mF1Key = key;
- break;
- case 32:
- mSpaceKey = key;
- break;
- }
- return key;
- }
-
- /**
- * Allows enter key resources to be overridden
- * @param res resources to grab given items from
- * @param previewId preview drawable shown on enter key
- * @param iconId normal drawable shown on enter key
- * @param labelId string shown on enter key
- */
- void setEnterKeyResources(Resources res, int previewId, int iconId, int labelId) {
- if (mEnterKey != null) {
- // Reset some of the rarely used attributes.
- mEnterKey.popupCharacters = null;
- mEnterKey.popupResId = 0;
- mEnterKey.text = null;
-
- mEnterKey.iconPreview = res.getDrawable(previewId);
- mEnterKey.icon = res.getDrawable(iconId);
- mEnterKey.label = res.getText(labelId);
-
- // Set the initial size of the preview icon
- if (mEnterKey.iconPreview != null) {
- mEnterKey.iconPreview.setBounds(0, 0,
- mEnterKey.iconPreview.getIntrinsicWidth(),
- mEnterKey.iconPreview.getIntrinsicHeight());
- }
- }
- }
-
- /**
- * Allows shiftlock to be turned on. See {@link #setShiftLocked(boolean)}
- *
- */
- void enableShiftLock() {
- int i = 0;
- for (int index : getShiftKeyIndices()) {
- if (index >= 0 && i < mShiftKeys.length) {
- mShiftKeys[i] = getKeys().get(index);
- if (mShiftKeys[i] instanceof LatinKey) {
- ((LatinKey)mShiftKeys[i]).enableShiftLock();
- }
- mOldShiftIcons[i] = mShiftKeys[i].icon;
- i++;
- }
- }
- }
-
- /**
- * Turn on shift lock. This turns on the LED for this key, if it has one.
- * It should be followed by a call to {@link KeyboardView#invalidateKey(int)}
- * or {@link KeyboardView#invalidateAllKeys()}
- *
- * @param shiftLocked
- */
- void setShiftLocked(boolean shiftLocked) {
- for (Key shiftKey : mShiftKeys) {
- if (shiftKey != null) {
- shiftKey.on = shiftLocked;
- shiftKey.icon = mShiftLockIcon;
- }
- }
- mShiftState = shiftLocked ? SHIFT_LOCKED : SHIFT_ON;
- }
-
- /**
- * Turn on shift mode. Sets shift mode and turns on icon for shift key.
- * It should be followed by a call to {@link KeyboardView#invalidateKey(int)}
- * or {@link KeyboardView#invalidateAllKeys()}
- *
- * @param shiftLocked
- */
- @Override
- public boolean setShifted(boolean shiftState) {
- boolean shiftChanged = false;
- if (shiftState == false) {
- shiftChanged = mShiftState != SHIFT_OFF;
- mShiftState = SHIFT_OFF;
- } else if (mShiftState == SHIFT_OFF) {
- shiftChanged = mShiftState == SHIFT_OFF;
- mShiftState = SHIFT_ON;
- }
- for (int i = 0; i < mShiftKeys.length; i++) {
- if (mShiftKeys[i] != null) {
- if (shiftState == false) {
- mShiftKeys[i].on = false;
- mShiftKeys[i].icon = mOldShiftIcons[i];
- } else if (mShiftState == SHIFT_OFF) {
- mShiftKeys[i].on = false;
- mShiftKeys[i].icon = mShiftIcon;
- }
- } else {
- // return super.setShifted(shiftState);
- }
- }
- return shiftChanged;
- }
-
- /**
- * Whether or not keyboard is shifted.
- * @return true if keyboard state is shifted.
- */
- @Override
- public boolean isShifted() {
- if (mShiftKeys[0] != null) {
- return mShiftState != SHIFT_OFF;
- } else {
- return super.isShifted();
- }
- }
-
- static class LatinKey extends Keyboard.Key {
- private boolean mShiftLockEnabled;
- private boolean mEnabled = true;
-
- public LatinKey(Resources res, Keyboard.Row parent, int x, int y,
- XmlResourceParser parser) {
- super(res, parent, x, y, parser);
- if (popupCharacters != null && popupCharacters.length() == 0) {
- // If there is a keyboard with no keys specified in popupCharacters
- popupResId = 0;
- }
- }
-
- void setEnabled(boolean enabled) {
- mEnabled = enabled;
- }
-
- void enableShiftLock() {
- mShiftLockEnabled = true;
- }
-
- @Override
- public void onReleased(boolean inside) {
- if (!mShiftLockEnabled) {
- super.onReleased(inside);
- } else {
- pressed = !pressed;
- }
- }
-
- /**
- * Overriding this method so that we can reduce the target area for certain keys.
- */
- @Override
- public boolean isInside(int x, int y) {
- if (!mEnabled) {
- return false;
- }
- final int code = codes[0];
- if (code == KEYCODE_SHIFT || code == KEYCODE_DELETE) {
- y -= height / 10;
- if (code == KEYCODE_SHIFT) x += width / 6;
- if (code == KEYCODE_DELETE) x -= width / 6;
- } else if (code == KEYCODE_SPACE) {
- y += PasswordEntryKeyboard.sSpacebarVerticalCorrection;
- }
- return super.isInside(x, y);
- }
- }
-}
diff --git a/core/java/com/android/internal/widget/PasswordEntryKeyboardHelper.java b/core/java/com/android/internal/widget/PasswordEntryKeyboardHelper.java
deleted file mode 100644
index b2c9dc53da39..000000000000
--- a/core/java/com/android/internal/widget/PasswordEntryKeyboardHelper.java
+++ /dev/null
@@ -1,340 +0,0 @@
-/*
- * Copyright (C) 2010 Google Inc.
- *
- * 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.widget;
-
-import android.content.Context;
-import android.content.res.Resources;
-import android.inputmethodservice.Keyboard;
-import android.inputmethodservice.KeyboardView;
-import android.inputmethodservice.KeyboardView.OnKeyboardActionListener;
-import android.os.SystemClock;
-import android.provider.Settings;
-import android.util.Log;
-import android.view.HapticFeedbackConstants;
-import android.view.KeyCharacterMap;
-import android.view.KeyEvent;
-import android.view.View;
-import android.view.ViewGroup;
-import android.view.ViewGroup.LayoutParams;
-import android.view.ViewRootImpl;
-import com.android.internal.R;
-
-public class PasswordEntryKeyboardHelper implements OnKeyboardActionListener {
-
- public static final int KEYBOARD_MODE_ALPHA = 0;
- public static final int KEYBOARD_MODE_NUMERIC = 1;
- private static final int KEYBOARD_STATE_NORMAL = 0;
- private static final int KEYBOARD_STATE_SHIFTED = 1;
- private static final int KEYBOARD_STATE_CAPSLOCK = 2;
- private static final String TAG = "PasswordEntryKeyboardHelper";
- private int mKeyboardMode = KEYBOARD_MODE_ALPHA;
- private int mKeyboardState = KEYBOARD_STATE_NORMAL;
- private PasswordEntryKeyboard mQwertyKeyboard;
- private PasswordEntryKeyboard mQwertyKeyboardShifted;
- private PasswordEntryKeyboard mSymbolsKeyboard;
- private PasswordEntryKeyboard mSymbolsKeyboardShifted;
- private PasswordEntryKeyboard mNumericKeyboard;
- private final Context mContext;
- private final View mTargetView;
- private final KeyboardView mKeyboardView;
- private long[] mVibratePattern;
- private boolean mEnableHaptics = false;
-
- private static final int NUMERIC = 0;
- private static final int QWERTY = 1;
- private static final int QWERTY_SHIFTED = 2;
- private static final int SYMBOLS = 3;
- private static final int SYMBOLS_SHIFTED = 4;
-
- int mLayouts[] = new int[] {
- R.xml.password_kbd_numeric,
- R.xml.password_kbd_qwerty,
- R.xml.password_kbd_qwerty_shifted,
- R.xml.password_kbd_symbols,
- R.xml.password_kbd_symbols_shift
- };
-
- private boolean mUsingScreenWidth;
-
- public PasswordEntryKeyboardHelper(Context context, KeyboardView keyboardView, View targetView) {
- this(context, keyboardView, targetView, true, null);
- }
-
- public PasswordEntryKeyboardHelper(Context context, KeyboardView keyboardView, View targetView,
- boolean useFullScreenWidth) {
- this(context, keyboardView, targetView, useFullScreenWidth, null);
- }
-
- public PasswordEntryKeyboardHelper(Context context, KeyboardView keyboardView, View targetView,
- boolean useFullScreenWidth, int layouts[]) {
- mContext = context;
- mTargetView = targetView;
- mKeyboardView = keyboardView;
- mKeyboardView.setOnKeyboardActionListener(this);
- mUsingScreenWidth = useFullScreenWidth;
- if (layouts != null) {
- if (layouts.length != mLayouts.length) {
- throw new RuntimeException("Wrong number of layouts");
- }
- for (int i = 0; i < mLayouts.length; i++) {
- mLayouts[i] = layouts[i];
- }
- }
- createKeyboards();
- }
-
- public void createKeyboards() {
- LayoutParams lp = mKeyboardView.getLayoutParams();
- if (mUsingScreenWidth || lp.width == ViewGroup.LayoutParams.MATCH_PARENT) {
- createKeyboardsWithDefaultWidth();
- } else {
- createKeyboardsWithSpecificSize(lp.width, lp.height);
- }
- }
-
- public void setEnableHaptics(boolean enabled) {
- mEnableHaptics = enabled;
- }
-
- public boolean isAlpha() {
- return mKeyboardMode == KEYBOARD_MODE_ALPHA;
- }
-
- private void createKeyboardsWithSpecificSize(int width, int height) {
- mNumericKeyboard = new PasswordEntryKeyboard(mContext, mLayouts[NUMERIC], width, height);
- mQwertyKeyboard = new PasswordEntryKeyboard(mContext, mLayouts[QWERTY], R.id.mode_normal,
- width, height);
- mQwertyKeyboard.enableShiftLock();
-
- mQwertyKeyboardShifted = new PasswordEntryKeyboard(mContext, mLayouts[QWERTY_SHIFTED],
- R.id.mode_normal, width, height);
- mQwertyKeyboardShifted.enableShiftLock();
- mQwertyKeyboardShifted.setShifted(true); // always shifted.
-
- mSymbolsKeyboard = new PasswordEntryKeyboard(mContext, mLayouts[SYMBOLS], width, height);
- mSymbolsKeyboard.enableShiftLock();
-
- mSymbolsKeyboardShifted = new PasswordEntryKeyboard(mContext, mLayouts[SYMBOLS_SHIFTED],
- width, height);
- mSymbolsKeyboardShifted.enableShiftLock();
- mSymbolsKeyboardShifted.setShifted(true); // always shifted
- }
-
- private void createKeyboardsWithDefaultWidth() {
- mNumericKeyboard = new PasswordEntryKeyboard(mContext, mLayouts[NUMERIC]);
- mQwertyKeyboard = new PasswordEntryKeyboard(mContext, mLayouts[QWERTY], R.id.mode_normal);
- mQwertyKeyboard.enableShiftLock();
-
- mQwertyKeyboardShifted = new PasswordEntryKeyboard(mContext, mLayouts[QWERTY_SHIFTED],
- R.id.mode_normal);
- mQwertyKeyboardShifted.enableShiftLock();
- mQwertyKeyboardShifted.setShifted(true); // always shifted.
-
- mSymbolsKeyboard = new PasswordEntryKeyboard(mContext, mLayouts[SYMBOLS]);
- mSymbolsKeyboard.enableShiftLock();
-
- mSymbolsKeyboardShifted = new PasswordEntryKeyboard(mContext, mLayouts[SYMBOLS_SHIFTED]);
- mSymbolsKeyboardShifted.enableShiftLock();
- mSymbolsKeyboardShifted.setShifted(true); // always shifted
- }
-
- public void setKeyboardMode(int mode) {
- switch (mode) {
- case KEYBOARD_MODE_ALPHA:
- mKeyboardView.setKeyboard(mQwertyKeyboard);
- mKeyboardState = KEYBOARD_STATE_NORMAL;
- final boolean visiblePassword = Settings.System.getInt(
- mContext.getContentResolver(),
- Settings.System.TEXT_SHOW_PASSWORD, 1) != 0;
- final boolean enablePreview = false; // TODO: grab from configuration
- mKeyboardView.setPreviewEnabled(visiblePassword && enablePreview);
- break;
- case KEYBOARD_MODE_NUMERIC:
- mKeyboardView.setKeyboard(mNumericKeyboard);
- mKeyboardState = KEYBOARD_STATE_NORMAL;
- mKeyboardView.setPreviewEnabled(false); // never show popup for numeric keypad
- break;
- }
- mKeyboardMode = mode;
- }
-
- private void sendKeyEventsToTarget(int character) {
- ViewRootImpl viewRootImpl = mTargetView.getViewRootImpl();
- KeyEvent[] events = KeyCharacterMap.load(KeyCharacterMap.VIRTUAL_KEYBOARD).getEvents(
- new char[] { (char) character });
- if (events != null) {
- final int N = events.length;
- for (int i=0; i<N; i++) {
- KeyEvent event = events[i];
- event = KeyEvent.changeFlags(event, event.getFlags()
- | KeyEvent.FLAG_SOFT_KEYBOARD | KeyEvent.FLAG_KEEP_TOUCH_MODE);
- viewRootImpl.dispatchInputEvent(event);
- }
- }
- }
-
- public void sendDownUpKeyEvents(int keyEventCode) {
- long eventTime = SystemClock.uptimeMillis();
- ViewRootImpl viewRootImpl = mTargetView.getViewRootImpl();
- viewRootImpl.dispatchKeyFromIme(
- new KeyEvent(eventTime, eventTime, KeyEvent.ACTION_DOWN, keyEventCode, 0, 0,
- KeyCharacterMap.VIRTUAL_KEYBOARD, 0,
- KeyEvent.FLAG_SOFT_KEYBOARD|KeyEvent.FLAG_KEEP_TOUCH_MODE));
- viewRootImpl.dispatchKeyFromIme(
- new KeyEvent(eventTime, eventTime, KeyEvent.ACTION_UP, keyEventCode, 0, 0,
- KeyCharacterMap.VIRTUAL_KEYBOARD, 0,
- KeyEvent.FLAG_SOFT_KEYBOARD|KeyEvent.FLAG_KEEP_TOUCH_MODE));
- }
-
- public void onKey(int primaryCode, int[] keyCodes) {
- if (primaryCode == Keyboard.KEYCODE_DELETE) {
- handleBackspace();
- } else if (primaryCode == Keyboard.KEYCODE_SHIFT) {
- handleShift();
- } else if (primaryCode == Keyboard.KEYCODE_CANCEL) {
- handleClose();
- return;
- } else if (primaryCode == Keyboard.KEYCODE_MODE_CHANGE && mKeyboardView != null) {
- handleModeChange();
- } else {
- handleCharacter(primaryCode, keyCodes);
- // Switch back to old keyboard if we're not in capslock mode
- if (mKeyboardState == KEYBOARD_STATE_SHIFTED) {
- // skip to the unlocked state
- mKeyboardState = KEYBOARD_STATE_CAPSLOCK;
- handleShift();
- }
- }
- }
-
- /**
- * Sets and enables vibrate pattern. If id is 0 (or can't be loaded), vibrate is disabled.
- * @param id resource id for array containing vibrate pattern.
- */
- public void setVibratePattern(int id) {
- int[] tmpArray = null;
- try {
- tmpArray = mContext.getResources().getIntArray(id);
- } catch (Resources.NotFoundException e) {
- if (id != 0) {
- Log.e(TAG, "Vibrate pattern missing", e);
- }
- }
- if (tmpArray == null) {
- mVibratePattern = null;
- return;
- }
- mVibratePattern = new long[tmpArray.length];
- for (int i = 0; i < tmpArray.length; i++) {
- mVibratePattern[i] = tmpArray[i];
- }
- }
-
- private void handleModeChange() {
- final Keyboard current = mKeyboardView.getKeyboard();
- Keyboard next = null;
- if (current == mQwertyKeyboard || current == mQwertyKeyboardShifted) {
- next = mSymbolsKeyboard;
- } else if (current == mSymbolsKeyboard || current == mSymbolsKeyboardShifted) {
- next = mQwertyKeyboard;
- }
- if (next != null) {
- mKeyboardView.setKeyboard(next);
- mKeyboardState = KEYBOARD_STATE_NORMAL;
- }
- }
-
- public void handleBackspace() {
- sendDownUpKeyEvents(KeyEvent.KEYCODE_DEL);
- performHapticFeedback();
- }
-
- private void handleShift() {
- if (mKeyboardView == null) {
- return;
- }
- Keyboard current = mKeyboardView.getKeyboard();
- PasswordEntryKeyboard next = null;
- final boolean isAlphaMode = current == mQwertyKeyboard
- || current == mQwertyKeyboardShifted;
- if (mKeyboardState == KEYBOARD_STATE_NORMAL) {
- mKeyboardState = isAlphaMode ? KEYBOARD_STATE_SHIFTED : KEYBOARD_STATE_CAPSLOCK;
- next = isAlphaMode ? mQwertyKeyboardShifted : mSymbolsKeyboardShifted;
- } else if (mKeyboardState == KEYBOARD_STATE_SHIFTED) {
- mKeyboardState = KEYBOARD_STATE_CAPSLOCK;
- next = isAlphaMode ? mQwertyKeyboardShifted : mSymbolsKeyboardShifted;
- } else if (mKeyboardState == KEYBOARD_STATE_CAPSLOCK) {
- mKeyboardState = KEYBOARD_STATE_NORMAL;
- next = isAlphaMode ? mQwertyKeyboard : mSymbolsKeyboard;
- }
- if (next != null) {
- if (next != current) {
- mKeyboardView.setKeyboard(next);
- }
- next.setShiftLocked(mKeyboardState == KEYBOARD_STATE_CAPSLOCK);
- mKeyboardView.setShifted(mKeyboardState != KEYBOARD_STATE_NORMAL);
- }
- }
-
- private void handleCharacter(int primaryCode, int[] keyCodes) {
- // Maybe turn off shift if not in capslock mode.
- if (mKeyboardView.isShifted() && primaryCode != ' ' && primaryCode != '\n') {
- primaryCode = Character.toUpperCase(primaryCode);
- }
- sendKeyEventsToTarget(primaryCode);
- }
-
- private void handleClose() {
-
- }
-
- public void onPress(int primaryCode) {
- performHapticFeedback();
- }
-
- private void performHapticFeedback() {
- if (mEnableHaptics) {
- mKeyboardView.performHapticFeedback(HapticFeedbackConstants.VIRTUAL_KEY,
- HapticFeedbackConstants.FLAG_IGNORE_VIEW_SETTING
- | HapticFeedbackConstants.FLAG_IGNORE_GLOBAL_SETTING);
- }
- }
-
- public void onRelease(int primaryCode) {
-
- }
-
- public void onText(CharSequence text) {
-
- }
-
- public void swipeDown() {
-
- }
-
- public void swipeLeft() {
-
- }
-
- public void swipeRight() {
-
- }
-
- public void swipeUp() {
-
- }
-};
diff --git a/core/java/com/android/internal/widget/PasswordEntryKeyboardView.java b/core/java/com/android/internal/widget/PasswordEntryKeyboardView.java
deleted file mode 100644
index d27346ba645e..000000000000
--- a/core/java/com/android/internal/widget/PasswordEntryKeyboardView.java
+++ /dev/null
@@ -1,55 +0,0 @@
-/*
- * Copyright (C) 2010 Google Inc.
- *
- * 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.widget;
-
-import android.content.Context;
-import android.inputmethodservice.KeyboardView;
-import android.util.AttributeSet;
-
-public class PasswordEntryKeyboardView extends KeyboardView {
-
- static final int KEYCODE_OPTIONS = -100;
- static final int KEYCODE_SHIFT_LONGPRESS = -101;
- static final int KEYCODE_VOICE = -102;
- static final int KEYCODE_F1 = -103;
- static final int KEYCODE_NEXT_LANGUAGE = -104;
-
- public PasswordEntryKeyboardView(Context context, AttributeSet attrs) {
- this(context, attrs, 0);
- }
-
- public PasswordEntryKeyboardView(Context context, AttributeSet attrs, int defStyleAttr) {
- this(context, attrs, defStyleAttr, 0);
- }
-
- public PasswordEntryKeyboardView(
- Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) {
- super(context, attrs, defStyleAttr, defStyleRes);
- }
-
- @Override
- public boolean setShifted(boolean shifted) {
- boolean result = super.setShifted(shifted);
- // invalidate both shift keys
- int[] indices = getKeyboard().getShiftKeyIndices();
- for (int index : indices) {
- invalidateKey(index);
- }
- return result;
- }
-
-}
diff --git a/core/java/com/android/internal/widget/RotarySelector.java b/core/java/com/android/internal/widget/RotarySelector.java
deleted file mode 100644
index 866f89b3a272..000000000000
--- a/core/java/com/android/internal/widget/RotarySelector.java
+++ /dev/null
@@ -1,780 +0,0 @@
-/*
- * Copyright (C) 2009 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.internal.widget;
-
-import android.content.Context;
-import android.content.res.Resources;
-import android.content.res.TypedArray;
-import android.graphics.Canvas;
-import android.graphics.Paint;
-import android.graphics.Bitmap;
-import android.graphics.BitmapFactory;
-import android.graphics.Matrix;
-import android.media.AudioAttributes;
-import android.os.UserHandle;
-import android.os.Vibrator;
-import android.provider.Settings;
-import android.util.AttributeSet;
-import android.util.Log;
-import android.view.MotionEvent;
-import android.view.View;
-import android.view.VelocityTracker;
-import android.view.ViewConfiguration;
-import android.view.animation.DecelerateInterpolator;
-
-import static android.view.animation.AnimationUtils.currentAnimationTimeMillis;
-
-import com.android.internal.R;
-
-
-/**
- * Custom view that presents up to two items that are selectable by rotating a semi-circle from
- * left to right, or right to left. Used by incoming call screen, and the lock screen when no
- * security pattern is set.
- */
-public class RotarySelector extends View {
- public static final int HORIZONTAL = 0;
- public static final int VERTICAL = 1;
-
- private static final String LOG_TAG = "RotarySelector";
- private static final boolean DBG = false;
- private static final boolean VISUAL_DEBUG = false;
-
- private static final AudioAttributes VIBRATION_ATTRIBUTES = new AudioAttributes.Builder()
- .setContentType(AudioAttributes.CONTENT_TYPE_SONIFICATION)
- .setUsage(AudioAttributes.USAGE_ASSISTANCE_SONIFICATION)
- .build();
-
- // Listener for onDialTrigger() callbacks.
- private OnDialTriggerListener mOnDialTriggerListener;
-
- private float mDensity;
-
- // UI elements
- private Bitmap mBackground;
- private Bitmap mDimple;
- private Bitmap mDimpleDim;
-
- private Bitmap mLeftHandleIcon;
- private Bitmap mRightHandleIcon;
-
- private Bitmap mArrowShortLeftAndRight;
- private Bitmap mArrowLongLeft; // Long arrow starting on the left, pointing clockwise
- private Bitmap mArrowLongRight; // Long arrow starting on the right, pointing CCW
-
- // positions of the left and right handle
- private int mLeftHandleX;
- private int mRightHandleX;
-
- // current offset of rotary widget along the x axis
- private int mRotaryOffsetX = 0;
-
- // state of the animation used to bring the handle back to its start position when
- // the user lets go before triggering an action
- private boolean mAnimating = false;
- private long mAnimationStartTime;
- private long mAnimationDuration;
- private int mAnimatingDeltaXStart; // the animation will interpolate from this delta to zero
- private int mAnimatingDeltaXEnd;
-
- private DecelerateInterpolator mInterpolator;
-
- private Paint mPaint = new Paint();
-
- // used to rotate the background and arrow assets depending on orientation
- final Matrix mBgMatrix = new Matrix();
- final Matrix mArrowMatrix = new Matrix();
-
- /**
- * If the user is currently dragging something.
- */
- private int mGrabbedState = NOTHING_GRABBED;
- public static final int NOTHING_GRABBED = 0;
- public static final int LEFT_HANDLE_GRABBED = 1;
- public static final int RIGHT_HANDLE_GRABBED = 2;
-
- /**
- * Whether the user has triggered something (e.g dragging the left handle all the way over to
- * the right).
- */
- private boolean mTriggered = false;
-
- // Vibration (haptic feedback)
- private Vibrator mVibrator;
- private static final long VIBRATE_SHORT = 20; // msec
- private static final long VIBRATE_LONG = 20; // msec
-
- /**
- * The drawable for the arrows need to be scrunched this many dips towards the rotary bg below
- * it.
- */
- private static final int ARROW_SCRUNCH_DIP = 6;
-
- /**
- * How far inset the left and right circles should be
- */
- private static final int EDGE_PADDING_DIP = 9;
-
- /**
- * How far from the edge of the screen the user must drag to trigger the event.
- */
- private static final int EDGE_TRIGGER_DIP = 100;
-
- /**
- * Dimensions of arc in background drawable.
- */
- static final int OUTER_ROTARY_RADIUS_DIP = 390;
- static final int ROTARY_STROKE_WIDTH_DIP = 83;
- static final int SNAP_BACK_ANIMATION_DURATION_MILLIS = 300;
- static final int SPIN_ANIMATION_DURATION_MILLIS = 800;
-
- private int mEdgeTriggerThresh;
- private int mDimpleWidth;
- private int mBackgroundWidth;
- private int mBackgroundHeight;
- private final int mOuterRadius;
- private final int mInnerRadius;
- private int mDimpleSpacing;
-
- private VelocityTracker mVelocityTracker;
- private int mMinimumVelocity;
- private int mMaximumVelocity;
-
- /**
- * The number of dimples we are flinging when we do the "spin" animation. Used to know when to
- * wrap the icons back around so they "rotate back" onto the screen.
- * @see #updateAnimation()
- */
- private int mDimplesOfFling = 0;
-
- /**
- * Either {@link #HORIZONTAL} or {@link #VERTICAL}.
- */
- private int mOrientation;
-
-
- public RotarySelector(Context context) {
- this(context, null);
- }
-
- /**
- * Constructor used when this widget is created from a layout file.
- */
- public RotarySelector(Context context, AttributeSet attrs) {
- super(context, attrs);
-
- TypedArray a =
- context.obtainStyledAttributes(attrs, R.styleable.RotarySelector);
- mOrientation = a.getInt(R.styleable.RotarySelector_orientation, HORIZONTAL);
- a.recycle();
-
- Resources r = getResources();
- mDensity = r.getDisplayMetrics().density;
- if (DBG) log("- Density: " + mDensity);
-
- // Assets (all are BitmapDrawables).
- mBackground = getBitmapFor(R.drawable.jog_dial_bg);
- mDimple = getBitmapFor(R.drawable.jog_dial_dimple);
- mDimpleDim = getBitmapFor(R.drawable.jog_dial_dimple_dim);
-
- mArrowLongLeft = getBitmapFor(R.drawable.jog_dial_arrow_long_left_green);
- mArrowLongRight = getBitmapFor(R.drawable.jog_dial_arrow_long_right_red);
- mArrowShortLeftAndRight = getBitmapFor(R.drawable.jog_dial_arrow_short_left_and_right);
-
- mInterpolator = new DecelerateInterpolator(1f);
-
- mEdgeTriggerThresh = (int) (mDensity * EDGE_TRIGGER_DIP);
-
- mDimpleWidth = mDimple.getWidth();
-
- mBackgroundWidth = mBackground.getWidth();
- mBackgroundHeight = mBackground.getHeight();
- mOuterRadius = (int) (mDensity * OUTER_ROTARY_RADIUS_DIP);
- mInnerRadius = (int) ((OUTER_ROTARY_RADIUS_DIP - ROTARY_STROKE_WIDTH_DIP) * mDensity);
-
- final ViewConfiguration configuration = ViewConfiguration.get(mContext);
- mMinimumVelocity = configuration.getScaledMinimumFlingVelocity() * 2;
- mMaximumVelocity = configuration.getScaledMaximumFlingVelocity();
- }
-
- private Bitmap getBitmapFor(int resId) {
- return BitmapFactory.decodeResource(getContext().getResources(), resId);
- }
-
- @Override
- protected void onSizeChanged(int w, int h, int oldw, int oldh) {
- super.onSizeChanged(w, h, oldw, oldh);
-
- final int edgePadding = (int) (EDGE_PADDING_DIP * mDensity);
- mLeftHandleX = edgePadding + mDimpleWidth / 2;
- final int length = isHoriz() ? w : h;
- mRightHandleX = length - edgePadding - mDimpleWidth / 2;
- mDimpleSpacing = (length / 2) - mLeftHandleX;
-
- // bg matrix only needs to be calculated once
- mBgMatrix.setTranslate(0, 0);
- if (!isHoriz()) {
- // set up matrix for translating drawing of background and arrow assets
- final int left = w - mBackgroundHeight;
- mBgMatrix.preRotate(-90, 0, 0);
- mBgMatrix.postTranslate(left, h);
-
- } else {
- mBgMatrix.postTranslate(0, h - mBackgroundHeight);
- }
- }
-
- private boolean isHoriz() {
- return mOrientation == HORIZONTAL;
- }
-
- /**
- * Sets the left handle icon to a given resource.
- *
- * The resource should refer to a Drawable object, or use 0 to remove
- * the icon.
- *
- * @param resId the resource ID.
- */
- public void setLeftHandleResource(int resId) {
- if (resId != 0) {
- mLeftHandleIcon = getBitmapFor(resId);
- }
- invalidate();
- }
-
- /**
- * Sets the right handle icon to a given resource.
- *
- * The resource should refer to a Drawable object, or use 0 to remove
- * the icon.
- *
- * @param resId the resource ID.
- */
- public void setRightHandleResource(int resId) {
- if (resId != 0) {
- mRightHandleIcon = getBitmapFor(resId);
- }
- invalidate();
- }
-
-
- @Override
- protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
- final int length = isHoriz() ?
- MeasureSpec.getSize(widthMeasureSpec) :
- MeasureSpec.getSize(heightMeasureSpec);
- final int arrowScrunch = (int) (ARROW_SCRUNCH_DIP * mDensity);
- final int arrowH = mArrowShortLeftAndRight.getHeight();
-
- // by making the height less than arrow + bg, arrow and bg will be scrunched together,
- // overlaying somewhat (though on transparent portions of the drawable).
- // this works because the arrows are drawn from the top, and the rotary bg is drawn
- // from the bottom.
- final int height = mBackgroundHeight + arrowH - arrowScrunch;
-
- if (isHoriz()) {
- setMeasuredDimension(length, height);
- } else {
- setMeasuredDimension(height, length);
- }
- }
-
- @Override
- protected void onDraw(Canvas canvas) {
- super.onDraw(canvas);
-
- final int width = getWidth();
-
- if (VISUAL_DEBUG) {
- // draw bounding box around widget
- mPaint.setColor(0xffff0000);
- mPaint.setStyle(Paint.Style.STROKE);
- canvas.drawRect(0, 0, width, getHeight(), mPaint);
- }
-
- final int height = getHeight();
-
- // update animating state before we draw anything
- if (mAnimating) {
- updateAnimation();
- }
-
- // Background:
- canvas.drawBitmap(mBackground, mBgMatrix, mPaint);
-
- // Draw the correct arrow(s) depending on the current state:
- mArrowMatrix.reset();
- switch (mGrabbedState) {
- case NOTHING_GRABBED:
- //mArrowShortLeftAndRight;
- break;
- case LEFT_HANDLE_GRABBED:
- mArrowMatrix.setTranslate(0, 0);
- if (!isHoriz()) {
- mArrowMatrix.preRotate(-90, 0, 0);
- mArrowMatrix.postTranslate(0, height);
- }
- canvas.drawBitmap(mArrowLongLeft, mArrowMatrix, mPaint);
- break;
- case RIGHT_HANDLE_GRABBED:
- mArrowMatrix.setTranslate(0, 0);
- if (!isHoriz()) {
- mArrowMatrix.preRotate(-90, 0, 0);
- // since bg width is > height of screen in landscape mode...
- mArrowMatrix.postTranslate(0, height + (mBackgroundWidth - height));
- }
- canvas.drawBitmap(mArrowLongRight, mArrowMatrix, mPaint);
- break;
- default:
- throw new IllegalStateException("invalid mGrabbedState: " + mGrabbedState);
- }
-
- final int bgHeight = mBackgroundHeight;
- final int bgTop = isHoriz() ?
- height - bgHeight:
- width - bgHeight;
-
- if (VISUAL_DEBUG) {
- // draw circle bounding arc drawable: good sanity check we're doing the math correctly
- float or = OUTER_ROTARY_RADIUS_DIP * mDensity;
- final int vOffset = mBackgroundWidth - height;
- final int midX = isHoriz() ? width / 2 : mBackgroundWidth / 2 - vOffset;
- if (isHoriz()) {
- canvas.drawCircle(midX, or + bgTop, or, mPaint);
- } else {
- canvas.drawCircle(or + bgTop, midX, or, mPaint);
- }
- }
-
- // left dimple / icon
- {
- final int xOffset = mLeftHandleX + mRotaryOffsetX;
- final int drawableY = getYOnArc(
- mBackgroundWidth,
- mInnerRadius,
- mOuterRadius,
- xOffset);
- final int x = isHoriz() ? xOffset : drawableY + bgTop;
- final int y = isHoriz() ? drawableY + bgTop : height - xOffset;
- if (mGrabbedState != RIGHT_HANDLE_GRABBED) {
- drawCentered(mDimple, canvas, x, y);
- drawCentered(mLeftHandleIcon, canvas, x, y);
- } else {
- drawCentered(mDimpleDim, canvas, x, y);
- }
- }
-
- // center dimple
- {
- final int xOffset = isHoriz() ?
- width / 2 + mRotaryOffsetX:
- height / 2 + mRotaryOffsetX;
- final int drawableY = getYOnArc(
- mBackgroundWidth,
- mInnerRadius,
- mOuterRadius,
- xOffset);
-
- if (isHoriz()) {
- drawCentered(mDimpleDim, canvas, xOffset, drawableY + bgTop);
- } else {
- // vertical
- drawCentered(mDimpleDim, canvas, drawableY + bgTop, height - xOffset);
- }
- }
-
- // right dimple / icon
- {
- final int xOffset = mRightHandleX + mRotaryOffsetX;
- final int drawableY = getYOnArc(
- mBackgroundWidth,
- mInnerRadius,
- mOuterRadius,
- xOffset);
-
- final int x = isHoriz() ? xOffset : drawableY + bgTop;
- final int y = isHoriz() ? drawableY + bgTop : height - xOffset;
- if (mGrabbedState != LEFT_HANDLE_GRABBED) {
- drawCentered(mDimple, canvas, x, y);
- drawCentered(mRightHandleIcon, canvas, x, y);
- } else {
- drawCentered(mDimpleDim, canvas, x, y);
- }
- }
-
- // draw extra left hand dimples
- int dimpleLeft = mRotaryOffsetX + mLeftHandleX - mDimpleSpacing;
- final int halfdimple = mDimpleWidth / 2;
- while (dimpleLeft > -halfdimple) {
- final int drawableY = getYOnArc(
- mBackgroundWidth,
- mInnerRadius,
- mOuterRadius,
- dimpleLeft);
-
- if (isHoriz()) {
- drawCentered(mDimpleDim, canvas, dimpleLeft, drawableY + bgTop);
- } else {
- drawCentered(mDimpleDim, canvas, drawableY + bgTop, height - dimpleLeft);
- }
- dimpleLeft -= mDimpleSpacing;
- }
-
- // draw extra right hand dimples
- int dimpleRight = mRotaryOffsetX + mRightHandleX + mDimpleSpacing;
- final int rightThresh = mRight + halfdimple;
- while (dimpleRight < rightThresh) {
- final int drawableY = getYOnArc(
- mBackgroundWidth,
- mInnerRadius,
- mOuterRadius,
- dimpleRight);
-
- if (isHoriz()) {
- drawCentered(mDimpleDim, canvas, dimpleRight, drawableY + bgTop);
- } else {
- drawCentered(mDimpleDim, canvas, drawableY + bgTop, height - dimpleRight);
- }
- dimpleRight += mDimpleSpacing;
- }
- }
-
- /**
- * Assuming bitmap is a bounding box around a piece of an arc drawn by two concentric circles
- * (as the background drawable for the rotary widget is), and given an x coordinate along the
- * drawable, return the y coordinate of a point on the arc that is between the two concentric
- * circles. The resulting y combined with the incoming x is a point along the circle in
- * between the two concentric circles.
- *
- * @param backgroundWidth The width of the asset (the bottom of the box surrounding the arc).
- * @param innerRadius The radius of the circle that intersects the drawable at the bottom two
- * corders of the drawable (top two corners in terms of drawing coordinates).
- * @param outerRadius The radius of the circle who's top most point is the top center of the
- * drawable (bottom center in terms of drawing coordinates).
- * @param x The distance along the x axis of the desired point. @return The y coordinate, in drawing coordinates, that will place (x, y) along the circle
- * in between the two concentric circles.
- */
- private int getYOnArc(int backgroundWidth, int innerRadius, int outerRadius, int x) {
-
- // the hypotenuse
- final int halfWidth = (outerRadius - innerRadius) / 2;
- final int middleRadius = innerRadius + halfWidth;
-
- // the bottom leg of the triangle
- final int triangleBottom = (backgroundWidth / 2) - x;
-
- // "Our offense is like the pythagorean theorem: There is no answer!" - Shaquille O'Neal
- final int triangleY =
- (int) Math.sqrt(middleRadius * middleRadius - triangleBottom * triangleBottom);
-
- // convert to drawing coordinates:
- // middleRadius - triangleY =
- // the vertical distance from the outer edge of the circle to the desired point
- // from there we add the distance from the top of the drawable to the middle circle
- return middleRadius - triangleY + halfWidth;
- }
-
- /**
- * Handle touch screen events.
- *
- * @param event The motion event.
- * @return True if the event was handled, false otherwise.
- */
- @Override
- public boolean onTouchEvent(MotionEvent event) {
- if (mAnimating) {
- return true;
- }
- if (mVelocityTracker == null) {
- mVelocityTracker = VelocityTracker.obtain();
- }
- mVelocityTracker.addMovement(event);
-
- final int height = getHeight();
-
- final int eventX = isHoriz() ?
- (int) event.getX():
- height - ((int) event.getY());
- final int hitWindow = mDimpleWidth;
-
- final int action = event.getAction();
- switch (action) {
- case MotionEvent.ACTION_DOWN:
- if (DBG) log("touch-down");
- mTriggered = false;
- if (mGrabbedState != NOTHING_GRABBED) {
- reset();
- invalidate();
- }
- if (eventX < mLeftHandleX + hitWindow) {
- mRotaryOffsetX = eventX - mLeftHandleX;
- setGrabbedState(LEFT_HANDLE_GRABBED);
- invalidate();
- vibrate(VIBRATE_SHORT);
- } else if (eventX > mRightHandleX - hitWindow) {
- mRotaryOffsetX = eventX - mRightHandleX;
- setGrabbedState(RIGHT_HANDLE_GRABBED);
- invalidate();
- vibrate(VIBRATE_SHORT);
- }
- break;
-
- case MotionEvent.ACTION_MOVE:
- if (DBG) log("touch-move");
- if (mGrabbedState == LEFT_HANDLE_GRABBED) {
- mRotaryOffsetX = eventX - mLeftHandleX;
- invalidate();
- final int rightThresh = isHoriz() ? getRight() : height;
- if (eventX >= rightThresh - mEdgeTriggerThresh && !mTriggered) {
- mTriggered = true;
- dispatchTriggerEvent(OnDialTriggerListener.LEFT_HANDLE);
- final VelocityTracker velocityTracker = mVelocityTracker;
- velocityTracker.computeCurrentVelocity(1000, mMaximumVelocity);
- final int rawVelocity = isHoriz() ?
- (int) velocityTracker.getXVelocity():
- -(int) velocityTracker.getYVelocity();
- final int velocity = Math.max(mMinimumVelocity, rawVelocity);
- mDimplesOfFling = Math.max(
- 8,
- Math.abs(velocity / mDimpleSpacing));
- startAnimationWithVelocity(
- eventX - mLeftHandleX,
- mDimplesOfFling * mDimpleSpacing,
- velocity);
- }
- } else if (mGrabbedState == RIGHT_HANDLE_GRABBED) {
- mRotaryOffsetX = eventX - mRightHandleX;
- invalidate();
- if (eventX <= mEdgeTriggerThresh && !mTriggered) {
- mTriggered = true;
- dispatchTriggerEvent(OnDialTriggerListener.RIGHT_HANDLE);
- final VelocityTracker velocityTracker = mVelocityTracker;
- velocityTracker.computeCurrentVelocity(1000, mMaximumVelocity);
- final int rawVelocity = isHoriz() ?
- (int) velocityTracker.getXVelocity():
- - (int) velocityTracker.getYVelocity();
- final int velocity = Math.min(-mMinimumVelocity, rawVelocity);
- mDimplesOfFling = Math.max(
- 8,
- Math.abs(velocity / mDimpleSpacing));
- startAnimationWithVelocity(
- eventX - mRightHandleX,
- -(mDimplesOfFling * mDimpleSpacing),
- velocity);
- }
- }
- break;
- case MotionEvent.ACTION_UP:
- if (DBG) log("touch-up");
- // handle animating back to start if they didn't trigger
- if (mGrabbedState == LEFT_HANDLE_GRABBED
- && Math.abs(eventX - mLeftHandleX) > 5) {
- // set up "snap back" animation
- startAnimation(eventX - mLeftHandleX, 0, SNAP_BACK_ANIMATION_DURATION_MILLIS);
- } else if (mGrabbedState == RIGHT_HANDLE_GRABBED
- && Math.abs(eventX - mRightHandleX) > 5) {
- // set up "snap back" animation
- startAnimation(eventX - mRightHandleX, 0, SNAP_BACK_ANIMATION_DURATION_MILLIS);
- }
- mRotaryOffsetX = 0;
- setGrabbedState(NOTHING_GRABBED);
- invalidate();
- if (mVelocityTracker != null) {
- mVelocityTracker.recycle(); // wishin' we had generational GC
- mVelocityTracker = null;
- }
- break;
- case MotionEvent.ACTION_CANCEL:
- if (DBG) log("touch-cancel");
- reset();
- invalidate();
- if (mVelocityTracker != null) {
- mVelocityTracker.recycle();
- mVelocityTracker = null;
- }
- break;
- }
- return true;
- }
-
- private void startAnimation(int startX, int endX, int duration) {
- mAnimating = true;
- mAnimationStartTime = currentAnimationTimeMillis();
- mAnimationDuration = duration;
- mAnimatingDeltaXStart = startX;
- mAnimatingDeltaXEnd = endX;
- setGrabbedState(NOTHING_GRABBED);
- mDimplesOfFling = 0;
- invalidate();
- }
-
- private void startAnimationWithVelocity(int startX, int endX, int pixelsPerSecond) {
- mAnimating = true;
- mAnimationStartTime = currentAnimationTimeMillis();
- mAnimationDuration = 1000 * (endX - startX) / pixelsPerSecond;
- mAnimatingDeltaXStart = startX;
- mAnimatingDeltaXEnd = endX;
- setGrabbedState(NOTHING_GRABBED);
- invalidate();
- }
-
- private void updateAnimation() {
- final long millisSoFar = currentAnimationTimeMillis() - mAnimationStartTime;
- final long millisLeft = mAnimationDuration - millisSoFar;
- final int totalDeltaX = mAnimatingDeltaXStart - mAnimatingDeltaXEnd;
- final boolean goingRight = totalDeltaX < 0;
- if (DBG) log("millisleft for animating: " + millisLeft);
- if (millisLeft <= 0) {
- reset();
- return;
- }
- // from 0 to 1 as animation progresses
- float interpolation =
- mInterpolator.getInterpolation((float) millisSoFar / mAnimationDuration);
- final int dx = (int) (totalDeltaX * (1 - interpolation));
- mRotaryOffsetX = mAnimatingDeltaXEnd + dx;
-
- // once we have gone far enough to animate the current buttons off screen, we start
- // wrapping the offset back to the other side so that when the animation is finished,
- // the buttons will come back into their original places.
- if (mDimplesOfFling > 0) {
- if (!goingRight && mRotaryOffsetX < -3 * mDimpleSpacing) {
- // wrap around on fling left
- mRotaryOffsetX += mDimplesOfFling * mDimpleSpacing;
- } else if (goingRight && mRotaryOffsetX > 3 * mDimpleSpacing) {
- // wrap around on fling right
- mRotaryOffsetX -= mDimplesOfFling * mDimpleSpacing;
- }
- }
- invalidate();
- }
-
- private void reset() {
- mAnimating = false;
- mRotaryOffsetX = 0;
- mDimplesOfFling = 0;
- setGrabbedState(NOTHING_GRABBED);
- mTriggered = false;
- }
-
- /**
- * Triggers haptic feedback.
- */
- private synchronized void vibrate(long duration) {
- final boolean hapticEnabled = Settings.System.getIntForUser(
- mContext.getContentResolver(), Settings.System.HAPTIC_FEEDBACK_ENABLED, 1,
- UserHandle.USER_CURRENT) != 0;
- if (hapticEnabled) {
- if (mVibrator == null) {
- mVibrator = (android.os.Vibrator) getContext()
- .getSystemService(Context.VIBRATOR_SERVICE);
- }
- mVibrator.vibrate(duration, VIBRATION_ATTRIBUTES);
- }
- }
-
- /**
- * Draw the bitmap so that it's centered
- * on the point (x,y), then draws it using specified canvas.
- * TODO: is there already a utility method somewhere for this?
- */
- private void drawCentered(Bitmap d, Canvas c, int x, int y) {
- int w = d.getWidth();
- int h = d.getHeight();
-
- c.drawBitmap(d, x - (w / 2), y - (h / 2), mPaint);
- }
-
-
- /**
- * Registers a callback to be invoked when the dial
- * is "triggered" by rotating it one way or the other.
- *
- * @param l the OnDialTriggerListener to attach to this view
- */
- public void setOnDialTriggerListener(OnDialTriggerListener l) {
- mOnDialTriggerListener = l;
- }
-
- /**
- * Dispatches a trigger event to our listener.
- */
- private void dispatchTriggerEvent(int whichHandle) {
- vibrate(VIBRATE_LONG);
- if (mOnDialTriggerListener != null) {
- mOnDialTriggerListener.onDialTrigger(this, whichHandle);
- }
- }
-
- /**
- * Sets the current grabbed state, and dispatches a grabbed state change
- * event to our listener.
- */
- private void setGrabbedState(int newState) {
- if (newState != mGrabbedState) {
- mGrabbedState = newState;
- if (mOnDialTriggerListener != null) {
- mOnDialTriggerListener.onGrabbedStateChange(this, mGrabbedState);
- }
- }
- }
-
- /**
- * Interface definition for a callback to be invoked when the dial
- * is "triggered" by rotating it one way or the other.
- */
- public interface OnDialTriggerListener {
- /**
- * The dial was triggered because the user grabbed the left handle,
- * and rotated the dial clockwise.
- */
- public static final int LEFT_HANDLE = 1;
-
- /**
- * The dial was triggered because the user grabbed the right handle,
- * and rotated the dial counterclockwise.
- */
- public static final int RIGHT_HANDLE = 2;
-
- /**
- * Called when the dial is triggered.
- *
- * @param v The view that was triggered
- * @param whichHandle Which "dial handle" the user grabbed,
- * either {@link #LEFT_HANDLE}, {@link #RIGHT_HANDLE}.
- */
- void onDialTrigger(View v, int whichHandle);
-
- /**
- * Called when the "grabbed state" changes (i.e. when
- * the user either grabs or releases one of the handles.)
- *
- * @param v the view that was triggered
- * @param grabbedState the new state: either {@link #NOTHING_GRABBED},
- * {@link #LEFT_HANDLE_GRABBED}, or {@link #RIGHT_HANDLE_GRABBED}.
- */
- void onGrabbedStateChange(View v, int grabbedState);
- }
-
-
- // Debugging / testing code
-
- private void log(String msg) {
- Log.d(LOG_TAG, msg);
- }
-}
diff --git a/core/jni/Android.mk b/core/jni/Android.mk
index a1f8e92961d6..68034f65c39a 100644
--- a/core/jni/Android.mk
+++ b/core/jni/Android.mk
@@ -82,6 +82,7 @@ LOCAL_SRC_FILES:= \
android_text_AndroidBidi.cpp \
android_text_StaticLayout.cpp \
android_os_Debug.cpp \
+ android_os_GraphicsEnvironment.cpp \
android_os_HwBinder.cpp \
android_os_HwBlob.cpp \
android_os_HwParcel.cpp \
diff --git a/core/jni/AndroidRuntime.cpp b/core/jni/AndroidRuntime.cpp
index c195cfe23bfb..fb5d0373725a 100644
--- a/core/jni/AndroidRuntime.cpp
+++ b/core/jni/AndroidRuntime.cpp
@@ -150,6 +150,7 @@ extern int register_android_database_SQLiteGlobal(JNIEnv* env);
extern int register_android_database_SQLiteDebug(JNIEnv* env);
extern int register_android_nio_utils(JNIEnv* env);
extern int register_android_os_Debug(JNIEnv* env);
+extern int register_android_os_GraphicsEnvironment(JNIEnv* env);
extern int register_android_os_HwBinder(JNIEnv *env);
extern int register_android_os_HwBlob(JNIEnv *env);
extern int register_android_os_HwParcel(JNIEnv *env);
@@ -1355,6 +1356,7 @@ static const RegJNIRec gRegJNI[] = {
REG_JNI(register_android_database_SQLiteDebug),
REG_JNI(register_android_os_Debug),
REG_JNI(register_android_os_FileObserver),
+ REG_JNI(register_android_os_GraphicsEnvironment),
REG_JNI(register_android_os_MessageQueue),
REG_JNI(register_android_os_SELinux),
REG_JNI(register_android_os_Trace),
diff --git a/core/jni/android_os_GraphicsEnvironment.cpp b/core/jni/android_os_GraphicsEnvironment.cpp
new file mode 100644
index 000000000000..905a85adc551
--- /dev/null
+++ b/core/jni/android_os_GraphicsEnvironment.cpp
@@ -0,0 +1,44 @@
+/*
+ * Copyright 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.
+ */
+
+#define LOG_TAG "GraphicsEnvironment"
+
+#include <gui/GraphicsEnv.h>
+#include <nativehelper/ScopedUtfChars.h>
+#include "core_jni_helpers.h"
+
+namespace {
+
+void setDriverPath(JNIEnv* env, jobject clazz, jstring path) {
+ ScopedUtfChars pathChars(env, path);
+ android::GraphicsEnv::getInstance().setDriverPath(pathChars.c_str());
+}
+
+const JNINativeMethod g_methods[] = {
+ { "setDriverPath", "(Ljava/lang/String;)V", reinterpret_cast<void*>(setDriverPath) },
+};
+
+const char* const kGraphicsEnvironmentName = "android/os/GraphicsEnvironment";
+
+} // anonymous namespace
+
+namespace android {
+
+int register_android_os_GraphicsEnvironment(JNIEnv* env) {
+ return RegisterMethodsOrDie(env, kGraphicsEnvironmentName, g_methods, NELEM(g_methods));
+}
+
+} // namespace android
diff --git a/core/jni/android_os_HwBinder.h b/core/jni/android_os_HwBinder.h
index 2ebc38164da8..fa8fe01d6e93 100644
--- a/core/jni/android_os_HwBinder.h
+++ b/core/jni/android_os_HwBinder.h
@@ -24,7 +24,7 @@
namespace android {
-struct JHwBinder : public hardware::BBinder {
+struct JHwBinder : public hardware::BHwBinder {
static void InitClass(JNIEnv *env);
static sp<JHwBinder> SetNativeContext(
diff --git a/core/res/res/values/attrs.xml b/core/res/res/values/attrs.xml
index e171cce93642..dd33718dfb2a 100644
--- a/core/res/res/values/attrs.xml
+++ b/core/res/res/values/attrs.xml
@@ -3178,6 +3178,18 @@
and subtype in order to provide the consistent user experience in switching
between IMEs and subtypes. -->
<attr name="supportsSwitchingToNextInputMethod" format="boolean" />
+ <!-- Set to true if this input method supports ways to dismiss the windows assigned to
+ the input method (e.g. a dismiss button rendered by the input method itself). The
+ System UI may optimize the UI by not showing system-level dismiss button if this
+ value is true.
+ <p> Must be a boolean value, either "true" or "false". The default value is "false".
+ <p> This may also be a reference to a resource (in the form "@[package:]type:name")
+ or theme attribute (in the form "?[package:]type:name") containing a value of this
+ type.
+ <p> A UI element that dismisses the input method window should report
+ {@link android.view.accessibility.AccessibilityNodeInfo#ACTION_DISMISS} action, so
+ that accessibility services can handle it accordingly. -->
+ <attr name="supportsDismissingWindow" format="boolean" />
</declare-styleable>
<!-- This is the subtype of InputMethod. Subtype can describe locales (e.g. en_US, fr_FR...)
@@ -5153,13 +5165,6 @@
<attr name="layout_scale" format="float" />
</declare-styleable>
- <!-- attributes for internal rotary widget used in lock screen and phone app
- @hide -->
- <declare-styleable name="RotarySelector">
- <!-- Use "horizontal" or "vertical". The default is horizontal. -->
- <attr name="orientation" />
- </declare-styleable>
-
<!-- @hide -->
<declare-styleable name="WeightedLinearLayout">
<attr name="majorWeightMin" format="float" />
diff --git a/core/res/res/values/public.xml b/core/res/res/values/public.xml
index 73cba89a32d2..064d31e84cca 100644
--- a/core/res/res/values/public.xml
+++ b/core/res/res/values/public.xml
@@ -2784,6 +2784,7 @@
<public name="focusedByDefault" />
<public name="appCategory" />
<public name="autoSizeMaxTextSize" />
+ <public name="supportsDismissingWindow" />
</public-group>
<public-group type="style" first-id="0x010302e0">
diff --git a/core/tests/coretests/res/xml/ime_meta.xml b/core/tests/coretests/res/xml/ime_meta.xml
new file mode 100644
index 000000000000..a975718c50e9
--- /dev/null
+++ b/core/tests/coretests/res/xml/ime_meta.xml
@@ -0,0 +1,27 @@
+<?xml version="1.0" encoding="utf-8"?>
+
+<!--
+ Copyright (C) 2017 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+
+<input-method
+ xmlns:android="http://schemas.android.com/apk/res/android"
+ android:settingsActivity="com.android.inputmethod.latin.settings.SettingsActivity"
+>
+ <subtype
+ android:label="subtype1"
+ android:imeSubtypeLocale="en_US"
+ android:imeSubtypeMode="keyboard" />
+</input-method>
diff --git a/core/tests/coretests/res/xml/ime_meta_dismiss.xml b/core/tests/coretests/res/xml/ime_meta_dismiss.xml
new file mode 100644
index 000000000000..59f8ecc278ad
--- /dev/null
+++ b/core/tests/coretests/res/xml/ime_meta_dismiss.xml
@@ -0,0 +1,28 @@
+<?xml version="1.0" encoding="utf-8"?>
+
+<!--
+ Copyright (C) 2017 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+
+<input-method
+ xmlns:android="http://schemas.android.com/apk/res/android"
+ android:settingsActivity="com.android.inputmethod.latin.settings.SettingsActivity"
+ android:supportsDismissingWindow="true"
+>
+ <subtype
+ android:label="subtype1"
+ android:imeSubtypeLocale="en_US"
+ android:imeSubtypeMode="keyboard" />
+</input-method>
diff --git a/core/tests/coretests/res/xml/ime_meta_sw_next.xml b/core/tests/coretests/res/xml/ime_meta_sw_next.xml
new file mode 100644
index 000000000000..2e2ee33e9933
--- /dev/null
+++ b/core/tests/coretests/res/xml/ime_meta_sw_next.xml
@@ -0,0 +1,28 @@
+<?xml version="1.0" encoding="utf-8"?>
+
+<!--
+ Copyright (C) 2017 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+
+<input-method
+ xmlns:android="http://schemas.android.com/apk/res/android"
+ android:settingsActivity="com.android.inputmethod.latin.settings.SettingsActivity"
+ android:supportsSwitchingToNextInputMethod="true"
+>
+ <subtype
+ android:label="subtype1"
+ android:imeSubtypeLocale="en_US"
+ android:imeSubtypeMode="keyboard" />
+</input-method>
diff --git a/core/tests/coretests/src/android/view/inputmethod/InputMethodInfoTest.java b/core/tests/coretests/src/android/view/inputmethod/InputMethodInfoTest.java
new file mode 100644
index 000000000000..23dc80f97b61
--- /dev/null
+++ b/core/tests/coretests/src/android/view/inputmethod/InputMethodInfoTest.java
@@ -0,0 +1,111 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.view.inputmethod;
+
+import static org.hamcrest.CoreMatchers.equalTo;
+import static org.hamcrest.CoreMatchers.is;
+import static org.junit.Assert.assertThat;
+
+import android.annotation.XmlRes;
+import android.content.Context;
+import android.content.pm.ResolveInfo;
+import android.content.pm.ServiceInfo;
+import android.os.Bundle;
+import android.os.Parcel;
+import android.support.test.InstrumentationRegistry;
+import android.support.test.runner.AndroidJUnit4;
+
+import com.android.frameworks.coretests.R;
+
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+@RunWith(AndroidJUnit4.class)
+public class InputMethodInfoTest {
+
+ @Test
+ public void testEqualsAndHashCode() throws Exception {
+ final InputMethodInfo imi = buildInputMethodForTest(R.xml.ime_meta);
+ final InputMethodInfo clone = cloneViaParcel(imi);
+
+ assertThat(clone.equals(imi), is(true));
+ assertThat(clone.hashCode(), equalTo(imi.hashCode()));
+ }
+
+ @Test
+ public void testBooleanAttributes_DefaultValues() throws Exception {
+ final InputMethodInfo imi = buildInputMethodForTest(R.xml.ime_meta);
+
+ assertThat(imi.supportsSwitchingToNextInputMethod(), is(false));
+ assertThat(imi.supportsDismissingWindow(), is(false));
+
+ final InputMethodInfo clone = cloneViaParcel(imi);
+
+ assertThat(clone.supportsSwitchingToNextInputMethod(), is(false));
+ assertThat(clone.supportsDismissingWindow(), is(false));
+ }
+
+ @Test
+ public void testSupportsSwitchingToNextInputMethod() throws Exception {
+ final InputMethodInfo imi = buildInputMethodForTest(R.xml.ime_meta_sw_next);
+
+ assertThat(imi.supportsSwitchingToNextInputMethod(), is(true));
+
+ final InputMethodInfo clone = cloneViaParcel(imi);
+
+ assertThat(clone.supportsSwitchingToNextInputMethod(), is(true));
+ }
+
+ @Test
+ public void testSupportsDismissingWindow() throws Exception {
+ final InputMethodInfo imi = buildInputMethodForTest(R.xml.ime_meta_dismiss);
+
+ assertThat(imi.supportsDismissingWindow(), is(true));
+
+ final InputMethodInfo clone = cloneViaParcel(imi);
+
+ assertThat(clone.supportsDismissingWindow(), is(true));
+ }
+
+ private InputMethodInfo buildInputMethodForTest(final @XmlRes int metaDataRes)
+ throws Exception {
+ final Context context = InstrumentationRegistry.getContext();
+ final ServiceInfo serviceInfo = new ServiceInfo();
+ serviceInfo.applicationInfo = context.getApplicationInfo();
+ serviceInfo.packageName = context.getPackageName();
+ serviceInfo.name = "DummyImeForTest";
+ serviceInfo.metaData = new Bundle();
+ serviceInfo.metaData.putInt(InputMethod.SERVICE_META_DATA, metaDataRes);
+ final ResolveInfo resolveInfo = new ResolveInfo();
+ resolveInfo.serviceInfo = serviceInfo;
+ return new InputMethodInfo(context, resolveInfo, null /* additionalSubtypesMap */);
+ }
+
+ private InputMethodInfo cloneViaParcel(final InputMethodInfo original) {
+ Parcel parcel = null;
+ try {
+ parcel = Parcel.obtain();
+ original.writeToParcel(parcel, 0);
+ parcel.setDataPosition(0);
+ return InputMethodInfo.CREATOR.createFromParcel(parcel);
+ } finally {
+ if (parcel != null) {
+ parcel.recycle();
+ }
+ }
+ }
+}
diff --git a/core/tests/coretests/src/com/android/internal/app/ChooserActivityTest.java b/core/tests/coretests/src/com/android/internal/app/ChooserActivityTest.java
index 8385b69f091d..aab4698fc49a 100644
--- a/core/tests/coretests/src/com/android/internal/app/ChooserActivityTest.java
+++ b/core/tests/coretests/src/com/android/internal/app/ChooserActivityTest.java
@@ -124,7 +124,7 @@ public class ChooserActivityTest {
}
@Test
- public void updateChooserCountsAfterUserSelection() throws InterruptedException {
+ public void updateChooserCountsAndModelAfterUserSelection() throws InterruptedException {
Intent sendIntent = createSendImageIntent();
List<ResolvedComponentInfo> resolvedComponentInfos = createResolvedComponentsForTest(2);
@@ -142,19 +142,15 @@ public class ChooserActivityTest {
sOverrides.onSafelyStartCallback = targetInfo -> {
return true;
};
- String action = sendIntent.getAction();
- String annotation = sendIntent.getType();
ResolveInfo toChoose = resolvedComponentInfos.get(0).getResolveInfoAt(0);
- String packageName = toChoose.activityInfo.packageName;
- long toChooseCount = getCount(usm, packageName, action, annotation);
onView(withText(toChoose.activityInfo.name))
.perform(click());
waitForIdle();
verify(sOverrides.resolverListController, times(1))
+ .updateChooserCounts(Mockito.anyString(), Mockito.anyInt(), Mockito.anyString());
+ verify(sOverrides.resolverListController, times(1))
.updateModel(toChoose.activityInfo.getComponentName());
assertThat(activity.getIsSelected(), is(true));
- long updatedCount = getCount(usm, packageName, action, annotation);
- assertThat(updatedCount, is(toChooseCount + 1l));
}
@Test
diff --git a/core/tests/coretests/src/com/android/internal/inputmethod/InputMethodSubtypeSwitchingControllerTest.java b/core/tests/coretests/src/com/android/internal/inputmethod/InputMethodSubtypeSwitchingControllerTest.java
index ba5206adeb77..34c34d7c07bb 100644
--- a/core/tests/coretests/src/com/android/internal/inputmethod/InputMethodSubtypeSwitchingControllerTest.java
+++ b/core/tests/coretests/src/com/android/internal/inputmethod/InputMethodSubtypeSwitchingControllerTest.java
@@ -74,7 +74,8 @@ public class InputMethodSubtypeSwitchingControllerTest extends InstrumentationTe
}
final InputMethodInfo imi = new InputMethodInfo(ri, DUMMY_IS_AUX_IME,
DUMMY_SETTING_ACTIVITY_NAME, subtypes, DUMMY_IS_DEFAULT_RES_ID,
- DUMMY_FORCE_DEFAULT, supportsSwitchingToNextInputMethod);
+ DUMMY_FORCE_DEFAULT, supportsSwitchingToNextInputMethod,
+ false /* supportsDismissingWindow */);
if (subtypes == null) {
items.add(new ImeSubtypeListItem(imeName, null /* variableName */, imi,
NOT_A_SUBTYPE_ID, null, SYSTEM_LOCALE));
diff --git a/location/java/android/location/GnssMeasurement.java b/location/java/android/location/GnssMeasurement.java
index 70339005581f..d2873e968e33 100644
--- a/location/java/android/location/GnssMeasurement.java
+++ b/location/java/android/location/GnssMeasurement.java
@@ -47,6 +47,7 @@ public final class GnssMeasurement implements Parcelable {
private double mCarrierPhaseUncertainty;
private int mMultipathIndicator;
private double mSnrInDb;
+ private double mAgcLevelDb;
// The following enumerations must be in sync with the values declared in gps.h
@@ -56,6 +57,7 @@ public final class GnssMeasurement implements Parcelable {
private static final int HAS_CARRIER_CYCLES = (1<<10);
private static final int HAS_CARRIER_PHASE = (1<<11);
private static final int HAS_CARRIER_PHASE_UNCERTAINTY = (1<<12);
+ private static final int HAS_AUTOMATIC_GAIN_CONTROL = (1<<13);
/**
* The status of the multipath indicator.
@@ -111,6 +113,20 @@ public final class GnssMeasurement implements Parcelable {
public static final int STATE_GAL_E1B_PAGE_SYNC = (1<<12);
/** This SBAS measurement's tracking state has whole second level sync. */
public static final int STATE_SBAS_SYNC = (1<<13);
+ /**
+ * This GNSS measurement's tracking state has time-of-week known, possibly not decoded
+ * over the air but has been determined from other sources. If TOW decoded is set then TOW Known
+ * will also be set.
+ * @hide
+ */
+ public static final int STATE_TOW_KNOWN = (1<<14);
+ /**
+ * This Glonass measurement's tracking state has time-of-day known, possibly not decoded
+ * over the air but has been determined from other sources. If TOD decoded is set then TOD Known
+ * will also be set.
+ * @hide
+ */
+ public static final int STATE_GLO_TOD_KNOWN = (1<<15);
/**
* All the GNSS receiver state flags, for bit masking purposes (not a sensible state for any
@@ -180,6 +196,7 @@ public final class GnssMeasurement implements Parcelable {
mCarrierPhaseUncertainty = measurement.mCarrierPhaseUncertainty;
mMultipathIndicator = measurement.mMultipathIndicator;
mSnrInDb = measurement.mSnrInDb;
+ mAgcLevelDb = measurement.mAgcLevelDb;
}
/**
@@ -299,6 +316,9 @@ public final class GnssMeasurement implements Parcelable {
if ((mState & STATE_TOW_DECODED) != 0) {
builder.append("TowDecoded|");
}
+ if ((mState & STATE_TOW_KNOWN) != 0) {
+ builder.append("TowKnown|");
+ }
if ((mState & STATE_MSEC_AMBIGUOUS) != 0) {
builder.append("MsecAmbiguous|");
}
@@ -311,6 +331,9 @@ public final class GnssMeasurement implements Parcelable {
if ((mState & STATE_GLO_TOD_DECODED) != 0) {
builder.append("GloTodDecoded|");
}
+ if ((mState & STATE_GLO_TOD_KNOWN) != 0) {
+ builder.append("GloTodKnown|");
+ }
if ((mState & STATE_BDS_D2_BIT_SYNC) != 0) {
builder.append("BdsD2BitSync|");
}
@@ -356,10 +379,14 @@ public final class GnssMeasurement implements Parcelable {
* C/A code lock : [ 0 1ms ] : STATE_CODE_LOCK is set
* Bit sync : [ 0 20ms ] : STATE_BIT_SYNC is set
* Subframe sync : [ 0 6s ] : STATE_SUBFRAME_SYNC is set
- * TOW decoded : [ 0 1week ] : STATE_TOW_DECODED is set</pre>
+ * TOW decoded : [ 0 1week ] : STATE_TOW_DECODED is set
+ * TOW Known : [ 0 1week ] : STATE_TOW_KNOWN set</pre>
+ *
+ * Note: TOW Known refers to the case where TOW is possibly not decoded over the air but has
+ * been determined from other sources. If TOW decoded is set then TOW Known must also be set.
*
* <p>Note well: if there is any ambiguity in integer millisecond, {@code STATE_MSEC_AMBIGUOUS}
- * should be set accordingly, in the 'state' field.
+ * must be set accordingly, in the 'state' field.
*
* <p>This value must be populated if 'state' != {@code STATE_UNKNOWN}.
*
@@ -371,12 +398,17 @@ public final class GnssMeasurement implements Parcelable {
* <p>Given the highest sync state that can be achieved, per each satellite, valid range for
* this field can be:
* <pre>
- * Searching : [ 0 ] : STATE_UNKNOWN
- * C/A code lock : [ 0 1ms ] : STATE_CODE_LOCK is set
- * Symbol sync : [ 0 10ms ] : STATE_SYMBOL_SYNC is set
- * Bit sync : [ 0 20ms ] : STATE_BIT_SYNC is set
- * String sync : [ 0 2s ] : STATE_GLO_STRING_SYNC is set
- * Time of day : [ 0 1day ] : STATE_GLO_TOD_DECODED is set</pre>
+ * Searching : [ 0 ] : STATE_UNKNOWN
+ * C/A code lock : [ 0 1ms ] : STATE_CODE_LOCK is set
+ * Symbol sync : [ 0 10ms ] : STATE_SYMBOL_SYNC is set
+ * Bit sync : [ 0 20ms ] : STATE_BIT_SYNC is set
+ * String sync : [ 0 2s ] : STATE_GLO_STRING_SYNC is set
+ * Time of day decoded : [ 0 1day ] : STATE_GLO_TOD_DECODED is set
+ * Time of day known : [ 0 1day ] : STATE_GLO_TOD_KNOWN set</pre>
+ *
+ * Note: Time of day known refers to the case where it is possibly not decoded over the air but
+ * has been determined from other sources. If Time of day decoded is set then Time of day known
+ * must also be set.
*
* <p>For Beidou, this is:
* <ul>
@@ -386,23 +418,31 @@ public final class GnssMeasurement implements Parcelable {
* <p>Given the highest sync state that can be achieved, per each satellite, valid range for
* this field can be:
* <pre>
- * Searching : [ 0 ] : STATE_UNKNOWN
- * C/A code lock : [ 0 1ms ] : STATE_CODE_LOCK is set
- * Bit sync (D2) : [ 0 2ms ] : STATE_BDS_D2_BIT_SYNC is set
- * Bit sync (D1) : [ 0 20ms ] : STATE_BIT_SYNC is set
- * Subframe (D2) : [ 0 0.6s ] : STATE_BDS_D2_SUBFRAME_SYNC is set
- * Subframe (D1) : [ 0 6s ] : STATE_SUBFRAME_SYNC is set
- * Time of week : [ 0 1week ] : STATE_TOW_DECODED is set</pre>
+ * Searching : [ 0 ] : STATE_UNKNOWN
+ * C/A code lock : [ 0 1ms ] : STATE_CODE_LOCK is set
+ * Bit sync (D2) : [ 0 2ms ] : STATE_BDS_D2_BIT_SYNC is set
+ * Bit sync (D1) : [ 0 20ms ] : STATE_BIT_SYNC is set
+ * Subframe (D2) : [ 0 0.6s ] : STATE_BDS_D2_SUBFRAME_SYNC is set
+ * Subframe (D1) : [ 0 6s ] : STATE_SUBFRAME_SYNC is set
+ * Time of week decoded : [ 0 1week ] : STATE_TOW_DECODED is set
+ * Time of week known : [ 0 1week ] : STATE_TOW_KNOWN set</pre>
+ *
+ * Note: TOW Known refers to the case where TOW is possibly not decoded over the air but has
+ * been determined from other sources. If TOW decoded is set then TOW Known must also be set.
*
* <p>For Galileo, this is:
* <ul>
* <li>Received Galileo time of week, at the measurement time in nanoseconds.</li>
* </ul>
* <pre>
- * E1BC code lock : [ 0 4ms ] : STATE_GAL_E1BC_CODE_LOCK is set
- * E1C 2nd code lock: [ 0 100ms ] : STATE_GAL_E1C_2ND_CODE_LOCK is set
- * E1B page : [ 0 2s ] : STATE_GAL_E1B_PAGE_SYNC is set
- * Time of week : [ 0 1week ] : STATE_GAL_TOW_DECODED is set</pre>
+ * E1BC code lock : [ 0 4ms ] : STATE_GAL_E1BC_CODE_LOCK is set
+ * E1C 2nd code lock : [ 0 100ms ] : STATE_GAL_E1C_2ND_CODE_LOCK is set
+ * E1B page : [ 0 2s ] : STATE_GAL_E1B_PAGE_SYNC is set
+ * Time of week decoded : [ 0 1week ] : STATE_GAL_TOW_DECODED is set
+ * Time of week known : [ 0 1week ] : STATE_GAL_TOW_KNOWN set</pre>
+ *
+ * Note: TOW Known refers to the case where TOW is possibly not decoded over the air but has
+ * been determined from other sources. If TOW decoded is set then TOW Known must also be set.
*
* <p>For SBAS, this is:
* <ul>
@@ -620,10 +660,10 @@ public final class GnssMeasurement implements Parcelable {
}
/**
- * Gets the carrier frequency at which codes and messages are modulated.
+ * Gets the carrier frequency of the tracked signal.
*
- * <p>For GPS, e.g., it can be L1 or L2. If the field is not set, it is the primary common use
- * frequency, e.g. L1 for GPS.
+ * <p>For example it can be the GPS L1 = 1.57542e9 Hz, or L2, L5, varying GLO channels, etc. If
+ * the field is not set, it is the primary common use frequency, e.g. L1 for GPS.
*
* <p>The value is only available if {@link #hasCarrierFrequencyHz()} is {@code true}.
*/
@@ -632,7 +672,7 @@ public final class GnssMeasurement implements Parcelable {
}
/**
- * Sets the Carrier frequency (L1 or L2) in Hz.
+ * Sets the Carrier frequency in Hz.
* @hide
*/
@TestApi
@@ -642,7 +682,7 @@ public final class GnssMeasurement implements Parcelable {
}
/**
- * Resets the Carrier frequency (L1 or L2) in Hz.
+ * Resets the Carrier frequency in Hz.
* @hide
*/
@TestApi
@@ -843,6 +883,51 @@ public final class GnssMeasurement implements Parcelable {
mSnrInDb = Double.NaN;
}
+ /**
+ * Returns {@code true} if {@link #getAgcLevelDb()} is available, {@code false} otherwise.
+ * @hide
+ */
+ public boolean hasAgcLevelDb() {
+ return isFlagSet(HAS_AUTOMATIC_GAIN_CONTROL);
+ }
+
+ /**
+ * Gets the Automatic Gain Control level in dB.
+ *
+ * <p> AGC acts as a variable gain amplifier adjusting the power of the incoming signal to
+ * minimize the quantization losses. The AGC level may be used to indicate potential
+ * interference. When AGC is at a nominal level, this value must be set as 0. Higher gain
+ * (and/or lower input power) shall be output as a positive number. Hence in cases of strong
+ * jamming, in the band of this signal, this value will go more negative.
+ * <p>Note: Different hardware designs (e.g. antenna, pre-amplification, or other RF HW
+ * components) may also affect the typical output of of this value on any given hardware design
+ * in an open sky test - the important aspect of this output is that changes in this value are
+ * indicative of changes on input signal power in the frequency band for this measurement.
+ * <p>The value is only available if {@link #hasAgcLevelDb()} is {@code true}.
+ * @hide
+ */
+ public double getAgcLevelDb() {
+ return mAgcLevelDb;
+ }
+
+ /**
+ * Sets the Automatic Gain Control level in dB.
+ * @hide
+ */
+ public void setAgcLevelDb(double agcLevelDb) {
+ setFlag(HAS_AUTOMATIC_GAIN_CONTROL);
+ mAgcLevelDb = agcLevelDb;
+ }
+
+ /**
+ * Resets the Automatic Gain Control level.
+ * @hide
+ */
+ public void resetAgcLevel() {
+ resetFlag(HAS_AUTOMATIC_GAIN_CONTROL);
+ mAgcLevelDb = Double.NaN;
+ }
+
public static final Creator<GnssMeasurement> CREATOR = new Creator<GnssMeasurement>() {
@Override
public GnssMeasurement createFromParcel(Parcel parcel) {
@@ -867,6 +952,7 @@ public final class GnssMeasurement implements Parcelable {
gnssMeasurement.mCarrierPhaseUncertainty = parcel.readDouble();
gnssMeasurement.mMultipathIndicator = parcel.readInt();
gnssMeasurement.mSnrInDb = parcel.readDouble();
+ gnssMeasurement.mAgcLevelDb = parcel.readDouble();
return gnssMeasurement;
}
@@ -898,6 +984,7 @@ public final class GnssMeasurement implements Parcelable {
parcel.writeDouble(mCarrierPhaseUncertainty);
parcel.writeInt(mMultipathIndicator);
parcel.writeDouble(mSnrInDb);
+ parcel.writeDouble(mAgcLevelDb);
}
@Override
@@ -968,6 +1055,10 @@ public final class GnssMeasurement implements Parcelable {
format,
"SnrInDb",
hasSnrInDb() ? mSnrInDb : null));
+ builder.append(String.format(
+ format,
+ "AgcLevelDb",
+ hasAgcLevelDb() ? mAgcLevelDb : null));
return builder.toString();
}
@@ -991,6 +1082,7 @@ public final class GnssMeasurement implements Parcelable {
resetCarrierPhaseUncertainty();
setMultipathIndicator(MULTIPATH_INDICATOR_UNKNOWN);
resetSnrInDb();
+ resetAgcLevel();
}
private void setFlag(int flag) {
diff --git a/location/java/android/location/GnssStatus.java b/location/java/android/location/GnssStatus.java
index f10514427455..6565042cc95f 100644
--- a/location/java/android/location/GnssStatus.java
+++ b/location/java/android/location/GnssStatus.java
@@ -51,6 +51,8 @@ public final class GnssStatus {
public static final int GNSS_SV_FLAGS_HAS_ALMANAC_DATA = (1 << 1);
/** @hide */
public static final int GNSS_SV_FLAGS_USED_IN_FIX = (1 << 2);
+ /** @hide */
+ public static final int GNSS_SV_FLAGS_HAS_CARRIER_FREQUENCY = (1 << 3);
/** @hide */
public static final int SVID_SHIFT_WIDTH = 7;
@@ -101,14 +103,16 @@ public final class GnssStatus {
/* package */ float[] mElevations;
/* package */ float[] mAzimuths;
/* package */ int mSvCount;
+ /* package */ float[] mCarrierFrequencies;
GnssStatus(int svCount, int[] svidWithFlags, float[] cn0s, float[] elevations,
- float[] azimuths) {
+ float[] azimuths, float[] carrierFrequencies) {
mSvCount = svCount;
mSvidWithFlags = svidWithFlags;
mCn0DbHz = cn0s;
mElevations = elevations;
mAzimuths = azimuths;
+ mCarrierFrequencies = carrierFrequencies;
}
/**
@@ -213,4 +217,28 @@ public final class GnssStatus {
public boolean usedInFix(int satIndex) {
return (mSvidWithFlags[satIndex] & GNSS_SV_FLAGS_USED_IN_FIX) != 0;
}
+
+ /**
+ * Reports whether {@link #getCarrierFrequencyHz(int satIndex)} is available (i.e. carrier
+ * frequency is available for the satellite at the specified index).
+ *
+ * @param satIndex the index of the satellite in the list.
+ * @hide
+ */
+ public boolean hasCarrierFrequency(int satIndex) {
+ return (mSvidWithFlags[satIndex] & GNSS_SV_FLAGS_HAS_CARRIER_FREQUENCY) != 0;
+ }
+
+ /**
+ * Gets the carrier frequency of the signal tracked.
+ *
+ * For example it can be the GPS L1 = 1.57542e9 Hz, or L2, L5, varying GLO channels, etc. If
+ * the field is not set, it is the primary common use frequency, e.g. L1 for GPS.
+ *
+ * <p>The value is only available if {@link #hasCarrierFrequency(int satIndex)} is {@code true}.
+ * @hide
+ */
+ public float getCarrierFrequencyHz(int satIndex) {
+ return mCarrierFrequencies[satIndex];
+ }
}
diff --git a/location/java/android/location/IGnssStatusListener.aidl b/location/java/android/location/IGnssStatusListener.aidl
index d84614f1b894..d824cb18765a 100644
--- a/location/java/android/location/IGnssStatusListener.aidl
+++ b/location/java/android/location/IGnssStatusListener.aidl
@@ -27,6 +27,7 @@ oneway interface IGnssStatusListener
void onGnssStopped();
void onFirstFix(int ttff);
void onSvStatusChanged(int svCount, in int[] svidWithFlags, in float[] cn0s,
- in float[] elevations, in float[] azimuths);
+ in float[] elevations, in float[] azimuths,
+ in float[] carrierFreqs);
void onNmeaReceived(long timestamp, String nmea);
}
diff --git a/location/java/android/location/Location.java b/location/java/android/location/Location.java
index 50f0badf6ac1..f5f437e0bbe5 100644
--- a/location/java/android/location/Location.java
+++ b/location/java/android/location/Location.java
@@ -81,23 +81,35 @@ public class Location implements Parcelable {
/**
* Bit mask for mFieldsMask indicating the presence of mAltitude.
*/
- private static final byte HAS_ALTITUDE_MASK = 1;
+ private static final int HAS_ALTITUDE_MASK = 1;
/**
* Bit mask for mFieldsMask indicating the presence of mSpeed.
*/
- private static final byte HAS_SPEED_MASK = 2;
+ private static final int HAS_SPEED_MASK = 2;
/**
* Bit mask for mFieldsMask indicating the presence of mBearing.
*/
- private static final byte HAS_BEARING_MASK = 4;
+ private static final int HAS_BEARING_MASK = 4;
/**
- * Bit mask for mFieldsMask indicating the presence of mAccuracy.
+ * Bit mask for mFieldsMask indicating the presence of mHorizontalAccuracy.
*/
- private static final byte HAS_ACCURACY_MASK = 8;
+ private static final int HAS_HORIZONTAL_ACCURACY_MASK = 8;
/**
* Bit mask for mFieldsMask indicating location is from a mock provider.
*/
- private static final byte HAS_MOCK_PROVIDER_MASK = 16;
+ private static final int HAS_MOCK_PROVIDER_MASK = 16;
+ /**
+ * Bit mask for mFieldsMask indicating the presence of mVerticalAccuracy.
+ */
+ private static final int HAS_VERTICAL_ACCURACY_MASK = 32;
+ /**
+ * Bit mask for mFieldsMask indicating the presence of mSpeedAccuracy.
+ */
+ private static final int HAS_SPEED_ACCURACY_MASK = 64;
+ /**
+ * Bit mask for mFieldsMask indicating the presence of mBearingAccuracy.
+ */
+ private static final int HAS_BEARING_ACCURACY_MASK = 128;
// Cached data to make bearing/distance computations more efficient for the case
// where distanceTo and bearingTo are called in sequence. Assume this typically happens
@@ -118,7 +130,11 @@ public class Location implements Parcelable {
private double mAltitude = 0.0f;
private float mSpeed = 0.0f;
private float mBearing = 0.0f;
- private float mAccuracy = 0.0f;
+ private float mHorizontalAccuracyMeters = 0.0f;
+ private float mVerticalAccuracyMeters = 0.0f;
+ private float mSpeedAccuracyMetersPerSecond = 0.0f;
+ private float mBearingAccuracyDegrees = 0.0f;
+
private Bundle mExtras = null;
// A bitmask of fields present in this object (see HAS_* constants defined above).
@@ -156,7 +172,10 @@ public class Location implements Parcelable {
mAltitude = l.mAltitude;
mSpeed = l.mSpeed;
mBearing = l.mBearing;
- mAccuracy = l.mAccuracy;
+ mHorizontalAccuracyMeters = l.mHorizontalAccuracyMeters;
+ mVerticalAccuracyMeters = l.mVerticalAccuracyMeters;
+ mSpeedAccuracyMetersPerSecond = l.mSpeedAccuracyMetersPerSecond;
+ mBearingAccuracyDegrees = l.mBearingAccuracyDegrees;
mExtras = (l.mExtras == null) ? null : new Bundle(l.mExtras);
}
@@ -173,7 +192,10 @@ public class Location implements Parcelable {
mAltitude = 0;
mSpeed = 0;
mBearing = 0;
- mAccuracy = 0;
+ mHorizontalAccuracyMeters = 0;
+ mVerticalAccuracyMeters = 0;
+ mSpeedAccuracyMetersPerSecond = 0;
+ mBearingAccuracyDegrees = 0;
mExtras = null;
}
@@ -718,19 +740,18 @@ public class Location implements Parcelable {
}
/**
- * True if this location has an accuracy.
+ * True if this location has a horizontal accuracy.
*
- * <p>All locations generated by the {@link LocationManager} have an
- * accuracy.
+ * <p>All locations generated by the {@link LocationManager} have an horizontal accuracy.
*/
public boolean hasAccuracy() {
- return (mFieldsMask & HAS_ACCURACY_MASK) != 0;
+ return (mFieldsMask & HAS_HORIZONTAL_ACCURACY_MASK) != 0;
}
/**
- * Get the estimated accuracy of this location, in meters.
+ * Get the estimated horizontal accuracy of this location, radial, in meters.
*
- * <p>We define accuracy as the radius of 68% confidence. In other
+ * <p>We define horizontal accuracy as the radius of 68% confidence. In other
* words, if you draw a circle centered at this location's
* latitude and longitude, and with a radius equal to the accuracy,
* then there is a 68% probability that the true location is inside
@@ -745,35 +766,183 @@ public class Location implements Parcelable {
* accuracy, and does not indicate the accuracy of bearing,
* velocity or altitude if those are included in this Location.
*
- * <p>If this location does not have an accuracy, then 0.0 is returned.
- * All locations generated by the {@link LocationManager} include
- * an accuracy.
+ * <p>If this location does not have a horizontal accuracy, then 0.0 is returned.
+ * All locations generated by the {@link LocationManager} include horizontal accuracy.
*/
public float getAccuracy() {
- return mAccuracy;
+ return mHorizontalAccuracyMeters;
}
/**
- * Set the estimated accuracy of this location, meters.
+ * Set the estimated horizontal accuracy of this location, meters.
*
- * <p>See {@link #getAccuracy} for the definition of accuracy.
+ * <p>See {@link #getAccuracy} for the definition of horizontal accuracy.
*
* <p>Following this call {@link #hasAccuracy} will return true.
*/
- public void setAccuracy(float accuracy) {
- mAccuracy = accuracy;
- mFieldsMask |= HAS_ACCURACY_MASK;
+ public void setAccuracy(float horizontalAccuracy) {
+ mHorizontalAccuracyMeters = horizontalAccuracy;
+ mFieldsMask |= HAS_HORIZONTAL_ACCURACY_MASK;
}
/**
- * Remove the accuracy from this location.
+ * Remove the horizontal accuracy from this location.
*
* <p>Following this call {@link #hasAccuracy} will return false, and
* {@link #getAccuracy} will return 0.0.
*/
public void removeAccuracy() {
- mAccuracy = 0.0f;
- mFieldsMask &= ~HAS_ACCURACY_MASK;
+ mHorizontalAccuracyMeters = 0.0f;
+ mFieldsMask &= ~HAS_HORIZONTAL_ACCURACY_MASK;
+ }
+
+ /**
+ * True if this location has a vertical accuracy.
+ * @hide
+ */
+ public boolean hasVerticalAccuracy() {
+ return (mFieldsMask & HAS_VERTICAL_ACCURACY_MASK) != 0;
+ }
+
+ /**
+ * Get the estimated vertical accuracy of this location, in meters.
+ *
+ * <p>We define vertical accuracy as the radius of 68% confidence. In other
+ * words, if you draw a circle centered at this location's altitude, and with a radius
+ * equal to the vertical accuracy, then there is a 68% probability that the true altitude is
+ * inside the circle.
+ *
+ * <p>In statistical terms, it is assumed that location errors
+ * are random with a normal distribution, so the 68% confidence circle
+ * represents one standard deviation. Note that in practice, location
+ * errors do not always follow such a simple distribution.
+ *
+ * <p>If this location does not have a vertical accuracy, then 0.0 is returned.
+ * @hide
+ */
+ public float getVerticalAccuracyMeters() {
+ return mVerticalAccuracyMeters;
+ }
+
+ /**
+ * Set the estimated vertical accuracy of this location, meters.
+ *
+ * <p>See {@link #getVerticalAccuracyMeters} for the definition of vertical accuracy.
+ *
+ * <p>Following this call {@link #hasVerticalAccuracy} will return true.
+ * @hide
+ */
+ public void setVerticalAccuracyMeters(float verticalAccuracyMeters) {
+ mVerticalAccuracyMeters = verticalAccuracyMeters;
+ mFieldsMask |= HAS_VERTICAL_ACCURACY_MASK;
+ }
+
+ /**
+ * Remove the vertical accuracy from this location.
+ *
+ * <p>Following this call {@link #hasVerticalAccuracy} will return false, and
+ * {@link #getVerticalAccuracyMeters} will return 0.0.
+ * @hide
+ */
+ public void removeVerticalAccuracy() {
+ mVerticalAccuracyMeters = 0.0f;
+ mFieldsMask &= ~HAS_VERTICAL_ACCURACY_MASK;
+ }
+
+ /**
+ * True if this location has a speed accuracy.
+ * @hide
+ */
+ public boolean hasSpeedAccuracy() {
+ return (mFieldsMask & HAS_SPEED_ACCURACY_MASK) != 0;
+ }
+
+ /**
+ * Get the estimated speed accuracy of this location, in meters per second.
+ *
+ * <p>We define speed accuracy as the radius of 68% confidence. In other
+ * words, if you draw a circle centered at this location's speed, and with a radius
+ * equal to the speed accuracy, then there is a 68% probability that the true speed is
+ * inside the circle.
+ *
+ * <p>If this location does not have a speed accuracy, then 0.0 is returned.
+ * @hide
+ */
+ public float getSpeedAccuracyMetersPerSecond() {
+ return mSpeedAccuracyMetersPerSecond;
+ }
+
+ /**
+ * Set the estimated speed accuracy of this location, meters per second.
+ *
+ * <p>See {@link #getSpeedAccuracyMetersPerSecond} for the definition of speed accuracy.
+ *
+ * <p>Following this call {@link #hasSpeedAccuracy} will return true.
+ * @hide
+ */
+ public void setSpeedAccuracyMetersPerSecond(float speedAccuracyMeterPerSecond) {
+ mSpeedAccuracyMetersPerSecond = speedAccuracyMeterPerSecond;
+ mFieldsMask |= HAS_SPEED_ACCURACY_MASK;
+ }
+
+ /**
+ * Remove the speed accuracy from this location.
+ *
+ * <p>Following this call {@link #hasSpeedAccuracy} will return false, and
+ * {@link #getSpeedAccuracyMetersPerSecond} will return 0.0.
+ * @hide
+ */
+ public void removeSpeedAccuracy() {
+ mSpeedAccuracyMetersPerSecond = 0.0f;
+ mFieldsMask &= ~HAS_SPEED_ACCURACY_MASK;
+ }
+
+ /**
+ * True if this location has a bearing accuracy.
+ * @hide
+ */
+ public boolean hasBearingAccuracy() {
+ return (mFieldsMask & HAS_BEARING_ACCURACY_MASK) != 0;
+ }
+
+ /**
+ * Get the estimated bearing accuracy of this location, in degrees.
+ *
+ * <p>We define bearing accuracy as the radius of 68% confidence. In other
+ * words, if you draw a circle centered at this location's bearing, and with a radius
+ * equal to the bearing accuracy, then there is a 68% probability that the true bearing is
+ * inside the circle.
+ *
+ * <p>If this location does not have a bearing accuracy, then 0.0 is returned.
+ * @hide
+ */
+ public float getBearingAccuracyDegrees() {
+ return mBearingAccuracyDegrees;
+ }
+
+ /**
+ * Set the estimated bearing accuracy of this location, degrees.
+ *
+ * <p>See {@link #getBearingAccuracyDegrees} for the definition of bearing accuracy.
+ *
+ * <p>Following this call {@link #hasBearingAccuracy} will return true.
+ * @hide
+ */
+ public void setBearingAccuracyDegrees(float bearingAccuracyDegrees) {
+ mBearingAccuracyDegrees = bearingAccuracyDegrees;
+ mFieldsMask |= HAS_BEARING_ACCURACY_MASK;
+ }
+
+ /**
+ * Remove the bearing accuracy from this location.
+ *
+ * <p>Following this call {@link #hasBearingAccuracy} will return false, and
+ * {@link #getBearingAccuracyDegrees} will return 0.0.
+ * @hide
+ */
+ public void removeBearingAccuracy() {
+ mBearingAccuracyDegrees = 0.0f;
+ mFieldsMask &= ~HAS_BEARING_ACCURACY_MASK;
}
/**
@@ -810,8 +979,8 @@ public class Location implements Parcelable {
public void makeComplete() {
if (mProvider == null) mProvider = "?";
if (!hasAccuracy()) {
- mFieldsMask |= HAS_ACCURACY_MASK;
- mAccuracy = 100.0f;
+ mFieldsMask |= HAS_HORIZONTAL_ACCURACY_MASK;
+ mHorizontalAccuracyMeters = 100.0f;
}
if (mTime == 0) mTime = System.currentTimeMillis();
if (mElapsedRealtimeNanos == 0) mElapsedRealtimeNanos = SystemClock.elapsedRealtimeNanos();
@@ -849,8 +1018,8 @@ public class Location implements Parcelable {
s.append("Location[");
s.append(mProvider);
s.append(String.format(" %.6f,%.6f", mLatitude, mLongitude));
- if (hasAccuracy()) s.append(String.format(" acc=%.0f", mAccuracy));
- else s.append(" acc=???");
+ if (hasAccuracy()) s.append(String.format(" hAcc=%.0f", mHorizontalAccuracyMeters));
+ else s.append(" hAcc=???");
if (mTime == 0) {
s.append(" t=?!?");
}
@@ -863,6 +1032,12 @@ public class Location implements Parcelable {
if (hasAltitude()) s.append(" alt=").append(mAltitude);
if (hasSpeed()) s.append(" vel=").append(mSpeed);
if (hasBearing()) s.append(" bear=").append(mBearing);
+ if (hasVerticalAccuracy()) s.append(String.format(" vAcc=%.0f", mVerticalAccuracyMeters));
+ else s.append(" vAcc=???");
+ if (hasSpeedAccuracy()) s.append(String.format(" sAcc=%.0f", mSpeedAccuracyMetersPerSecond));
+ else s.append(" sAcc=???");
+ if (hasBearingAccuracy()) s.append(String.format(" bAcc=%.0f", mBearingAccuracyDegrees));
+ else s.append(" bAcc=???");
if (isFromMockProvider()) s.append(" mock");
if (mExtras != null) {
@@ -890,7 +1065,10 @@ public class Location implements Parcelable {
l.mAltitude = in.readDouble();
l.mSpeed = in.readFloat();
l.mBearing = in.readFloat();
- l.mAccuracy = in.readFloat();
+ l.mHorizontalAccuracyMeters = in.readFloat();
+ l.mVerticalAccuracyMeters = in.readFloat();
+ l.mSpeedAccuracyMetersPerSecond = in.readFloat();
+ l.mBearingAccuracyDegrees = in.readFloat();
l.mExtras = Bundle.setDefusable(in.readBundle(), true);
return l;
}
@@ -917,7 +1095,10 @@ public class Location implements Parcelable {
parcel.writeDouble(mAltitude);
parcel.writeFloat(mSpeed);
parcel.writeFloat(mBearing);
- parcel.writeFloat(mAccuracy);
+ parcel.writeFloat(mHorizontalAccuracyMeters);
+ parcel.writeFloat(mVerticalAccuracyMeters);
+ parcel.writeFloat(mSpeedAccuracyMetersPerSecond);
+ parcel.writeFloat(mBearingAccuracyDegrees);
parcel.writeBundle(mExtras);
}
diff --git a/location/java/android/location/LocationManager.java b/location/java/android/location/LocationManager.java
index 087e74f54006..4e146268b256 100644
--- a/location/java/android/location/LocationManager.java
+++ b/location/java/android/location/LocationManager.java
@@ -1554,9 +1554,10 @@ public class LocationManager {
@Override
public void onSvStatusChanged(int svCount, int[] prnWithFlags,
- float[] cn0s, float[] elevations, float[] azimuths) {
+ float[] cn0s, float[] elevations, float[] azimuths, float[] carrierFreqs) {
if (mGnssCallback != null) {
- mGnssStatus = new GnssStatus(svCount, prnWithFlags, cn0s, elevations, azimuths);
+ mGnssStatus = new GnssStatus(svCount, prnWithFlags, cn0s, elevations, azimuths,
+ carrierFreqs);
Message msg = Message.obtain();
msg.what = GpsStatus.GPS_EVENT_SATELLITE_STATUS;
diff --git a/media/java/android/media/AudioManager.java b/media/java/android/media/AudioManager.java
index 1d124c5e7f91..7c603853a3ba 100644
--- a/media/java/android/media/AudioManager.java
+++ b/media/java/android/media/AudioManager.java
@@ -633,6 +633,15 @@ public class AudioManager {
/**
* @hide
+ * For test purposes only, will throw NPE with some methods that require a Context.
+ */
+ public AudioManager() {
+ mUseVolumeKeySounds = true;
+ mUseFixedVolume = false;
+ }
+
+ /**
+ * @hide
*/
public AudioManager(Context context) {
setContext(context);
diff --git a/media/java/android/media/AudioPlaybackConfiguration.java b/media/java/android/media/AudioPlaybackConfiguration.java
index 147c5dffca48..b38a07f15511 100644
--- a/media/java/android/media/AudioPlaybackConfiguration.java
+++ b/media/java/android/media/AudioPlaybackConfiguration.java
@@ -20,8 +20,10 @@ import android.annotation.IntDef;
import android.annotation.NonNull;
import android.annotation.SystemApi;
import android.os.Binder;
+import android.os.IBinder;
import android.os.Parcel;
import android.os.Parcelable;
+import android.os.RemoteException;
import android.util.Log;
import java.io.PrintWriter;
@@ -36,6 +38,8 @@ import java.util.Objects;
public final class AudioPlaybackConfiguration implements Parcelable {
private final static String TAG = new String("AudioPlaybackConfiguration");
+ private final static boolean DEBUG = false;
+
/** @hide */
public final static int PLAYER_PIID_INVALID = -1;
/** @hide */
@@ -147,6 +151,8 @@ public final class AudioPlaybackConfiguration implements Parcelable {
private int mPlayerType;
private int mClientUid;
private int mClientPid;
+ // the IPlayer reference and death monitor
+ private IPlayerShell mIPlayerShell;
private int mPlayerState;
private AudioAttributes mPlayerAttr; // never null
@@ -156,18 +162,34 @@ public final class AudioPlaybackConfiguration implements Parcelable {
*/
private AudioPlaybackConfiguration(int piid) {
mPlayerIId = piid;
+ mIPlayerShell = null;
}
/**
* @hide
*/
public AudioPlaybackConfiguration(PlayerBase.PlayerIdCard pic, int piid, int uid, int pid) {
+ if (DEBUG) { Log.d(TAG, "new: piid=" + piid + " iplayer=" + pic.mIPlayer); }
mPlayerIId = piid;
mPlayerType = pic.mPlayerType;
mClientUid = uid;
mClientPid = pid;
mPlayerState = PLAYER_STATE_IDLE;
mPlayerAttr = pic.mAttributes;
+ if ((sPlayerDeathMonitor != null) && (pic.mIPlayer != null)) {
+ mIPlayerShell = new IPlayerShell(this, pic.mIPlayer);
+ } else {
+ mIPlayerShell = null;
+ }
+ }
+
+ /**
+ * @hide
+ */
+ public void init() {
+ if (mIPlayerShell != null) {
+ mIPlayerShell.monitorDeath();
+ }
}
// Note that this method is called server side, so no "privileged" information is ever sent
@@ -191,6 +213,7 @@ public final class AudioPlaybackConfiguration implements Parcelable {
anonymCopy.mPlayerType = PLAYER_TYPE_UNKNOWN;
anonymCopy.mClientUid = PLAYER_UPID_INVALID;
anonymCopy.mClientPid = PLAYER_UPID_INVALID;
+ anonymCopy.mIPlayerShell = null;
return anonymCopy;
}
@@ -250,6 +273,25 @@ public final class AudioPlaybackConfiguration implements Parcelable {
/**
* @hide
+ * Return an identifier unique for the lifetime of the player.
+ * @return a player interface identifier
+ */
+ @SystemApi
+ public int getPlayerInterfaceId() {
+ return mPlayerIId;
+ }
+
+ /**
+ * @hide
+ * FIXME return a player proxy instead, make systemApi
+ * @return
+ */
+ public IPlayer getPlayerProxy() {
+ return mIPlayerShell == null ? null : mIPlayerShell.getIPlayer();
+ }
+
+ /**
+ * @hide
* Handle a change of audio attributes
* @param attr
*/
@@ -268,9 +310,26 @@ public final class AudioPlaybackConfiguration implements Parcelable {
public boolean handleStateEvent(int event) {
final boolean changed = (mPlayerState != event);
mPlayerState = event;
+ if ((event == PLAYER_STATE_RELEASED) && (mIPlayerShell != null)) {
+ mIPlayerShell.release();
+ }
return changed;
}
+ // To report IPlayer death from death recipient
+ /** @hide */
+ public interface PlayerDeathMonitor {
+ public void playerDeath(int piid);
+ }
+ /** @hide */
+ public static PlayerDeathMonitor sPlayerDeathMonitor;
+
+ private void playerDied() {
+ if (sPlayerDeathMonitor != null) {
+ sPlayerDeathMonitor.playerDeath(mPlayerIId);
+ }
+ }
+
/**
* @hide
* Returns true if the player is considered "active", i.e. actively playing, and thus
@@ -338,6 +397,7 @@ public final class AudioPlaybackConfiguration implements Parcelable {
dest.writeInt(mClientPid);
dest.writeInt(mPlayerState);
mPlayerAttr.writeToParcel(dest, 0);
+ dest.writeStrongInterface(mIPlayerShell == null ? null : mIPlayerShell.getIPlayer());
}
private AudioPlaybackConfiguration(Parcel in) {
@@ -347,6 +407,8 @@ public final class AudioPlaybackConfiguration implements Parcelable {
mClientPid = in.readInt();
mPlayerState = in.readInt();
mPlayerAttr = AudioAttributes.CREATOR.createFromParcel(in);
+ final IPlayer p = IPlayer.Stub.asInterface(in.readStrongBinder());
+ mIPlayerShell = (p == null) ? null : new IPlayerShell(null, p);
}
@Override
@@ -363,6 +425,46 @@ public final class AudioPlaybackConfiguration implements Parcelable {
}
//=====================================================================
+ // Inner class for corresponding IPlayer and its death monitoring
+ final static class IPlayerShell implements IBinder.DeathRecipient {
+
+ final AudioPlaybackConfiguration mMonitor; // never null
+ private IPlayer mIPlayer;
+
+ IPlayerShell(@NonNull AudioPlaybackConfiguration monitor, @NonNull IPlayer iplayer) {
+ mMonitor = monitor;
+ mIPlayer = iplayer;
+ }
+
+ void monitorDeath() {
+ try {
+ mIPlayer.asBinder().linkToDeath(this, 0);
+ } catch (RemoteException e) {
+ if (mMonitor != null) {
+ Log.w(TAG, "Could not link to client death for piid=" + mMonitor.mPlayerIId, e);
+ } else {
+ Log.w(TAG, "Could not link to client death", e);
+ }
+ }
+ }
+
+ IPlayer getIPlayer() {
+ return mIPlayer;
+ }
+
+ public void binderDied() {
+ if (mMonitor != null) {
+ if (DEBUG) { Log.i(TAG, "IPlayerShell binderDied for piid=" + mMonitor.mPlayerIId);}
+ mMonitor.playerDied();
+ } else if (DEBUG) { Log.i(TAG, "IPlayerShell binderDied"); }
+ }
+
+ void release() {
+ mIPlayer.asBinder().unlinkToDeath(this, 0);
+ }
+ }
+
+ //=====================================================================
// Utilities
/** @hide */
diff --git a/media/java/android/media/AudioTrack.java b/media/java/android/media/AudioTrack.java
index 464cbdb985a4..031ac0667c64 100644
--- a/media/java/android/media/AudioTrack.java
+++ b/media/java/android/media/AudioTrack.java
@@ -537,6 +537,8 @@ public class AudioTrack extends PlayerBase
} else {
mState = STATE_INITIALIZED;
}
+
+ baseRegisterPlayer();
}
/**
@@ -566,6 +568,7 @@ public class AudioTrack extends PlayerBase
// other initialization...
if (nativeTrackInJavaObj != 0) {
+ baseRegisterPlayer();
deferred_connect(nativeTrackInJavaObj);
} else {
mState = STATE_UNINITIALIZED;
@@ -2739,6 +2742,24 @@ public class AudioTrack extends PlayerBase
}
//---------------------------------------------------------
+ // Methods for IPlayer interface
+ //--------------------
+ @Override
+ void playerStart() {
+ play();
+ }
+
+ @Override
+ void playerPause() {
+ pause();
+ }
+
+ @Override
+ void playerStop() {
+ stop();
+ }
+
+ //---------------------------------------------------------
// Java methods called from the native side
//--------------------
@SuppressWarnings("unused")
diff --git a/media/java/android/media/IPlayer.aidl b/media/java/android/media/IPlayer.aidl
index 32984f98264c..ccb60f7fd843 100644
--- a/media/java/android/media/IPlayer.aidl
+++ b/media/java/android/media/IPlayer.aidl
@@ -24,4 +24,5 @@ interface IPlayer {
oneway void start();
oneway void pause();
oneway void stop();
+ oneway void setVolume(float vol);
}
diff --git a/media/java/android/media/MediaPlayer.java b/media/java/android/media/MediaPlayer.java
index 500556aa7256..e3a0f25d5ccd 100644
--- a/media/java/android/media/MediaPlayer.java
+++ b/media/java/android/media/MediaPlayer.java
@@ -645,6 +645,8 @@ public class MediaPlayer extends PlayerBase
* It's easier to create it here than in C++.
*/
native_setup(new WeakReference<MediaPlayer>(this));
+
+ baseRegisterPlayer();
}
/*
@@ -1261,6 +1263,21 @@ public class MediaPlayer extends PlayerBase
private native void _pause() throws IllegalStateException;
+ @Override
+ void playerStart() {
+ start();
+ }
+
+ @Override
+ void playerPause() {
+ pause();
+ }
+
+ @Override
+ void playerStop() {
+ stop();
+ }
+
/**
* Set the low-level power management behavior for this MediaPlayer. This
* can be used when the MediaPlayer is not playing through a SurfaceHolder
diff --git a/media/java/android/media/PlayerBase.java b/media/java/android/media/PlayerBase.java
index 4eacb3880a94..981939583e3b 100644
--- a/media/java/android/media/PlayerBase.java
+++ b/media/java/android/media/PlayerBase.java
@@ -56,14 +56,14 @@ public abstract class PlayerBase {
protected float mAuxEffectSendLevel = 0.0f;
// for AppOps
- private final IAppOpsService mAppOps;
- private final IAppOpsCallback mAppOpsCallback;
+ private IAppOpsService mAppOps;
+ private IAppOpsCallback mAppOpsCallback;
private boolean mHasAppOpsPlayAudio = true;
private final Object mAppOpsLock = new Object();
private final int mImplType;
// uniquely identifies the Player Interface throughout the system (P I Id)
- private final int mPlayerIId;
+ private int mPlayerIId;
private int mState;
@@ -78,6 +78,12 @@ public abstract class PlayerBase {
}
mAttributes = attr;
mImplType = implType;
+ };
+
+ /**
+ * Call from derived class when instantiation / initialization is successful
+ */
+ protected void baseRegisterPlayer() {
int newPiid = AudioPlaybackConfiguration.PLAYER_PIID_INVALID;
IBinder b = ServiceManager.getService(Context.APP_OPS_SERVICE);
mAppOps = IAppOpsService.Stub.asInterface(b);
@@ -100,14 +106,16 @@ public abstract class PlayerBase {
mHasAppOpsPlayAudio = false;
}
try {
- newPiid = getService().trackPlayer(new PlayerIdCard(mImplType, mAttributes));
+ if (mIPlayer == null) {
+ throw new IllegalStateException("Cannot register a player with a null mIPlayer");
+ }
+ newPiid = getService().trackPlayer(new PlayerIdCard(mImplType, mAttributes, mIPlayer));
} catch (RemoteException e) {
Log.e(TAG, "Error talking to audio service, player will not be tracked", e);
}
mPlayerIId = newPiid;
}
-
/**
* To be called whenever the audio attributes of the player change
* @param attr non-null audio attributes
@@ -295,16 +303,34 @@ public abstract class PlayerBase {
*/
abstract void playerSetVolume(boolean muting, float leftVolume, float rightVolume);
abstract int playerSetAuxEffectSendLevel(boolean muting, float level);
+ abstract void playerStart();
+ abstract void playerPause();
+ abstract void playerStop();
//=====================================================================
- // Implementation of IPlayer
- private final IPlayer mIPlayer = new IPlayer.Stub() {
+ /**
+ * Implementation of IPlayer for all subclasses of PlayerBase
+ */
+ private IPlayer mIPlayer = new IPlayer.Stub() {
@Override
- public void start() {}
+ public void start() {
+ playerStart();
+ }
+
+ @Override
+ public void pause() {
+ playerPause();
+ }
+
@Override
- public void pause() {}
+ public void stop() {
+ playerStop();
+ }
+
@Override
- public void stop() {}
+ public void setVolume(float vol) {
+ baseSetVolume(vol, vol);
+ }
};
//=====================================================================
@@ -317,10 +343,12 @@ public abstract class PlayerBase {
public final static int AUDIO_ATTRIBUTES_NONE = 0;
public final static int AUDIO_ATTRIBUTES_DEFINED = 1;
public final AudioAttributes mAttributes;
+ public final IPlayer mIPlayer;
- PlayerIdCard(int type, @NonNull AudioAttributes attr) {
+ PlayerIdCard(int type, @NonNull AudioAttributes attr, @NonNull IPlayer iplayer) {
mPlayerType = type;
mAttributes = attr;
+ mIPlayer = iplayer;
}
@Override
@@ -337,6 +365,7 @@ public abstract class PlayerBase {
public void writeToParcel(Parcel dest, int flags) {
dest.writeInt(mPlayerType);
mAttributes.writeToParcel(dest, 0);
+ dest.writeStrongBinder(mIPlayer == null ? null : mIPlayer.asBinder());
}
public static final Parcelable.Creator<PlayerIdCard> CREATOR
@@ -357,6 +386,9 @@ public abstract class PlayerBase {
private PlayerIdCard(Parcel in) {
mPlayerType = in.readInt();
mAttributes = AudioAttributes.CREATOR.createFromParcel(in);
+ // IPlayer can be null if unmarshalling a Parcel coming from who knows where
+ final IBinder b = in.readStrongBinder();
+ mIPlayer = (b == null ? null : IPlayer.Stub.asInterface(b));
}
@Override
diff --git a/media/java/android/media/SoundPool.java b/media/java/android/media/SoundPool.java
index c985cbd010d4..4cc1f8ebe4b4 100644
--- a/media/java/android/media/SoundPool.java
+++ b/media/java/android/media/SoundPool.java
@@ -159,6 +159,8 @@ public class SoundPool extends PlayerBase {
}
mLock = new Object();
mAttributes = attributes;
+
+ baseRegisterPlayer();
}
/**
@@ -399,6 +401,21 @@ public class SoundPool extends PlayerBase {
return AudioSystem.SUCCESS;
}
+ @Override
+ void playerStart() {
+ // FIXME implement resuming any paused sound
+ }
+
+ @Override
+ void playerPause() {
+ // FIXME implement pausing any playing sound
+ }
+
+ @Override
+ void playerStop() {
+ // FIXME implement pausing any playing sound
+ }
+
/**
* Similar, except set volume of all channels to same value.
* @hide
diff --git a/packages/SettingsLib/res/drawable/ic_bt_cellphone.xml b/packages/SettingsLib/res/drawable/ic_bt_cellphone.xml
index cc9b73238d71..be9b094134c8 100644
--- a/packages/SettingsLib/res/drawable/ic_bt_cellphone.xml
+++ b/packages/SettingsLib/res/drawable/ic_bt_cellphone.xml
@@ -18,7 +18,7 @@
android:height="24dp"
android:viewportWidth="24.0"
android:viewportHeight="24.0"
- android:tint="?android:attr/colorAccent"
+ android:tint="?android:attr/colorControlNormal"
android:autoMirrored="true">
<path
android:fillColor="#FF000000"
diff --git a/packages/SettingsLib/res/drawable/ic_bt_headphones_a2dp.xml b/packages/SettingsLib/res/drawable/ic_bt_headphones_a2dp.xml
index bbe39143c539..32f39a39754f 100644
--- a/packages/SettingsLib/res/drawable/ic_bt_headphones_a2dp.xml
+++ b/packages/SettingsLib/res/drawable/ic_bt_headphones_a2dp.xml
@@ -18,7 +18,7 @@
android:height="24dp"
android:viewportWidth="24.0"
android:viewportHeight="24.0"
- android:tint="?android:attr/colorAccent">
+ android:tint="?android:attr/colorControlNormal">
<path
android:fillColor="#FF000000"
android:pathData="M12,1c-4.97,0 -9,4.03 -9,9v7c0,1.66 1.34,3 3,3h3v-8H5v-2c0,-3.87
diff --git a/packages/SettingsLib/res/drawable/ic_bt_headset_hfp.xml b/packages/SettingsLib/res/drawable/ic_bt_headset_hfp.xml
index ceeef191bc99..e43fe39409af 100644
--- a/packages/SettingsLib/res/drawable/ic_bt_headset_hfp.xml
+++ b/packages/SettingsLib/res/drawable/ic_bt_headset_hfp.xml
@@ -18,7 +18,7 @@
android:height="24dp"
android:viewportWidth="24.0"
android:viewportHeight="24.0"
- android:tint="?android:attr/colorAccent">
+ android:tint="?android:attr/colorControlNormal">
<path
android:fillColor="#FF000000"
android:pathData="M12,1c-4.97,0 -9,4.03 -9,9v7c0,1.66 1.34,3 3,3h3v-8H5v-2c0,-3.87
diff --git a/packages/SettingsLib/res/drawable/ic_bt_misc_hid.xml b/packages/SettingsLib/res/drawable/ic_bt_misc_hid.xml
index 67b42ae020d8..ac460ab675e9 100644
--- a/packages/SettingsLib/res/drawable/ic_bt_misc_hid.xml
+++ b/packages/SettingsLib/res/drawable/ic_bt_misc_hid.xml
@@ -18,7 +18,7 @@
android:height="24dp"
android:viewportWidth="24.0"
android:viewportHeight="24.0"
- android:tint="?android:attr/colorAccent">
+ android:tint="?android:attr/colorControlNormal">
<path
android:fillColor="#FF000000"
android:pathData="M15,7.5V2H9v5.5l3,3 3,-3zM7.5,9H2v6h5.5l3,-3 -3,-3zM9,16.5V22h6v-5.5l-3,-3
diff --git a/packages/SettingsLib/res/drawable/ic_bt_network_pan.xml b/packages/SettingsLib/res/drawable/ic_bt_network_pan.xml
index c5ab01c5ede8..6e4361b95eab 100644
--- a/packages/SettingsLib/res/drawable/ic_bt_network_pan.xml
+++ b/packages/SettingsLib/res/drawable/ic_bt_network_pan.xml
@@ -18,7 +18,7 @@
android:height="24dp"
android:viewportWidth="24.0"
android:viewportHeight="24.0"
- android:tint="?android:attr/colorAccent">
+ android:tint="?android:attr/colorControlNormal">
<path
android:fillColor="#FF000000"
android:pathData="M14.24,12.01l2.32,2.32c0.28,-0.72 0.44,-1.51 0.44,-2.33 0,-0.82
diff --git a/packages/SettingsLib/res/drawable/ic_bt_pointing_hid.xml b/packages/SettingsLib/res/drawable/ic_bt_pointing_hid.xml
index 821618dbc4a6..de97e249789f 100644
--- a/packages/SettingsLib/res/drawable/ic_bt_pointing_hid.xml
+++ b/packages/SettingsLib/res/drawable/ic_bt_pointing_hid.xml
@@ -18,7 +18,7 @@
android:height="24dp"
android:viewportWidth="24.0"
android:viewportHeight="24.0"
- android:tint="?android:attr/colorAccent">
+ android:tint="?android:attr/colorControlNormal">
<path
android:fillColor="#FF000000"
android:pathData="M13,1.07L13,9h7c0,-4.08 -3.05,-7.44 -7,-7.93zM4,15c0,4.42
diff --git a/packages/SettingsLib/src/com/android/settingslib/users/AppRestrictionsHelper.java b/packages/SettingsLib/src/com/android/settingslib/users/AppRestrictionsHelper.java
index c4aa57d7b593..4c1119757eba 100644
--- a/packages/SettingsLib/src/com/android/settingslib/users/AppRestrictionsHelper.java
+++ b/packages/SettingsLib/src/com/android/settingslib/users/AppRestrictionsHelper.java
@@ -115,7 +115,8 @@ public class AppRestrictionsHelper {
PackageManager.MATCH_ANY_USER, userId);
if (info == null || !info.enabled
|| (info.flags&ApplicationInfo.FLAG_INSTALLED) == 0) {
- mIPm.installExistingPackageAsUser(packageName, mUser.getIdentifier());
+ mIPm.installExistingPackageAsUser(packageName, mUser.getIdentifier(),
+ PackageManager.INSTALL_REASON_UNKNOWN);
if (DEBUG) {
Log.d(TAG, "Installing " + packageName);
}
diff --git a/packages/SettingsLib/tests/integ/src/com/android/settingslib/users/AppRestrictionsHelperTest.java b/packages/SettingsLib/tests/integ/src/com/android/settingslib/users/AppRestrictionsHelperTest.java
index 3f989bd20065..4df199cbd4bd 100644
--- a/packages/SettingsLib/tests/integ/src/com/android/settingslib/users/AppRestrictionsHelperTest.java
+++ b/packages/SettingsLib/tests/integ/src/com/android/settingslib/users/AppRestrictionsHelperTest.java
@@ -145,7 +145,8 @@ public class AppRestrictionsHelperTest extends BaseTest {
mock(AppRestrictionsHelper.OnDisableUiForPackageListener.class);
mHelper.applyUserAppsStates(mockListener);
- verify(mIpm, times(1)).installExistingPackageAsUser("app1", testUserId);
+ verify(mIpm, times(1)).installExistingPackageAsUser("app1", testUserId,
+ PackageManager.INSTALL_REASON_UNKNOWN);
verify(mIpm, times(1)).setApplicationHiddenSettingAsUser("app2", false, testUserId);
verify(mockListener).onDisableUiForPackage("app2");
verify(mPm, times(1)).deletePackageAsUser(eq("app3"), any(IPackageDeleteObserver.class),
@@ -158,14 +159,14 @@ public class AppRestrictionsHelperTest extends BaseTest {
for (String pkg : defaultImes) {
final ResolveInfo ri = createResolveInfoForSystemApp(pkg);
final InputMethodInfo inputMethodInfo = new InputMethodInfo(
- ri, false, null, null, 0, true, true);
+ ri, false, null, null, 0, true, true, false);
inputMethods.add(inputMethodInfo);
addInstalledApp(ri);
}
for (String pkg : otherImes) {
final ResolveInfo ri = createResolveInfoForSystemApp(pkg);
final InputMethodInfo inputMethodInfo = new InputMethodInfo(
- ri, false, null, null, 0, false, true);
+ ri, false, null, null, 0, false, true, false);
inputMethods.add(inputMethodInfo);
addInstalledApp(ri);
}
diff --git a/packages/SystemUI/src/com/android/systemui/recents/views/TaskViewThumbnail.java b/packages/SystemUI/src/com/android/systemui/recents/views/TaskViewThumbnail.java
index 3ae51b0bde35..e3bf1df76677 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/views/TaskViewThumbnail.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/views/TaskViewThumbnail.java
@@ -18,6 +18,7 @@ package com.android.systemui.recents.views;
import android.content.Context;
import android.content.res.Configuration;
+import android.content.res.Resources;
import android.graphics.Bitmap;
import android.graphics.BitmapShader;
import android.graphics.Canvas;
@@ -62,6 +63,8 @@ public class TaskViewThumbnail extends View {
@ViewDebug.ExportedProperty(category="recents")
private float mThumbnailScale;
private float mFullscreenThumbnailScale;
+ /** The height, in pixels, of the task view's title bar. */
+ private int mTitleBarHeight;
private boolean mSizeToFit = false;
private boolean mOverlayHeaderOnThumbnailActionBar = true;
private ThumbnailData mThumbnailData;
@@ -104,12 +107,13 @@ public class TaskViewThumbnail extends View {
mDrawPaint.setColorFilter(mLightingColorFilter);
mDrawPaint.setFilterBitmap(true);
mDrawPaint.setAntiAlias(true);
- mCornerRadius = getResources().getDimensionPixelSize(
- R.dimen.recents_task_view_rounded_corners_radius);
+ Resources res = getResources();
+ mCornerRadius = res.getDimensionPixelSize(R.dimen.recents_task_view_rounded_corners_radius);
mBgFillPaint.setColor(Color.WHITE);
mLockedPaint.setColor(Color.WHITE);
- mFullscreenThumbnailScale = context.getResources().getFraction(
+ mFullscreenThumbnailScale = res.getFraction(
com.android.internal.R.fraction.thumbnail_fullscreen_scale, 1, 1);
+ mTitleBarHeight = res.getDimensionPixelSize(R.dimen.recents_grid_task_view_header_height);
}
/**
@@ -246,7 +250,20 @@ public class TaskViewThumbnail extends View {
// If we haven't measured , skip the thumbnail drawing and only draw the background
// color
mThumbnailScale = 0f;
- } else if (isStackTask && !mSizeToFit) {
+ } else if (mSizeToFit) {
+ // Make sure we fill the entire space regardless of the orientation.
+ float viewAspectRatio = (float) mTaskViewRect.width() /
+ (float) (mTaskViewRect.height() - mTitleBarHeight);
+ float thumbnailAspectRatio =
+ (float) mThumbnailRect.width() / (float) mThumbnailRect.height();
+ if (viewAspectRatio > thumbnailAspectRatio) {
+ mThumbnailScale =
+ (float) mTaskViewRect.width() / (float) mThumbnailRect.width();
+ } else {
+ mThumbnailScale = (float) (mTaskViewRect.height() - mTitleBarHeight)
+ / (float) mThumbnailRect.height();
+ }
+ } else if (isStackTask) {
float invThumbnailScale = 1f / mFullscreenThumbnailScale;
if (mDisplayOrientation == Configuration.ORIENTATION_PORTRAIT) {
if (mThumbnailData.orientation == Configuration.ORIENTATION_PORTRAIT) {
diff --git a/services/core/java/com/android/server/audio/PlaybackActivityMonitor.java b/services/core/java/com/android/server/audio/PlaybackActivityMonitor.java
index d6b4bee102aa..c6b2cf64c30c 100644
--- a/services/core/java/com/android/server/audio/PlaybackActivityMonitor.java
+++ b/services/core/java/com/android/server/audio/PlaybackActivityMonitor.java
@@ -41,7 +41,8 @@ import java.util.List;
/**
* Class to receive and dispatch updates from AudioSystem about recording configurations.
*/
-public final class PlaybackActivityMonitor {
+public final class PlaybackActivityMonitor
+ implements AudioPlaybackConfiguration.PlayerDeathMonitor {
public final static String TAG = "AudioService.PlaybackActivityMonitor";
private final static boolean DEBUG = false;
@@ -57,7 +58,8 @@ public final class PlaybackActivityMonitor {
new HashMap<Integer, AudioPlaybackConfiguration>();
PlaybackActivityMonitor() {
- PlayMonitorClient.sMonitor = this;
+ PlayMonitorClient.sListenerDeathMonitor = this;
+ AudioPlaybackConfiguration.sPlayerDeathMonitor = this;
}
//=================================================================
@@ -72,6 +74,7 @@ public final class PlaybackActivityMonitor {
final AudioPlaybackConfiguration apc =
new AudioPlaybackConfiguration(pic, newPiid,
Binder.getCallingUid(), Binder.getCallingPid());
+ apc.init();
synchronized(mPlayerLock) {
mPlayers.put(newPiid, apc);
}
@@ -124,6 +127,12 @@ public final class PlaybackActivityMonitor {
}
}
+ // Implementation of AudioPlaybackConfiguration.PlayerDeathMonitor
+ @Override
+ public void playerDeath(int piid) {
+ releasePlayer(piid, 0);
+ }
+
protected void dump(PrintWriter pw) {
pw.println("\nPlaybackActivityMonitor dump time: "
+ DateFormat.getTimeInstance().format(new Date()));
@@ -275,7 +284,7 @@ public final class PlaybackActivityMonitor {
private final static class PlayMonitorClient implements IBinder.DeathRecipient {
// can afford to be static because only one PlaybackActivityMonitor ever instantiated
- static PlaybackActivityMonitor sMonitor;
+ static PlaybackActivityMonitor sListenerDeathMonitor;
final IPlaybackConfigDispatcher mDispatcherCb;
final boolean mIsPrivileged;
@@ -291,7 +300,7 @@ public final class PlaybackActivityMonitor {
public void binderDied() {
Log.w(TAG, "client died");
- sMonitor.unregisterPlaybackCallback(mDispatcherCb);
+ sListenerDeathMonitor.unregisterPlaybackCallback(mDispatcherCb);
}
boolean init() {
diff --git a/services/core/java/com/android/server/connectivity/NetworkNotificationManager.java b/services/core/java/com/android/server/connectivity/NetworkNotificationManager.java
index 70faa5ac4a4a..68fe5053b585 100644
--- a/services/core/java/com/android/server/connectivity/NetworkNotificationManager.java
+++ b/services/core/java/com/android/server/connectivity/NetworkNotificationManager.java
@@ -182,7 +182,7 @@ public class NetworkNotificationManager {
details = r.getString(R.string.network_switch_metered_detail, toTransport,
fromTransport);
} else {
- Slog.wtf(TAG, "Unknown notification type " + notifyType + "on network transport "
+ Slog.wtf(TAG, "Unknown notification type " + notifyType + " on network transport "
+ getTransportName(transportType));
return;
}
diff --git a/services/core/java/com/android/server/location/FlpHardwareProvider.java b/services/core/java/com/android/server/location/FlpHardwareProvider.java
index 6d08c3642988..5c9b0eaa29cc 100644
--- a/services/core/java/com/android/server/location/FlpHardwareProvider.java
+++ b/services/core/java/com/android/server/location/FlpHardwareProvider.java
@@ -17,523 +17,58 @@
package com.android.server.location;
import android.content.Context;
-import android.hardware.location.GeofenceHardware;
-import android.hardware.location.GeofenceHardwareImpl;
-import android.hardware.location.GeofenceHardwareRequestParcelable;
import android.hardware.location.IFusedLocationHardware;
-import android.hardware.location.IFusedLocationHardwareSink;
-import android.location.FusedBatchOptions;
import android.location.IFusedGeofenceHardware;
-import android.location.Location;
-import android.location.LocationListener;
-import android.location.LocationManager;
-import android.location.LocationRequest;
-import android.os.Bundle;
-import android.os.Looper;
-import android.os.RemoteException;
-import android.os.SystemClock;
import android.util.Log;
/**
- * This class is an interop layer for JVM types and the JNI code that interacts
+ * This class was an interop layer for JVM types and the JNI code that interacted
* with the FLP HAL implementation.
*
+ * Now, after Treble FLP & GNSS HAL simplification, it is a thin shell that acts like the
+ * pre-existing cases where there was no FLP Hardware support, to keep legacy users of this
+ * class operating.
+ *
* {@hide}
+ * {@Deprecated}
*/
public class FlpHardwareProvider {
- private static final int FIRST_VERSION_WITH_FLUSH_LOCATIONS = 2;
- private GeofenceHardwareImpl mGeofenceHardwareSink = null;
- private IFusedLocationHardwareSink mLocationSink = null;
- // Capabilities provided by FlpCallbacks
- private boolean mHaveBatchingCapabilities;
- private int mBatchingCapabilities;
- private int mVersion = 1;
-
private static FlpHardwareProvider sSingletonInstance = null;
private final static String TAG = "FlpHardwareProvider";
- private final Context mContext;
- private final Object mLocationSinkLock = new Object();
- // FlpHal result codes, they must be equal to the ones in fused_location.h
- private static final int FLP_RESULT_SUCCESS = 0;
- private static final int FLP_RESULT_ERROR = -1;
- private static final int FLP_RESULT_INSUFFICIENT_MEMORY = -2;
- private static final int FLP_RESULT_TOO_MANY_GEOFENCES = -3;
- private static final int FLP_RESULT_ID_EXISTS = -4;
- private static final int FLP_RESULT_ID_UNKNOWN = -5;
- private static final int FLP_RESULT_INVALID_GEOFENCE_TRANSITION = -6;
-
- // FlpHal monitor status codes, they must be equal to the ones in fused_location.h
- private static final int FLP_GEOFENCE_MONITOR_STATUS_UNAVAILABLE = 1<<0;
- private static final int FLP_GEOFENCE_MONITOR_STATUS_AVAILABLE = 1<<1;
+ private static final boolean DEBUG = Log.isLoggable(TAG, Log.DEBUG);
public static FlpHardwareProvider getInstance(Context context) {
if (sSingletonInstance == null) {
- sSingletonInstance = new FlpHardwareProvider(context);
- sSingletonInstance.nativeInit();
+ sSingletonInstance = new FlpHardwareProvider();
+ if (DEBUG) Log.d(TAG, "getInstance() created empty provider");
}
-
return sSingletonInstance;
}
- private FlpHardwareProvider(Context context) {
- mContext = context;
-
- // register for listening for passive provider data
- LocationManager manager = (LocationManager) mContext.getSystemService(
- Context.LOCATION_SERVICE);
- final long minTime = 0;
- final float minDistance = 0;
- final boolean oneShot = false;
- LocationRequest request = LocationRequest.createFromDeprecatedProvider(
- LocationManager.PASSIVE_PROVIDER,
- minTime,
- minDistance,
- oneShot);
- // Don't keep track of this request since it's done on behalf of other clients
- // (which are kept track of separately).
- request.setHideFromAppOps(true);
- manager.requestLocationUpdates(
- request,
- new NetworkLocationListener(),
- Looper.myLooper());
+ private FlpHardwareProvider() {
}
public static boolean isSupported() {
- return nativeIsSupported();
- }
-
- /**
- * Private callback functions used by FLP HAL.
- */
- // FlpCallbacks members
- private void onLocationReport(Location[] locations) {
- for (Location location : locations) {
- location.setProvider(LocationManager.FUSED_PROVIDER);
- // set the elapsed time-stamp just as GPS provider does
- location.setElapsedRealtimeNanos(SystemClock.elapsedRealtimeNanos());
- }
-
- IFusedLocationHardwareSink sink;
- synchronized (mLocationSinkLock) {
- sink = mLocationSink;
- }
- try {
- if (sink != null) {
- sink.onLocationAvailable(locations);
- }
- } catch (RemoteException e) {
- Log.e(TAG, "RemoteException calling onLocationAvailable");
- }
- }
-
- private void onBatchingCapabilities(int capabilities) {
- synchronized (mLocationSinkLock) {
- mHaveBatchingCapabilities = true;
- mBatchingCapabilities = capabilities;
- }
-
- maybeSendCapabilities();
-
- if (mGeofenceHardwareSink != null) {
- mGeofenceHardwareSink.setVersion(getVersion());
- }
- }
-
- private void onBatchingStatus(int status) {
- IFusedLocationHardwareSink sink;
- synchronized (mLocationSinkLock) {
- sink = mLocationSink;
- }
- try {
- if (sink != null) {
- sink.onStatusChanged(status);
- }
- } catch (RemoteException e) {
- Log.e(TAG, "RemoteException calling onBatchingStatus");
- }
- }
-
- // Returns the current version of the FLP HAL. This depends both on the version of the
- // structure returned by the hardware layer, and whether or not we've received the
- // capabilities callback on initialization. Assume original version until we get
- // the new initialization callback.
- private int getVersion() {
- synchronized (mLocationSinkLock) {
- if (mHaveBatchingCapabilities) {
- return mVersion;
- }
- }
- return 1;
- }
-
- private void setVersion(int version) {
- mVersion = version;
- if (mGeofenceHardwareSink != null) {
- mGeofenceHardwareSink.setVersion(getVersion());
- }
- }
-
- private void maybeSendCapabilities() {
- IFusedLocationHardwareSink sink;
- boolean haveBatchingCapabilities;
- int batchingCapabilities;
- synchronized (mLocationSinkLock) {
- sink = mLocationSink;
- haveBatchingCapabilities = mHaveBatchingCapabilities;
- batchingCapabilities = mBatchingCapabilities;
- }
- try {
- if (sink != null && haveBatchingCapabilities) {
- sink.onCapabilities(batchingCapabilities);
- }
- } catch (RemoteException e) {
- Log.e(TAG, "RemoteException calling onLocationAvailable");
- }
- }
-
- // FlpDiagnosticCallbacks members
- private void onDataReport(String data) {
- IFusedLocationHardwareSink sink;
- synchronized (mLocationSinkLock) {
- sink = mLocationSink;
- }
- try {
- if (mLocationSink != null) {
- sink.onDiagnosticDataAvailable(data);
- }
- } catch (RemoteException e) {
- Log.e(TAG, "RemoteException calling onDiagnosticDataAvailable");
- }
- }
-
- // FlpGeofenceCallbacks members
- private void onGeofenceTransition(
- int geofenceId,
- Location location,
- int transition,
- long timestamp,
- int sourcesUsed) {
- // the transition Id does not require translation because the values in fused_location.h
- // and GeofenceHardware are in sync
- getGeofenceHardwareSink().reportGeofenceTransition(
- geofenceId,
- updateLocationInformation(location),
- transition,
- timestamp,
- GeofenceHardware.MONITORING_TYPE_FUSED_HARDWARE,
- sourcesUsed);
- }
-
- private void onGeofenceMonitorStatus(int status, int source, Location location) {
- // allow the location to be optional in this event
- Location updatedLocation = null;
- if(location != null) {
- updatedLocation = updateLocationInformation(location);
- }
-
- int monitorStatus;
- switch (status) {
- case FLP_GEOFENCE_MONITOR_STATUS_UNAVAILABLE:
- monitorStatus = GeofenceHardware.MONITOR_CURRENTLY_UNAVAILABLE;
- break;
- case FLP_GEOFENCE_MONITOR_STATUS_AVAILABLE:
- monitorStatus = GeofenceHardware.MONITOR_CURRENTLY_AVAILABLE;
- break;
- default:
- Log.e(TAG, "Invalid FlpHal Geofence monitor status: " + status);
- monitorStatus = GeofenceHardware.MONITOR_CURRENTLY_UNAVAILABLE;
- break;
- }
-
- getGeofenceHardwareSink().reportGeofenceMonitorStatus(
- GeofenceHardware.MONITORING_TYPE_FUSED_HARDWARE,
- monitorStatus,
- updatedLocation,
- source);
+ if (DEBUG) Log.d(TAG, "isSupported() returning false");
+ return false;
}
- private void onGeofenceAdd(int geofenceId, int result) {
- getGeofenceHardwareSink().reportGeofenceAddStatus(
- geofenceId,
- translateToGeofenceHardwareStatus(result));
- }
-
- private void onGeofenceRemove(int geofenceId, int result) {
- getGeofenceHardwareSink().reportGeofenceRemoveStatus(
- geofenceId,
- translateToGeofenceHardwareStatus(result));
- }
-
- private void onGeofencePause(int geofenceId, int result) {
- getGeofenceHardwareSink().reportGeofencePauseStatus(
- geofenceId,
- translateToGeofenceHardwareStatus(result));
- }
-
- private void onGeofenceResume(int geofenceId, int result) {
- getGeofenceHardwareSink().reportGeofenceResumeStatus(
- geofenceId,
- translateToGeofenceHardwareStatus(result));
- }
-
- private void onGeofencingCapabilities(int capabilities) {
- getGeofenceHardwareSink().onCapabilities(capabilities);
- }
-
- /**
- * Private native methods accessing FLP HAL.
- */
- static { nativeClassInit(); }
-
- // Core members
- private static native void nativeClassInit();
- private static native boolean nativeIsSupported();
-
- // FlpLocationInterface members
- private native void nativeInit();
- private native int nativeGetBatchSize();
- private native void nativeStartBatching(int requestId, FusedBatchOptions options);
- private native void nativeUpdateBatchingOptions(int requestId, FusedBatchOptions optionsObject);
- private native void nativeStopBatching(int id);
- private native void nativeRequestBatchedLocation(int lastNLocations);
- private native void nativeFlushBatchedLocations();
- private native void nativeInjectLocation(Location location);
- private native void nativeCleanup();
-
- // FlpDiagnosticsInterface members
- private native boolean nativeIsDiagnosticSupported();
- private native void nativeInjectDiagnosticData(String data);
-
- // FlpDeviceContextInterface members
- private native boolean nativeIsDeviceContextSupported();
- private native void nativeInjectDeviceContext(int deviceEnabledContext);
-
- // FlpGeofencingInterface members
- private native boolean nativeIsGeofencingSupported();
- private native void nativeAddGeofences(
- GeofenceHardwareRequestParcelable[] geofenceRequestsArray);
- private native void nativePauseGeofence(int geofenceId);
- private native void nativeResumeGeofence(int geofenceId, int monitorTransitions);
- private native void nativeModifyGeofenceOption(
- int geofenceId,
- int lastTransition,
- int monitorTransitions,
- int notificationResponsiveness,
- int unknownTimer,
- int sourcesToUse);
- private native void nativeRemoveGeofences(int[] geofenceIdsArray);
-
/**
* Interface implementations for services built on top of this functionality.
*/
public static final String LOCATION = "Location";
- public static final String GEOFENCING = "Geofencing";
public IFusedLocationHardware getLocationHardware() {
- return mLocationHardware;
+ return null;
}
public IFusedGeofenceHardware getGeofenceHardware() {
- return mGeofenceHardwareService;
+ return null;
}
public void cleanup() {
- Log.i(TAG, "Calling nativeCleanup()");
- nativeCleanup();
- }
-
- private final IFusedLocationHardware mLocationHardware = new IFusedLocationHardware.Stub() {
- @Override
- public void registerSink(IFusedLocationHardwareSink eventSink) {
- synchronized (mLocationSinkLock) {
- // only one sink is allowed at the moment
- if (mLocationSink != null) {
- Log.e(TAG, "Replacing an existing IFusedLocationHardware sink");
- }
-
- mLocationSink = eventSink;
- }
- maybeSendCapabilities();
- }
-
- @Override
- public void unregisterSink(IFusedLocationHardwareSink eventSink) {
- synchronized (mLocationSinkLock) {
- // don't throw if the sink is not registered, simply make it a no-op
- if (mLocationSink == eventSink) {
- mLocationSink = null;
- }
- }
- }
-
- @Override
- public int getSupportedBatchSize() {
- return nativeGetBatchSize();
- }
-
- @Override
- public void startBatching(int requestId, FusedBatchOptions options) {
- nativeStartBatching(requestId, options);
- }
-
- @Override
- public void stopBatching(int requestId) {
- nativeStopBatching(requestId);
- }
-
- @Override
- public void updateBatchingOptions(int requestId, FusedBatchOptions options) {
- nativeUpdateBatchingOptions(requestId, options);
- }
-
- @Override
- public void requestBatchOfLocations(int batchSizeRequested) {
- nativeRequestBatchedLocation(batchSizeRequested);
- }
-
- @Override
- public void flushBatchedLocations() {
- if (getVersion() >= FIRST_VERSION_WITH_FLUSH_LOCATIONS) {
- nativeFlushBatchedLocations();
- } else {
- Log.wtf(TAG,
- "Tried to call flushBatchedLocations on an unsupported implementation");
- }
- }
-
- @Override
- public boolean supportsDiagnosticDataInjection() {
- return nativeIsDiagnosticSupported();
- }
-
- @Override
- public void injectDiagnosticData(String data) {
- nativeInjectDiagnosticData(data);
- }
-
- @Override
- public boolean supportsDeviceContextInjection() {
- return nativeIsDeviceContextSupported();
- }
-
- @Override
- public void injectDeviceContext(int deviceEnabledContext) {
- nativeInjectDeviceContext(deviceEnabledContext);
- }
-
- @Override
- public int getVersion() {
- return FlpHardwareProvider.this.getVersion();
- }
- };
-
- private final IFusedGeofenceHardware mGeofenceHardwareService =
- new IFusedGeofenceHardware.Stub() {
- @Override
- public boolean isSupported() {
- return nativeIsGeofencingSupported();
- }
-
- @Override
- public void addGeofences(GeofenceHardwareRequestParcelable[] geofenceRequestsArray) {
- nativeAddGeofences(geofenceRequestsArray);
- }
-
- @Override
- public void removeGeofences(int[] geofenceIds) {
- nativeRemoveGeofences(geofenceIds);
- }
-
- @Override
- public void pauseMonitoringGeofence(int geofenceId) {
- nativePauseGeofence(geofenceId);
- }
-
- @Override
- public void resumeMonitoringGeofence(int geofenceId, int monitorTransitions) {
- nativeResumeGeofence(geofenceId, monitorTransitions);
- }
-
- @Override
- public void modifyGeofenceOptions(int geofenceId,
- int lastTransition,
- int monitorTransitions,
- int notificationResponsiveness,
- int unknownTimer,
- int sourcesToUse) {
- nativeModifyGeofenceOption(
- geofenceId,
- lastTransition,
- monitorTransitions,
- notificationResponsiveness,
- unknownTimer,
- sourcesToUse);
- }
- };
-
- /**
- * Internal classes and functions used by the provider.
- */
- private final class NetworkLocationListener implements LocationListener {
- @Override
- public void onLocationChanged(Location location) {
- if (
- !LocationManager.NETWORK_PROVIDER.equals(location.getProvider()) ||
- !location.hasAccuracy()
- ) {
- return;
- }
-
- nativeInjectLocation(location);
- }
-
- @Override
- public void onStatusChanged(String provider, int status, Bundle extras) { }
-
- @Override
- public void onProviderEnabled(String provider) { }
-
- @Override
- public void onProviderDisabled(String provider) { }
- }
-
- private GeofenceHardwareImpl getGeofenceHardwareSink() {
- if (mGeofenceHardwareSink == null) {
- mGeofenceHardwareSink = GeofenceHardwareImpl.getInstance(mContext);
- mGeofenceHardwareSink.setVersion(getVersion());
- }
-
- return mGeofenceHardwareSink;
- }
-
- private static int translateToGeofenceHardwareStatus(int flpHalResult) {
- switch(flpHalResult) {
- case FLP_RESULT_SUCCESS:
- return GeofenceHardware.GEOFENCE_SUCCESS;
- case FLP_RESULT_ERROR:
- return GeofenceHardware.GEOFENCE_FAILURE;
- case FLP_RESULT_INSUFFICIENT_MEMORY:
- return GeofenceHardware.GEOFENCE_ERROR_INSUFFICIENT_MEMORY;
- case FLP_RESULT_TOO_MANY_GEOFENCES:
- return GeofenceHardware.GEOFENCE_ERROR_TOO_MANY_GEOFENCES;
- case FLP_RESULT_ID_EXISTS:
- return GeofenceHardware.GEOFENCE_ERROR_ID_EXISTS;
- case FLP_RESULT_ID_UNKNOWN:
- return GeofenceHardware.GEOFENCE_ERROR_ID_UNKNOWN;
- case FLP_RESULT_INVALID_GEOFENCE_TRANSITION:
- return GeofenceHardware.GEOFENCE_ERROR_INVALID_TRANSITION;
- default:
- Log.e(TAG, String.format("Invalid FlpHal result code: %d", flpHalResult));
- return GeofenceHardware.GEOFENCE_FAILURE;
- }
- }
-
- private Location updateLocationInformation(Location location) {
- location.setProvider(LocationManager.FUSED_PROVIDER);
- // set the elapsed time-stamp just as GPS provider does
- location.setElapsedRealtimeNanos(SystemClock.elapsedRealtimeNanos());
- return location;
+ if (DEBUG) Log.d(TAG, "empty cleanup()");
}
}
diff --git a/services/core/java/com/android/server/location/GnssLocationProvider.java b/services/core/java/com/android/server/location/GnssLocationProvider.java
index fb2b961fce77..eb8f8fc9af96 100644
--- a/services/core/java/com/android/server/location/GnssLocationProvider.java
+++ b/services/core/java/com/android/server/location/GnssLocationProvider.java
@@ -100,7 +100,7 @@ import java.util.HashMap;
import libcore.io.IoUtils;
/**
- * A GPS implementation of LocationProvider used by LocationManager.
+ * A GNSS implementation of LocationProvider used by LocationManager.
*
* {@hide}
*/
@@ -115,23 +115,23 @@ public class GnssLocationProvider implements LocationProviderInterface {
true, true, false, false, true, true, true,
Criteria.POWER_HIGH, Criteria.ACCURACY_FINE);
- // these need to match GpsPositionMode enum in gps.h
+ // these need to match GnssPositionMode enum in IGnss.hal
private static final int GPS_POSITION_MODE_STANDALONE = 0;
private static final int GPS_POSITION_MODE_MS_BASED = 1;
private static final int GPS_POSITION_MODE_MS_ASSISTED = 2;
- // these need to match GpsPositionRecurrence enum in gps.h
+ // these need to match GnssPositionRecurrence enum in IGnss.hal
private static final int GPS_POSITION_RECURRENCE_PERIODIC = 0;
private static final int GPS_POSITION_RECURRENCE_SINGLE = 1;
- // these need to match GpsStatusValue defines in gps.h
+ // these need to match GnssStatusValue enum in IGnssCallback.hal
private static final int GPS_STATUS_NONE = 0;
private static final int GPS_STATUS_SESSION_BEGIN = 1;
private static final int GPS_STATUS_SESSION_END = 2;
private static final int GPS_STATUS_ENGINE_ON = 3;
private static final int GPS_STATUS_ENGINE_OFF = 4;
- // these need to match GpsApgsStatusValue defines in gps.h
+ // these need to match AGnssStatusValue enum in IAGnssCallback.hal
/** AGPS status event values. */
private static final int GPS_REQUEST_AGPS_DATA_CONN = 1;
private static final int GPS_RELEASE_AGPS_DATA_CONN = 2;
@@ -139,15 +139,19 @@ public class GnssLocationProvider implements LocationProviderInterface {
private static final int GPS_AGPS_DATA_CONN_DONE = 4;
private static final int GPS_AGPS_DATA_CONN_FAILED = 5;
- // these need to match GpsLocationFlags enum in gps.h
+ // these need to match GnssLocationFlags enum in types.hal
private static final int LOCATION_INVALID = 0;
private static final int LOCATION_HAS_LAT_LONG = 1;
private static final int LOCATION_HAS_ALTITUDE = 2;
private static final int LOCATION_HAS_SPEED = 4;
private static final int LOCATION_HAS_BEARING = 8;
- private static final int LOCATION_HAS_ACCURACY = 16;
+ private static final int LOCATION_HAS_HORIZONTAL_ACCURACY = 16;
+ private static final int LOCATION_HAS_VERTICAL_ACCURACY = 32;
+ private static final int LOCATION_HAS_SPEED_ACCURACY = 64;
+ private static final int LOCATION_HAS_BEARING_ACCURACY = 128;
- // IMPORTANT - the GPS_DELETE_* symbols here must match constants in gps.h
+
+ // IMPORTANT - the GPS_DELETE_* symbols here must match GnssAidingData enum in IGnss.hal
private static final int GPS_DELETE_EPHEMERIS = 0x0001;
private static final int GPS_DELETE_ALMANAC = 0x0002;
private static final int GPS_DELETE_POSITION = 0x0004;
@@ -162,7 +166,7 @@ public class GnssLocationProvider implements LocationProviderInterface {
private static final int GPS_DELETE_CELLDB_INFO = 0x8000;
private static final int GPS_DELETE_ALL = 0xFFFF;
- // The GPS_CAPABILITY_* flags must match the values in gps.h
+ // The GPS_CAPABILITY_* flags must match Capabilities enum in IGnssCallback.hal
private static final int GPS_CAPABILITY_SCHEDULING = 0x0000001;
private static final int GPS_CAPABILITY_MSB = 0x0000002;
private static final int GPS_CAPABILITY_MSA = 0x0000004;
@@ -176,11 +180,11 @@ public class GnssLocationProvider implements LocationProviderInterface {
private static final int AGPS_SUPL_MODE_MSA = 0x02;
private static final int AGPS_SUPL_MODE_MSB = 0x01;
- // these need to match AGpsType enum in gps.h
+ // these need to match AGnssType enum in IAGnssCallback.hal
private static final int AGPS_TYPE_SUPL = 1;
private static final int AGPS_TYPE_C2K = 2;
- // these must match the definitions in gps.h
+ // these must match the ApnIpType enum in IAGnss.hal
private static final int APN_INVALID = 0;
private static final int APN_IPV4 = 1;
private static final int APN_IPV6 = 2;
@@ -227,7 +231,7 @@ public class GnssLocationProvider implements LocationProviderInterface {
private static final int GPS_GEOFENCE_UNAVAILABLE = 1<<0L;
private static final int GPS_GEOFENCE_AVAILABLE = 1<<1L;
- // GPS Geofence errors. Should match gps.h constants.
+ // GPS Geofence errors. Should match GeofenceStatus enum in IGnssGeofenceCallback.hal.
private static final int GPS_GEOFENCE_OPERATION_SUCCESS = 0;
private static final int GPS_GEOFENCE_ERROR_TOO_MANY_GEOFENCES = 100;
private static final int GPS_GEOFENCE_ERROR_ID_EXISTS = -101;
@@ -1479,7 +1483,9 @@ public class GnssLocationProvider implements LocationProviderInterface {
* called from native code to update our position.
*/
private void reportLocation(int flags, double latitude, double longitude, double altitude,
- float speedMetersPerSecond, float bearing, float accuracy, long timestamp) {
+ float speedMetersPerSecond, float bearing, float horizontalAccuracyMeters,
+ float verticalAccuracyMeters, float speedAccuracyMetersPerSeconds,
+ float bearingAccuracyDegrees, long timestamp) {
if ((flags & LOCATION_HAS_SPEED) == LOCATION_HAS_SPEED) {
mItarSpeedLimitExceeded = speedMetersPerSecond > ITAR_SPEED_LIMIT_METERS_PER_SECOND;
}
@@ -1491,7 +1497,7 @@ public class GnssLocationProvider implements LocationProviderInterface {
}
if (VERBOSE) Log.v(TAG, "reportLocation lat: " + latitude + " long: " + longitude +
- " timestamp: " + timestamp);
+ " timestamp: " + timestamp + " flags: " + flags);
synchronized (mLocation) {
mLocationFlags = flags;
@@ -1518,11 +1524,26 @@ public class GnssLocationProvider implements LocationProviderInterface {
} else {
mLocation.removeBearing();
}
- if ((flags & LOCATION_HAS_ACCURACY) == LOCATION_HAS_ACCURACY) {
- mLocation.setAccuracy(accuracy);
+ if ((flags & LOCATION_HAS_HORIZONTAL_ACCURACY) == LOCATION_HAS_HORIZONTAL_ACCURACY) {
+ mLocation.setAccuracy(horizontalAccuracyMeters);
} else {
mLocation.removeAccuracy();
}
+ if ((flags & LOCATION_HAS_VERTICAL_ACCURACY) == LOCATION_HAS_VERTICAL_ACCURACY) {
+ mLocation.setVerticalAccuracyMeters(verticalAccuracyMeters);
+ } else {
+ mLocation.removeVerticalAccuracy();
+ }
+ if((flags & LOCATION_HAS_SPEED_ACCURACY) == LOCATION_HAS_SPEED_ACCURACY) {
+ mLocation.setSpeedAccuracyMetersPerSecond(speedAccuracyMetersPerSeconds);
+ } else {
+ mLocation.removeSpeedAccuracy();
+ }
+ if((flags & LOCATION_HAS_BEARING_ACCURACY) == LOCATION_HAS_BEARING_ACCURACY) {
+ mLocation.setBearingAccuracyDegrees(bearingAccuracyDegrees);
+ } else {
+ mLocation.removeBearingAccuracy();
+ }
mLocation.setExtras(mLocationExtras);
try {
@@ -1605,13 +1626,18 @@ public class GnssLocationProvider implements LocationProviderInterface {
* called from native code to update SV info
*/
private void reportSvStatus() {
- int svCount = native_read_sv_status(mSvidWithFlags, mCn0s, mSvElevations, mSvAzimuths);
+ int svCount = native_read_sv_status(mSvidWithFlags,
+ mCn0s,
+ mSvElevations,
+ mSvAzimuths,
+ mSvCarrierFreqs);
mListenerHelper.onSvStatusChanged(
svCount,
mSvidWithFlags,
mCn0s,
mSvElevations,
- mSvAzimuths);
+ mSvAzimuths,
+ mSvCarrierFreqs);
if (VERBOSE) {
Log.v(TAG, "SV count: " + svCount);
@@ -1627,12 +1653,15 @@ public class GnssLocationProvider implements LocationProviderInterface {
" cn0: " + mCn0s[i]/10 +
" elev: " + mSvElevations[i] +
" azimuth: " + mSvAzimuths[i] +
+ " carrier frequency: " + mSvCarrierFreqs[i] +
((mSvidWithFlags[i] & GnssStatus.GNSS_SV_FLAGS_HAS_EPHEMERIS_DATA) == 0
? " " : " E") +
((mSvidWithFlags[i] & GnssStatus.GNSS_SV_FLAGS_HAS_ALMANAC_DATA) == 0
? " " : " A") +
((mSvidWithFlags[i] & GnssStatus.GNSS_SV_FLAGS_USED_IN_FIX) == 0
- ? "" : "U"));
+ ? "" : "U") +
+ ((mSvidWithFlags[i] & GnssStatus.GNSS_SV_FLAGS_HAS_CARRIER_FREQUENCY) == 0
+ ? "" : "F"));
}
}
// return number of sets used in fix instead of total
@@ -1780,7 +1809,10 @@ public class GnssLocationProvider implements LocationProviderInterface {
double altitude,
float speed,
float bearing,
- float accuracy,
+ float horizontalAccuracy,
+ float verticalAccuracy,
+ float speedAccuracy,
+ float bearingAccuracy,
long timestamp) {
Location location = new Location(LocationManager.GPS_PROVIDER);
if((flags & LOCATION_HAS_LAT_LONG) == LOCATION_HAS_LAT_LONG) {
@@ -1798,8 +1830,17 @@ public class GnssLocationProvider implements LocationProviderInterface {
if((flags & LOCATION_HAS_BEARING) == LOCATION_HAS_BEARING) {
location.setBearing(bearing);
}
- if((flags & LOCATION_HAS_ACCURACY) == LOCATION_HAS_ACCURACY) {
- location.setAccuracy(accuracy);
+ if((flags & LOCATION_HAS_HORIZONTAL_ACCURACY) == LOCATION_HAS_HORIZONTAL_ACCURACY) {
+ location.setAccuracy(horizontalAccuracy);
+ }
+ if((flags & LOCATION_HAS_VERTICAL_ACCURACY) == LOCATION_HAS_VERTICAL_ACCURACY) {
+ location.setVerticalAccuracyMeters(verticalAccuracy);
+ }
+ if((flags & LOCATION_HAS_SPEED_ACCURACY) == LOCATION_HAS_SPEED_ACCURACY) {
+ location.setSpeedAccuracyMetersPerSecond(speedAccuracy);
+ }
+ if((flags & LOCATION_HAS_BEARING_ACCURACY) == LOCATION_HAS_BEARING_ACCURACY) {
+ location.setBearingAccuracyDegrees(bearingAccuracy);
}
return location;
}
@@ -1831,8 +1872,9 @@ public class GnssLocationProvider implements LocationProviderInterface {
* All geofence callbacks are called on the same thread
*/
private void reportGeofenceTransition(int geofenceId, int flags, double latitude,
- double longitude, double altitude, float speed, float bearing, float accuracy,
- long timestamp, int transition, long transitionTimestamp) {
+ double longitude, double altitude, float speed, float bearing, float horizontalAccuracy,
+ float verticalAccuracy, float speedAccuracy, float bearingAccuracy, long timestamp,
+ int transition, long transitionTimestamp) {
if (mGeofenceHardwareImpl == null) {
mGeofenceHardwareImpl = GeofenceHardwareImpl.getInstance(mContext);
}
@@ -1843,7 +1885,10 @@ public class GnssLocationProvider implements LocationProviderInterface {
altitude,
speed,
bearing,
- accuracy,
+ horizontalAccuracy,
+ verticalAccuracy,
+ speedAccuracy,
+ bearingAccuracy,
timestamp);
mGeofenceHardwareImpl.reportGeofenceTransition(
geofenceId,
@@ -1858,8 +1903,8 @@ public class GnssLocationProvider implements LocationProviderInterface {
* called from native code to report GPS status change.
*/
private void reportGeofenceStatus(int status, int flags, double latitude,
- double longitude, double altitude, float speed, float bearing, float accuracy,
- long timestamp) {
+ double longitude, double altitude, float speed, float bearing, float horizontalAccuracy,
+ float verticalAccuracy, float speedAccuracy, float bearingAccuracy, long timestamp) {
if (mGeofenceHardwareImpl == null) {
mGeofenceHardwareImpl = GeofenceHardwareImpl.getInstance(mContext);
}
@@ -1870,7 +1915,10 @@ public class GnssLocationProvider implements LocationProviderInterface {
altitude,
speed,
bearing,
- accuracy,
+ horizontalAccuracy,
+ verticalAccuracy,
+ speedAccuracy,
+ bearingAccuracy,
timestamp);
int monitorStatus = GeofenceHardware.MONITOR_CURRENTLY_UNAVAILABLE;
if(status == GPS_GEOFENCE_AVAILABLE) {
@@ -2435,6 +2483,7 @@ public class GnssLocationProvider implements LocationProviderInterface {
private float mCn0s[] = new float[MAX_SVS];
private float mSvElevations[] = new float[MAX_SVS];
private float mSvAzimuths[] = new float[MAX_SVS];
+ private float mSvCarrierFreqs[] = new float[MAX_SVS];
private int mSvCount;
// preallocated to avoid memory allocation in reportNmea()
private byte[] mNmeaBuffer = new byte[120];
@@ -2455,7 +2504,7 @@ public class GnssLocationProvider implements LocationProviderInterface {
// returns number of SVs
// mask[0] is ephemeris mask and mask[1] is almanac mask
private native int native_read_sv_status(int[] prnWithFlags, float[] cn0s, float[] elevations,
- float[] azimuths);
+ float[] azimuths, float[] carrierFrequencies);
private native int native_read_nmea(byte[] buffer, int bufferSize);
private native void native_inject_location(double latitude, double longitude, float accuracy);
diff --git a/services/core/java/com/android/server/location/GnssStatusListenerHelper.java b/services/core/java/com/android/server/location/GnssStatusListenerHelper.java
index d471e45eacdc..fe2bb38d7c98 100644
--- a/services/core/java/com/android/server/location/GnssStatusListenerHelper.java
+++ b/services/core/java/com/android/server/location/GnssStatusListenerHelper.java
@@ -77,7 +77,8 @@ abstract class GnssStatusListenerHelper extends RemoteListenerHelper<IGnssStatus
final int[] prnWithFlags,
final float[] cn0s,
final float[] elevations,
- final float[] azimuths) {
+ final float[] azimuths,
+ final float[] carrierFreqs) {
Operation operation = new Operation() {
@Override
public void execute(IGnssStatusListener listener) throws RemoteException {
@@ -86,7 +87,8 @@ abstract class GnssStatusListenerHelper extends RemoteListenerHelper<IGnssStatus
prnWithFlags,
cn0s,
elevations,
- azimuths);
+ azimuths,
+ carrierFreqs);
}
};
foreach(operation);
diff --git a/services/core/java/com/android/server/pm/PackageManagerService.java b/services/core/java/com/android/server/pm/PackageManagerService.java
index b29bf7b90ed7..c9c855b30a29 100644
--- a/services/core/java/com/android/server/pm/PackageManagerService.java
+++ b/services/core/java/com/android/server/pm/PackageManagerService.java
@@ -576,7 +576,7 @@ public class PackageManagerService extends IPackageManager.Stub {
/**
* Whether the package parser cache is enabled.
*/
- private static final boolean DEFAULT_PACKAGE_PARSER_CACHE_ENABLED = false;
+ private static final boolean DEFAULT_PACKAGE_PARSER_CACHE_ENABLED = true;
final ServiceThread mHandlerThread;
@@ -2842,7 +2842,12 @@ public class PackageManagerService extends IPackageManager.Stub {
return null;
}
- if (SystemProperties.getBoolean("ro.boot.disable_package_cache", false)) {
+ // Disable package parsing on eng builds to allow for faster incremental development.
+ if ("eng".equals(Build.TYPE)) {
+ return null;
+ }
+
+ if (SystemProperties.getBoolean("pm.boot.disable_package_cache", false)) {
Slog.i(TAG, "Disabling package parser cache due to system property.");
return null;
}
@@ -2861,9 +2866,33 @@ public class PackageManagerService extends IPackageManager.Stub {
FileUtils.deleteContents(cacheBaseDir);
}
+
// Return the versioned package cache directory. This is something like
// "/data/system/package_cache/1"
- return FileUtils.createDir(cacheBaseDir, PACKAGE_PARSER_CACHE_VERSION);
+ File cacheDir = FileUtils.createDir(cacheBaseDir, PACKAGE_PARSER_CACHE_VERSION);
+
+ // The following is a workaround to aid development on non-numbered userdebug
+ // builds or cases where "adb sync" is used on userdebug builds. If we detect that
+ // the system partition is newer.
+ //
+ // NOTE: When no BUILD_NUMBER is set by the build system, it defaults to a build
+ // that starts with "eng." to signify that this is an engineering build and not
+ // destined for release.
+ if ("userdebug".equals(Build.TYPE) && Build.VERSION.INCREMENTAL.startsWith("eng.")) {
+ Slog.w(TAG, "Wiping cache directory because the system partition changed.");
+
+ // Heuristic: If the /system directory has been modified recently due to an "adb sync"
+ // or a regular make, then blow away the cache. Note that mtimes are *NOT* reliable
+ // in general and should not be used for production changes. In this specific case,
+ // we know that they will work.
+ File frameworkDir = new File(Environment.getRootDirectory(), "framework");
+ if (cacheDir.lastModified() < frameworkDir.lastModified()) {
+ FileUtils.deleteContents(cacheBaseDir);
+ cacheDir = FileUtils.createDir(cacheBaseDir, PACKAGE_PARSER_CACHE_VERSION);
+ }
+ }
+
+ return cacheDir;
}
@Override
@@ -12149,7 +12178,7 @@ public class PackageManagerService extends IPackageManager.Stub {
final InstallParams params = new InstallParams(origin, null /*moveInfo*/, observer,
installFlags, installerPackageName, null /*volumeUuid*/, verificationInfo, user,
null /*packageAbiOverride*/, null /*grantedPermissions*/,
- null /*certificates*/);
+ null /*certificates*/, PackageManager.INSTALL_REASON_UNKNOWN);
params.setTraceMethod("installAsUser").setTraceCookie(System.identityHashCode(params));
msg.obj = params;
@@ -12185,7 +12214,7 @@ public class PackageManagerService extends IPackageManager.Stub {
final InstallParams params = new InstallParams(origin, null, observer,
sessionParams.installFlags, installerPackageName, sessionParams.volumeUuid,
verificationInfo, user, sessionParams.abiOverride,
- sessionParams.grantedRuntimePermissions, certificates);
+ sessionParams.grantedRuntimePermissions, certificates, sessionParams.installReason);
params.setTraceMethod("installStage").setTraceCookie(System.identityHashCode(params));
msg.obj = params;
@@ -12369,7 +12398,7 @@ public class PackageManagerService extends IPackageManager.Stub {
* @hide
*/
@Override
- public int installExistingPackageAsUser(String packageName, int userId) {
+ public int installExistingPackageAsUser(String packageName, int userId, int installReason) {
mContext.enforceCallingOrSelfPermission(android.Manifest.permission.INSTALL_PACKAGES,
null);
PackageSetting pkgSetting;
@@ -12394,6 +12423,7 @@ public class PackageManagerService extends IPackageManager.Stub {
if (!pkgSetting.getInstalled(userId)) {
pkgSetting.setInstalled(true, userId);
pkgSetting.setHidden(false, userId);
+ pkgSetting.setInstallReason(installReason, userId);
mSettings.writePackageRestrictionsLPr(userId);
installed = true;
}
@@ -13408,11 +13438,12 @@ public class PackageManagerService extends IPackageManager.Stub {
final String[] grantedRuntimePermissions;
final VerificationInfo verificationInfo;
final Certificate[][] certificates;
+ final int installReason;
InstallParams(OriginInfo origin, MoveInfo move, IPackageInstallObserver2 observer,
int installFlags, String installerPackageName, String volumeUuid,
VerificationInfo verificationInfo, UserHandle user, String packageAbiOverride,
- String[] grantedPermissions, Certificate[][] certificates) {
+ String[] grantedPermissions, Certificate[][] certificates, int installReason) {
super(user);
this.origin = origin;
this.move = move;
@@ -13424,6 +13455,7 @@ public class PackageManagerService extends IPackageManager.Stub {
this.packageAbiOverride = packageAbiOverride;
this.grantedRuntimePermissions = grantedPermissions;
this.certificates = certificates;
+ this.installReason = installReason;
}
@Override
@@ -13893,6 +13925,7 @@ public class PackageManagerService extends IPackageManager.Stub {
final String traceMethod;
final int traceCookie;
final Certificate[][] certificates;
+ final int installReason;
// The list of instruction sets supported by this app. This is currently
// only used during the rmdex() phase to clean up resources. We can get rid of this
@@ -13903,7 +13936,8 @@ public class PackageManagerService extends IPackageManager.Stub {
int installFlags, String installerPackageName, String volumeUuid,
UserHandle user, String[] instructionSets,
String abiOverride, String[] installGrantPermissions,
- String traceMethod, int traceCookie, Certificate[][] certificates) {
+ String traceMethod, int traceCookie, Certificate[][] certificates,
+ int installReason) {
this.origin = origin;
this.move = move;
this.installFlags = installFlags;
@@ -13917,6 +13951,7 @@ public class PackageManagerService extends IPackageManager.Stub {
this.traceMethod = traceMethod;
this.traceCookie = traceCookie;
this.certificates = certificates;
+ this.installReason = installReason;
}
abstract int copyApk(IMediaContainerService imcs, boolean temp) throws RemoteException;
@@ -14011,7 +14046,8 @@ public class PackageManagerService extends IPackageManager.Stub {
params.installerPackageName, params.volumeUuid,
params.getUser(), null /*instructionSets*/, params.packageAbiOverride,
params.grantedRuntimePermissions,
- params.traceMethod, params.traceCookie, params.certificates);
+ params.traceMethod, params.traceCookie, params.certificates,
+ params.installReason);
if (isFwdLocked()) {
throw new IllegalArgumentException("Forward locking only supported in ASEC");
}
@@ -14020,7 +14056,8 @@ public class PackageManagerService extends IPackageManager.Stub {
/** Existing install */
FileInstallArgs(String codePath, String resourcePath, String[] instructionSets) {
super(OriginInfo.fromNothing(), null, null, 0, null, null, null, instructionSets,
- null, null, null, 0, null /*certificates*/);
+ null, null, null, 0, null /*certificates*/,
+ PackageManager.INSTALL_REASON_UNKNOWN);
this.codeFile = (codePath != null) ? new File(codePath) : null;
this.resourceFile = (resourcePath != null) ? new File(resourcePath) : null;
}
@@ -14245,15 +14282,17 @@ public class PackageManagerService extends IPackageManager.Stub {
params.installerPackageName, params.volumeUuid,
params.getUser(), null /* instruction sets */, params.packageAbiOverride,
params.grantedRuntimePermissions,
- params.traceMethod, params.traceCookie, params.certificates);
+ params.traceMethod, params.traceCookie, params.certificates,
+ params.installReason);
}
/** Existing install */
AsecInstallArgs(String fullCodePath, String[] instructionSets,
boolean isExternal, boolean isForwardLocked) {
super(OriginInfo.fromNothing(), null, null, (isExternal ? INSTALL_EXTERNAL : 0)
- | (isForwardLocked ? INSTALL_FORWARD_LOCK : 0), null, null, null,
- instructionSets, null, null, null, 0, null /*certificates*/);
+ | (isForwardLocked ? INSTALL_FORWARD_LOCK : 0), null, null, null,
+ instructionSets, null, null, null, 0, null /*certificates*/,
+ PackageManager.INSTALL_REASON_UNKNOWN);
// Hackily pretend we're still looking at a full code path
if (!fullCodePath.endsWith(RES_FILE_NAME)) {
fullCodePath = new File(fullCodePath, RES_FILE_NAME).getAbsolutePath();
@@ -14269,8 +14308,9 @@ public class PackageManagerService extends IPackageManager.Stub {
AsecInstallArgs(String cid, String[] instructionSets, boolean isForwardLocked) {
super(OriginInfo.fromNothing(), null, null, (isAsecExternal(cid) ? INSTALL_EXTERNAL : 0)
- | (isForwardLocked ? INSTALL_FORWARD_LOCK : 0), null, null, null,
- instructionSets, null, null, null, 0, null /*certificates*/);
+ | (isForwardLocked ? INSTALL_FORWARD_LOCK : 0), null, null, null,
+ instructionSets, null, null, null, 0, null /*certificates*/,
+ PackageManager.INSTALL_REASON_UNKNOWN);
this.cid = cid;
setMountPath(PackageHelper.getSdDir(cid));
}
@@ -14539,7 +14579,8 @@ public class PackageManagerService extends IPackageManager.Stub {
params.installerPackageName, params.volumeUuid,
params.getUser(), null /* instruction sets */, params.packageAbiOverride,
params.grantedRuntimePermissions,
- params.traceMethod, params.traceCookie, params.certificates);
+ params.traceMethod, params.traceCookie, params.certificates,
+ params.installReason);
}
int copyApk(IMediaContainerService imcs, boolean temp) {
@@ -14770,7 +14811,7 @@ public class PackageManagerService extends IPackageManager.Stub {
*/
private void installNewPackageLIF(PackageParser.Package pkg, final int policyFlags,
int scanFlags, UserHandle user, String installerPackageName, String volumeUuid,
- PackageInstalledInfo res) {
+ PackageInstalledInfo res, int installReason) {
Trace.traceBegin(TRACE_TAG_PACKAGE_MANAGER, "installNewPackage");
// Remember this for later, in case we need to rollback this install
@@ -14802,7 +14843,7 @@ public class PackageManagerService extends IPackageManager.Stub {
PackageParser.Package newPackage = scanPackageTracedLI(pkg, policyFlags, scanFlags,
System.currentTimeMillis(), user);
- updateSettingsLI(newPackage, installerPackageName, null, res, user);
+ updateSettingsLI(newPackage, installerPackageName, null, res, user, installReason);
if (res.returnCode == PackageManager.INSTALL_SUCCEEDED) {
prepareAppDataAfterInstallLIF(newPackage);
@@ -14864,7 +14905,8 @@ public class PackageManagerService extends IPackageManager.Stub {
}
private void replacePackageLIF(PackageParser.Package pkg, final int policyFlags, int scanFlags,
- UserHandle user, String installerPackageName, PackageInstalledInfo res) {
+ UserHandle user, String installerPackageName, PackageInstalledInfo res,
+ int installReason) {
final boolean isEphemeral = (policyFlags & PackageParser.PARSE_IS_EPHEMERAL) != 0;
final PackageParser.Package oldPackage;
@@ -14964,17 +15006,26 @@ public class PackageManagerService extends IPackageManager.Stub {
res.removedInfo.removedPackage = oldPackage.packageName;
res.removedInfo.isUpdate = true;
res.removedInfo.origUsers = installedUsers;
+ final PackageSetting ps = mSettings.getPackageLPr(pkgName);
+ res.removedInfo.installReasons = new SparseArray<>(installedUsers.length);
+ for (int i = 0; i < installedUsers.length; i++) {
+ final int userId = installedUsers[i];
+ res.removedInfo.installReasons.put(userId, ps.getInstallReason(userId));
+ }
+
final int childCount = (oldPackage.childPackages != null)
? oldPackage.childPackages.size() : 0;
for (int i = 0; i < childCount; i++) {
boolean childPackageUpdated = false;
PackageParser.Package childPkg = oldPackage.childPackages.get(i);
+ final PackageSetting childPs = mSettings.getPackageLPr(childPkg.packageName);
if (res.addedChildPackages != null) {
PackageInstalledInfo childRes = res.addedChildPackages.get(childPkg.packageName);
if (childRes != null) {
childRes.removedInfo.uid = childPkg.applicationInfo.uid;
childRes.removedInfo.removedPackage = childPkg.packageName;
childRes.removedInfo.isUpdate = true;
+ childRes.removedInfo.installReasons = res.removedInfo.installReasons;
childPackageUpdated = true;
}
}
@@ -14984,7 +15035,6 @@ public class PackageManagerService extends IPackageManager.Stub {
childRemovedRes.isUpdate = false;
childRemovedRes.dataRemoved = true;
synchronized (mPackages) {
- PackageSetting childPs = mSettings.getPackageLPr(childPkg.packageName);
if (childPs != null) {
childRemovedRes.origUsers = childPs.queryInstalledUsers(allUsers, true);
}
@@ -15007,10 +15057,10 @@ public class PackageManagerService extends IPackageManager.Stub {
| (privileged ? PackageParser.PARSE_IS_PRIVILEGED : 0);
replaceSystemPackageLIF(oldPackage, pkg, systemPolicyFlags, scanFlags,
- user, allUsers, installerPackageName, res);
+ user, allUsers, installerPackageName, res, installReason);
} else {
replaceNonSystemPackageLIF(oldPackage, pkg, policyFlags, scanFlags,
- user, allUsers, installerPackageName, res);
+ user, allUsers, installerPackageName, res, installReason);
}
}
@@ -15025,7 +15075,8 @@ public class PackageManagerService extends IPackageManager.Stub {
private void replaceNonSystemPackageLIF(PackageParser.Package deletedPackage,
PackageParser.Package pkg, final int policyFlags, int scanFlags, UserHandle user,
- int[] allUsers, String installerPackageName, PackageInstalledInfo res) {
+ int[] allUsers, String installerPackageName, PackageInstalledInfo res,
+ int installReason) {
if (DEBUG_INSTALL) Slog.d(TAG, "replaceNonSystemPackageLI: new=" + pkg + ", old="
+ deletedPackage);
@@ -15068,7 +15119,8 @@ public class PackageManagerService extends IPackageManager.Stub {
try {
final PackageParser.Package newPackage = scanPackageTracedLI(pkg, policyFlags,
scanFlags | SCAN_UPDATE_TIME, System.currentTimeMillis(), user);
- updateSettingsLI(newPackage, installerPackageName, allUsers, res, user);
+ updateSettingsLI(newPackage, installerPackageName, allUsers, res, user,
+ installReason);
// Update the in-memory copy of the previous code paths.
PackageSetting ps = mSettings.mPackages.get(pkgName);
@@ -15164,7 +15216,8 @@ public class PackageManagerService extends IPackageManager.Stub {
private void replaceSystemPackageLIF(PackageParser.Package deletedPackage,
PackageParser.Package pkg, final int policyFlags, int scanFlags, UserHandle user,
- int[] allUsers, String installerPackageName, PackageInstalledInfo res) {
+ int[] allUsers, String installerPackageName, PackageInstalledInfo res,
+ int installReason) {
if (DEBUG_INSTALL) Slog.d(TAG, "replaceSystemPackageLI: new=" + pkg
+ ", old=" + deletedPackage);
@@ -15237,7 +15290,8 @@ public class PackageManagerService extends IPackageManager.Stub {
}
}
- updateSettingsLI(newPackage, installerPackageName, allUsers, res, user);
+ updateSettingsLI(newPackage, installerPackageName, allUsers, res, user,
+ installReason);
prepareAppDataAfterInstallLIF(newPackage);
}
} catch (PackageManagerException e) {
@@ -15425,10 +15479,10 @@ public class PackageManagerService extends IPackageManager.Stub {
}
private void updateSettingsLI(PackageParser.Package newPackage, String installerPackageName,
- int[] allUsers, PackageInstalledInfo res, UserHandle user) {
+ int[] allUsers, PackageInstalledInfo res, UserHandle user, int installReason) {
// Update the parent package setting
updateSettingsInternalLI(newPackage, installerPackageName, allUsers, res.origUsers,
- res, user);
+ res, user, installReason);
// Update the child packages setting
final int childCount = (newPackage.childPackages != null)
? newPackage.childPackages.size() : 0;
@@ -15436,13 +15490,13 @@ public class PackageManagerService extends IPackageManager.Stub {
PackageParser.Package childPackage = newPackage.childPackages.get(i);
PackageInstalledInfo childRes = res.addedChildPackages.get(childPackage.packageName);
updateSettingsInternalLI(childPackage, installerPackageName, allUsers,
- childRes.origUsers, childRes, user);
+ childRes.origUsers, childRes, user, installReason);
}
}
private void updateSettingsInternalLI(PackageParser.Package newPackage,
String installerPackageName, int[] allUsers, int[] installedForUsers,
- PackageInstalledInfo res, UserHandle user) {
+ PackageInstalledInfo res, UserHandle user, int installReason) {
Trace.traceBegin(TRACE_TAG_PACKAGE_MANAGER, "updateSettings");
String pkgName = newPackage.packageName;
@@ -15500,6 +15554,30 @@ public class PackageManagerService extends IPackageManager.Stub {
ps.setInstalled(true, userId);
ps.setEnabled(COMPONENT_ENABLED_STATE_DEFAULT, userId, installerPackageName);
}
+
+ // When replacing an existing package, preserve the original install reason for all
+ // users that had the package installed before.
+ final Set<Integer> previousUserIds = new ArraySet<>();
+ if (res.removedInfo != null && res.removedInfo.installReasons != null) {
+ final int installReasonCount = res.removedInfo.installReasons.size();
+ for (int i = 0; i < installReasonCount; i++) {
+ final int previousUserId = res.removedInfo.installReasons.keyAt(i);
+ final int previousInstallReason = res.removedInfo.installReasons.valueAt(i);
+ ps.setInstallReason(previousInstallReason, previousUserId);
+ previousUserIds.add(previousUserId);
+ }
+ }
+
+ // Set install reason for users that are having the package newly installed.
+ if (userId == UserHandle.USER_ALL) {
+ for (int currentUserId : sUserManager.getUserIds()) {
+ if (!previousUserIds.contains(currentUserId)) {
+ ps.setInstallReason(installReason, currentUserId);
+ }
+ }
+ } else if (!previousUserIds.contains(userId)) {
+ ps.setInstallReason(installReason, userId);
+ }
}
res.name = pkgName;
res.uid = newPackage.applicationInfo.uid;
@@ -15877,10 +15955,10 @@ public class PackageManagerService extends IPackageManager.Stub {
"installPackageLI")) {
if (replace) {
replacePackageLIF(pkg, parseFlags, scanFlags | SCAN_REPLACING, args.user,
- installerPackageName, res);
+ installerPackageName, res, args.installReason);
} else {
installNewPackageLIF(pkg, parseFlags, scanFlags | SCAN_DELETE_DATA_ON_FAILURES,
- args.user, installerPackageName, volumeUuid, res);
+ args.user, installerPackageName, volumeUuid, res, args.installReason);
}
}
synchronized (mPackages) {
@@ -16388,6 +16466,7 @@ public class PackageManagerService extends IPackageManager.Stub {
int removedAppId = -1;
int[] origUsers;
int[] removedUsers = null;
+ SparseArray<Integer> installReasons;
boolean isRemovedPackageSystemUpdate = false;
boolean isUpdate;
boolean dataRemoved;
@@ -17026,7 +17105,8 @@ public class PackageManagerService extends IPackageManager.Stub {
false /*installed*/, true /*stopped*/, true /*notLaunched*/,
false /*hidden*/, false /*suspended*/, null, null, null,
false /*blockUninstall*/,
- ps.readUserState(nextUserId).domainVerificationStatus, 0);
+ ps.readUserState(nextUserId).domainVerificationStatus, 0,
+ PackageManager.INSTALL_REASON_UNKNOWN);
}
}
@@ -21045,7 +21125,8 @@ Slog.v(TAG, ":: stepped forward, applying functor at tag " + parser.getName());
final OriginInfo origin = OriginInfo.fromExistingFile(codeFile);
final InstallParams params = new InstallParams(origin, move, installObserver, installFlags,
installerPackageName, volumeUuid, null /*verificationInfo*/, user,
- packageAbiOverride, null /*grantedPermissions*/, null /*certificates*/);
+ packageAbiOverride, null /*grantedPermissions*/, null /*certificates*/,
+ PackageManager.INSTALL_REASON_UNKNOWN);
params.setTraceMethod("movePackage").setTraceCookie(System.identityHashCode(params));
msg.obj = params;
@@ -21829,4 +21910,18 @@ Slog.v(TAG, ":: stepped forward, applying functor at tag " + parser.getName());
public void deleteCompilerPackageStats(String pkgName) {
mCompilerStats.deletePackageStats(pkgName);
}
+
+ @Override
+ public int getInstallReason(String packageName, int userId) {
+ enforceCrossUserPermission(Binder.getCallingUid(), userId,
+ true /* requireFullPermission */, false /* checkShell */,
+ "get install reason");
+ synchronized (mPackages) {
+ final PackageSetting ps = mSettings.mPackages.get(packageName);
+ if (ps != null) {
+ return ps.getInstallReason(userId);
+ }
+ }
+ return PackageManager.INSTALL_REASON_UNKNOWN;
+ }
}
diff --git a/services/core/java/com/android/server/pm/PackageSettingBase.java b/services/core/java/com/android/server/pm/PackageSettingBase.java
index 9456a5c4b6a2..b332fa5c6f9b 100644
--- a/services/core/java/com/android/server/pm/PackageSettingBase.java
+++ b/services/core/java/com/android/server/pm/PackageSettingBase.java
@@ -292,6 +292,14 @@ abstract class PackageSettingBase extends SettingBase {
return readUserState(userId).installed;
}
+ int getInstallReason(int userId) {
+ return readUserState(userId).installReason;
+ }
+
+ void setInstallReason(int installReason, int userId) {
+ modifyUserState(userId).installReason = installReason;
+ }
+
/** Only use for testing. Do NOT use in production code. */
@VisibleForTesting
SparseArray<PackageUserState> getUserState() {
@@ -377,7 +385,7 @@ abstract class PackageSettingBase extends SettingBase {
boolean notLaunched, boolean hidden, boolean suspended,
String lastDisableAppCaller, ArraySet<String> enabledComponents,
ArraySet<String> disabledComponents, boolean blockUninstall, int domainVerifState,
- int linkGeneration) {
+ int linkGeneration, int installReason) {
PackageUserState state = modifyUserState(userId);
state.ceDataInode = ceDataInode;
state.enabled = enabled;
@@ -392,6 +400,7 @@ abstract class PackageSettingBase extends SettingBase {
state.blockUninstall = blockUninstall;
state.domainVerificationStatus = domainVerifState;
state.appLinkGeneration = linkGeneration;
+ state.installReason = installReason;
}
ArraySet<String> getEnabledComponents(int userId) {
diff --git a/services/core/java/com/android/server/pm/Settings.java b/services/core/java/com/android/server/pm/Settings.java
index 395108bfab94..8761a6dc60c1 100644
--- a/services/core/java/com/android/server/pm/Settings.java
+++ b/services/core/java/com/android/server/pm/Settings.java
@@ -216,6 +216,7 @@ final class Settings {
private static final String ATTR_ENABLED_CALLER = "enabledCaller";
private static final String ATTR_DOMAIN_VERIFICATON_STATE = "domainVerificationStatus";
private static final String ATTR_APP_LINK_GENERATION = "app-link-generation";
+ private static final String ATTR_INSTALL_REASON = "install-reason";
private static final String ATTR_PACKAGE_NAME = "packageName";
private static final String ATTR_FINGERPRINT = "fingerprint";
@@ -736,7 +737,8 @@ final class Settings {
false, // suspended
null, null, null,
false, // blockUninstall
- INTENT_FILTER_DOMAIN_VERIFICATION_STATUS_UNDEFINED, 0);
+ INTENT_FILTER_DOMAIN_VERIFICATION_STATUS_UNDEFINED, 0,
+ PackageManager.INSTALL_REASON_UNKNOWN);
}
}
}
@@ -1615,7 +1617,8 @@ final class Settings {
false, // suspended
null, null, null,
false, // blockUninstall
- INTENT_FILTER_DOMAIN_VERIFICATION_STATUS_UNDEFINED, 0);
+ INTENT_FILTER_DOMAIN_VERIFICATION_STATUS_UNDEFINED, 0,
+ PackageManager.INSTALL_REASON_UNKNOWN);
}
return;
}
@@ -1695,6 +1698,8 @@ final class Settings {
if (linkGeneration > maxAppLinkGeneration) {
maxAppLinkGeneration = linkGeneration;
}
+ final int installReason = XmlUtils.readIntAttribute(parser,
+ ATTR_INSTALL_REASON, PackageManager.INSTALL_REASON_UNKNOWN);
ArraySet<String> enabledComponents = null;
ArraySet<String> disabledComponents = null;
@@ -1717,7 +1722,7 @@ final class Settings {
ps.setUserState(userId, ceDataInode, enabled, installed, stopped, notLaunched,
hidden, suspended, enabledCaller, enabledComponents, disabledComponents,
- blockUninstall, verifState, linkGeneration);
+ blockUninstall, verifState, linkGeneration, installReason);
} else if (tagName.equals("preferred-activities")) {
readPreferredActivitiesLPw(parser, userId);
} else if (tagName.equals(TAG_PERSISTENT_PREFERRED_ACTIVITIES)) {
@@ -2004,6 +2009,10 @@ final class Settings {
XmlUtils.writeIntAttribute(serializer, ATTR_APP_LINK_GENERATION,
ustate.appLinkGeneration);
}
+ if (ustate.installReason != PackageManager.INSTALL_REASON_UNKNOWN) {
+ serializer.attribute(null, ATTR_INSTALL_REASON,
+ Integer.toString(ustate.installReason));
+ }
if (!ArrayUtils.isEmpty(ustate.enabledComponents)) {
serializer.startTag(null, TAG_ENABLED_COMPONENTS);
for (final String name : ustate.enabledComponents) {
diff --git a/services/core/java/com/android/server/wm/AppTransition.java b/services/core/java/com/android/server/wm/AppTransition.java
index 862c1455d3a1..915951381ac7 100644
--- a/services/core/java/com/android/server/wm/AppTransition.java
+++ b/services/core/java/com/android/server/wm/AppTransition.java
@@ -57,6 +57,7 @@ import android.os.Debug;
import android.os.IBinder;
import android.os.IRemoteCallback;
import android.os.RemoteException;
+import android.os.SystemProperties;
import android.util.ArraySet;
import android.util.Slog;
import android.util.SparseArray;
@@ -250,6 +251,8 @@ public class AppTransition implements Dump {
private boolean mLastHadClipReveal;
private boolean mProlongedAnimationsEnded;
+ private final boolean mGridLayoutRecentsEnabled;
+
AppTransition(Context context, WindowManagerService service) {
mContext = context;
mService = service;
@@ -288,6 +291,7 @@ public class AppTransition implements Dump {
};
mClipRevealTranslationY = (int) (CLIP_REVEAL_TRANSLATION_Y_DP
* mContext.getResources().getDisplayMetrics().density);
+ mGridLayoutRecentsEnabled = SystemProperties.getBoolean("ro.recents.grid", false);
}
boolean isTransitionSet() {
@@ -952,7 +956,7 @@ public class AppTransition implements Dump {
final float toY;
final float pivotX;
final float pivotY;
- if (isTvUiMode(uiMode) || orientation == Configuration.ORIENTATION_PORTRAIT) {
+ if (shouldScaleDownThumbnailTransition(uiMode, orientation)) {
fromX = mTmpRect.left;
fromY = mTmpRect.top;
@@ -1123,7 +1127,7 @@ public class AppTransition implements Dump {
mTmpFromClipRect.inset(contentInsets);
mNextAppTransitionInsets.set(contentInsets);
- if (isTvUiMode(uiMode) || orientation == Configuration.ORIENTATION_PORTRAIT) {
+ if (shouldScaleDownThumbnailTransition(uiMode, orientation)) {
// We scale the width and clip to the top/left square
float scale = thumbWidth /
(appWidth - contentInsets.left - contentInsets.right);
@@ -2037,6 +2041,15 @@ public class AppTransition implements Dump {
}
/**
+ * @return whether the transition should show the thumbnail being scaled down.
+ */
+ private boolean shouldScaleDownThumbnailTransition(int uiMode, int orientation) {
+ return isTvUiMode(uiMode)
+ || mGridLayoutRecentsEnabled
+ || orientation == Configuration.ORIENTATION_PORTRAIT;
+ }
+
+ /**
* @return whether the specified {@param uiMode} is the TV mode.
*/
private boolean isTvUiMode(int uiMode) {
diff --git a/services/core/java/com/android/server/wm/AppWindowContainerController.java b/services/core/java/com/android/server/wm/AppWindowContainerController.java
index 9b2d3c6b090f..a9007029d929 100644
--- a/services/core/java/com/android/server/wm/AppWindowContainerController.java
+++ b/services/core/java/com/android/server/wm/AppWindowContainerController.java
@@ -617,7 +617,7 @@ public class AppWindowContainerController
}
return dc.screenshotApplications(mToken.asBinder(), width, height,
false /* includeFullDisplay */, frameScale, Bitmap.Config.RGB_565,
- false /* wallpaperOnly */, false /* includeDecor */, true /* toAshmem */);
+ false /* wallpaperOnly */, false /* includeDecor */);
} finally {
Trace.traceEnd(Trace.TRACE_TAG_WINDOW_MANAGER);
}
diff --git a/services/core/java/com/android/server/wm/DisplayContent.java b/services/core/java/com/android/server/wm/DisplayContent.java
index 66267bdb018c..73bf3dc3e230 100644
--- a/services/core/java/com/android/server/wm/DisplayContent.java
+++ b/services/core/java/com/android/server/wm/DisplayContent.java
@@ -94,6 +94,7 @@ import android.annotation.NonNull;
import android.app.ActivityManager.StackId;
import android.content.res.Configuration;
import android.graphics.Bitmap;
+import android.graphics.GraphicBuffer;
import android.graphics.Matrix;
import android.graphics.Rect;
import android.graphics.RectF;
@@ -2105,12 +2106,55 @@ class DisplayContent extends WindowContainer<DisplayContent.DisplayChildWindowCo
* @param wallpaperOnly true if only the wallpaper layer should be included in the screenshot
* @param includeDecor whether to include window decors, like the status or navigation bar
* background of the window
- * @param toAshmem whether to convert the resulting bitmap to ashmem; this should be set to
- * true if the Bitmap is sent over binder, and false otherwise
*/
Bitmap screenshotApplications(IBinder appToken, int width, int height,
boolean includeFullDisplay, float frameScale, Bitmap.Config config,
- boolean wallpaperOnly, boolean includeDecor, boolean toAshmem) {
+ boolean wallpaperOnly, boolean includeDecor) {
+ Bitmap bitmap = screenshotApplications(appToken, width, height, includeFullDisplay,
+ frameScale, wallpaperOnly, includeDecor, SurfaceControl::screenshot);
+
+ if (DEBUG_SCREENSHOT) {
+ // TEST IF IT's ALL BLACK
+ int[] buffer = new int[bitmap.getWidth() * bitmap.getHeight()];
+ bitmap.getPixels(buffer, 0, bitmap.getWidth(), 0, 0, bitmap.getWidth(),
+ bitmap.getHeight());
+ boolean allBlack = true;
+ final int firstColor = buffer[0];
+ for (int i = 0; i < buffer.length; i++) {
+ if (buffer[i] != firstColor) {
+ allBlack = false;
+ break;
+ }
+ }
+ if (allBlack) {
+ final WindowState appWin = mScreenshotApplicationState.appWin;
+ final int maxLayer = mScreenshotApplicationState.maxLayer;
+ final int minLayer = mScreenshotApplicationState.minLayer;
+ Slog.i(TAG_WM, "Screenshot " + appWin + " was monochrome(" +
+ Integer.toHexString(firstColor) + ")! mSurfaceLayer=" +
+ (appWin != null ?
+ appWin.mWinAnimator.mSurfaceController.getLayer() : "null") +
+ " minLayer=" + minLayer + " maxLayer=" + maxLayer);
+ }
+ }
+
+ // Create a copy of the screenshot that is immutable and backed in ashmem.
+ // This greatly reduces the overhead of passing the bitmap between processes.
+ Bitmap ret = bitmap.createAshmemBitmap(config);
+ bitmap.recycle();
+ return ret;
+ }
+
+ GraphicBuffer screenshotApplicationsToBuffer(IBinder appToken, int width, int height,
+ boolean includeFullDisplay, float frameScale, boolean wallpaperOnly,
+ boolean includeDecor) {
+ return screenshotApplications(appToken, width, height, includeFullDisplay, frameScale,
+ wallpaperOnly, includeDecor, SurfaceControl::screenshotToBuffer);
+ }
+
+ private <E> E screenshotApplications(IBinder appToken, int width, int height,
+ boolean includeFullDisplay, float frameScale, boolean wallpaperOnly,
+ boolean includeDecor, Screenshoter<E> screenshoter) {
int dw = mDisplayInfo.logicalWidth;
int dh = mDisplayInfo.logicalHeight;
if (dw == 0 || dh == 0) {
@@ -2119,7 +2163,7 @@ class DisplayContent extends WindowContainer<DisplayContent.DisplayChildWindowCo
return null;
}
- Bitmap bm = null;
+ E bitmap;
mScreenshotApplicationState.reset(appToken == null && !wallpaperOnly);
final Rect frame = new Rect();
@@ -2327,48 +2371,15 @@ class DisplayContent extends WindowContainer<DisplayContent.DisplayChildWindowCo
SurfaceControl.openTransaction();
SurfaceControl.closeTransactionSync();
- bm = SurfaceControl.screenshot(crop, width, height, minLayer, maxLayer,
+ bitmap = screenshoter.screenshot(crop, width, height, minLayer, maxLayer,
inRotation, rot);
- if (bm == null) {
+ if (bitmap == null) {
Slog.w(TAG_WM, "Screenshot failure taking screenshot for (" + dw + "x" + dh
+ ") to layer " + maxLayer);
return null;
}
}
-
- if (DEBUG_SCREENSHOT) {
- // TEST IF IT's ALL BLACK
- int[] buffer = new int[bm.getWidth() * bm.getHeight()];
- bm.getPixels(buffer, 0, bm.getWidth(), 0, 0, bm.getWidth(), bm.getHeight());
- boolean allBlack = true;
- final int firstColor = buffer[0];
- for (int i = 0; i < buffer.length; i++) {
- if (buffer[i] != firstColor) {
- allBlack = false;
- break;
- }
- }
- if (allBlack) {
- final WindowState appWin = mScreenshotApplicationState.appWin;
- final int maxLayer = mScreenshotApplicationState.maxLayer;
- final int minLayer = mScreenshotApplicationState.minLayer;
- Slog.i(TAG_WM, "Screenshot " + appWin + " was monochrome(" +
- Integer.toHexString(firstColor) + ")! mSurfaceLayer=" +
- (appWin != null ?
- appWin.mWinAnimator.mSurfaceController.getLayer() : "null") +
- " minLayer=" + minLayer + " maxLayer=" + maxLayer);
- }
- }
-
- // Create a copy of the screenshot that is immutable and backed in ashmem.
- // This greatly reduces the overhead of passing the bitmap between processes.
- if (toAshmem) {
- Bitmap ret = bm.createAshmemBitmap(config);
- bm.recycle();
- return ret;
- } else {
- return bm;
- }
+ return bitmap;
}
// TODO: Can this use createRotationMatrix()?
@@ -2721,4 +2732,13 @@ class DisplayContent extends WindowContainer<DisplayContent.DisplayChildWindowCo
return mName;
}
}
+
+ /**
+ * Interface to screenshot into various types, i.e. {@link Bitmap} and {@link GraphicBuffer}.
+ */
+ @FunctionalInterface
+ private interface Screenshoter<E> {
+ E screenshot(Rect sourceCrop, int width, int height, int minLayer, int maxLayer,
+ boolean useIdentityTransform, int rotation);
+ }
}
diff --git a/services/core/java/com/android/server/wm/TaskSnapshotController.java b/services/core/java/com/android/server/wm/TaskSnapshotController.java
index 4421d618737a..68aceae8a6aa 100644
--- a/services/core/java/com/android/server/wm/TaskSnapshotController.java
+++ b/services/core/java/com/android/server/wm/TaskSnapshotController.java
@@ -96,17 +96,11 @@ class TaskSnapshotController {
if (top == null) {
return null;
}
- final Bitmap bmp = top.mDisplayContent.screenshotApplications(top.token, -1, -1, false,
- 1.0f, ARGB_8888, false, true, false);
- if (bmp == null) {
+ final GraphicBuffer buffer = top.mDisplayContent.screenshotApplicationsToBuffer(top.token,
+ -1, -1, false, 1.0f, false, true);
+ if (buffer == null) {
return null;
}
- // TODO: Already use a GraphicBuffer when snapshotting the content.
- final GraphicBuffer buffer = GraphicBuffer.create(bmp.getWidth(), bmp.getHeight(),
- RGBA_8888, USAGE_HW_TEXTURE | USAGE_SW_WRITE_NEVER | USAGE_SW_READ_NEVER);
- final Canvas c = buffer.lockCanvas();
- c.drawBitmap(bmp, 0, 0, null);
- buffer.unlockCanvasAndPost(c);
return new TaskSnapshot(buffer, top.getConfiguration().orientation,
top.findMainWindow().mStableInsets);
}
diff --git a/services/core/java/com/android/server/wm/WindowManagerService.java b/services/core/java/com/android/server/wm/WindowManagerService.java
index 195d4c31f566..7cc77de8f9e5 100644
--- a/services/core/java/com/android/server/wm/WindowManagerService.java
+++ b/services/core/java/com/android/server/wm/WindowManagerService.java
@@ -3862,8 +3862,7 @@ public class WindowManagerService extends IWindowManager.Stub
Trace.traceBegin(Trace.TRACE_TAG_WINDOW_MANAGER, "screenshotWallpaper");
return screenshotApplications(null /* appToken */, DEFAULT_DISPLAY, -1 /* width */,
-1 /* height */, true /* includeFullDisplay */, 1f /* frameScale */,
- Bitmap.Config.ARGB_8888, true /* wallpaperOnly */, false /* includeDecor */,
- true /* toAshmem */);
+ Bitmap.Config.ARGB_8888, true /* wallpaperOnly */, false /* includeDecor */);
} finally {
Trace.traceEnd(Trace.TRACE_TAG_WINDOW_MANAGER);
}
@@ -3885,7 +3884,7 @@ public class WindowManagerService extends IWindowManager.Stub
Bitmap bm = screenshotApplications(null /* appToken */, DEFAULT_DISPLAY,
-1 /* width */, -1 /* height */, true /* includeFullDisplay */,
1f /* frameScale */, Bitmap.Config.ARGB_8888, false /* wallpaperOnly */,
- false /* includeDecor */, true /* toAshmem */);
+ false /* includeDecor */);
try {
receiver.send(bm);
} catch (RemoteException e) {
@@ -3908,12 +3907,10 @@ public class WindowManagerService extends IWindowManager.Stub
* @param wallpaperOnly true if only the wallpaper layer should be included in the screenshot
* @param includeDecor whether to include window decors, like the status or navigation bar
* background of the window
- * @param toAshmem whether to convert the resulting bitmap to ashmem; this should be set to
- * true if the Bitmap is sent over binder, and false otherwise
*/
private Bitmap screenshotApplications(IBinder appToken, int displayId, int width,
int height, boolean includeFullDisplay, float frameScale, Bitmap.Config config,
- boolean wallpaperOnly, boolean includeDecor, boolean toAshmem) {
+ boolean wallpaperOnly, boolean includeDecor) {
final DisplayContent displayContent;
synchronized(mWindowMap) {
displayContent = mRoot.getDisplayContentOrCreate(displayId);
@@ -3924,7 +3921,7 @@ public class WindowManagerService extends IWindowManager.Stub
}
}
return displayContent.screenshotApplications(appToken, width, height,
- includeFullDisplay, frameScale, config, wallpaperOnly, includeDecor, toAshmem);
+ includeFullDisplay, frameScale, config, wallpaperOnly, includeDecor);
}
/**
diff --git a/services/core/jni/Android.mk b/services/core/jni/Android.mk
index d238215f7e10..eab5d8a48ba3 100644
--- a/services/core/jni/Android.mk
+++ b/services/core/jni/Android.mk
@@ -17,7 +17,6 @@ LOCAL_SRC_FILES += \
$(LOCAL_REL_DIR)/com_android_server_lights_LightsService.cpp \
$(LOCAL_REL_DIR)/com_android_server_location_ContextHubService.cpp \
$(LOCAL_REL_DIR)/com_android_server_location_GnssLocationProvider.cpp \
- $(LOCAL_REL_DIR)/com_android_server_location_FlpHardwareProvider.cpp \
$(LOCAL_REL_DIR)/com_android_server_power_PowerManagerService.cpp \
$(LOCAL_REL_DIR)/com_android_server_SerialService.cpp \
$(LOCAL_REL_DIR)/com_android_server_storage_AppFuseBridge.cpp \
diff --git a/services/core/jni/com_android_server_location_FlpHardwareProvider.cpp b/services/core/jni/com_android_server_location_FlpHardwareProvider.cpp
deleted file mode 100644
index 06d2031e4a4a..000000000000
--- a/services/core/jni/com_android_server_location_FlpHardwareProvider.cpp
+++ /dev/null
@@ -1,1101 +0,0 @@
-/*
- * Copyright (C) 2013 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/license/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.
- */
-
-#define LOG_TAG "FlpHardwareProvider"
-#define LOG_NDEBUG 0
-
-#define WAKE_LOCK_NAME "FLP"
-#define LOCATION_CLASS_NAME "android/location/Location"
-
-#include "jni.h"
-#include "JNIHelp.h"
-#include "android_runtime/AndroidRuntime.h"
-#include "android_runtime/Log.h"
-#include "hardware/fused_location.h"
-#include "hardware_legacy/power.h"
-
-static jobject sCallbacksObj = NULL;
-static JNIEnv *sCallbackEnv = NULL;
-static hw_device_t* sHardwareDevice = NULL;
-
-static jmethodID sSetVersion = NULL;
-static jmethodID sOnLocationReport = NULL;
-static jmethodID sOnDataReport = NULL;
-static jmethodID sOnBatchingCapabilities = NULL;
-static jmethodID sOnBatchingStatus = NULL;
-static jmethodID sOnGeofenceTransition = NULL;
-static jmethodID sOnGeofenceMonitorStatus = NULL;
-static jmethodID sOnGeofenceAdd = NULL;
-static jmethodID sOnGeofenceRemove = NULL;
-static jmethodID sOnGeofencePause = NULL;
-static jmethodID sOnGeofenceResume = NULL;
-static jmethodID sOnGeofencingCapabilities = NULL;
-
-static const FlpLocationInterface* sFlpInterface = NULL;
-static const FlpDiagnosticInterface* sFlpDiagnosticInterface = NULL;
-static const FlpGeofencingInterface* sFlpGeofencingInterface = NULL;
-static const FlpDeviceContextInterface* sFlpDeviceContextInterface = NULL;
-
-namespace android {
-
-static inline void CheckExceptions(JNIEnv* env, const char* methodName) {
- if(!env->ExceptionCheck()) {
- return;
- }
-
- ALOGE("An exception was thrown by '%s'.", methodName);
- LOGE_EX(env);
- env->ExceptionClear();
-}
-
-static inline void ThrowOnError(
- JNIEnv* env,
- int resultCode,
- const char* methodName) {
- if(resultCode == FLP_RESULT_SUCCESS) {
- return;
- }
-
- ALOGE("Error %d in '%s'", resultCode, methodName);
- // TODO: this layer needs to be refactored to return error codes to Java
- // raising a FatalError is harsh, and because FLP Hardware Provider is loaded inside the system
- // service, it can cause the device to reboot, or remain in a reboot loop
- // a simple exception is still dumped to logcat, but it is handled more gracefully
- jclass exceptionClass = env->FindClass("java/lang/RuntimeException");
- env->ThrowNew(exceptionClass, methodName);
-}
-
-static bool IsValidCallbackThreadEnvOnly() {
- JNIEnv* env = AndroidRuntime::getJNIEnv();
-
- if(sCallbackEnv == NULL || sCallbackEnv != env) {
- ALOGE("CallbackThread check fail: env=%p, expected=%p", env, sCallbackEnv);
- return false;
- }
-
- return true;
-}
-
-static bool IsValidCallbackThread() {
- // sCallbacksObject is created when FlpHardwareProvider on Java side is
- // initialized. Sometimes the hardware may call a function before the Java
- // side is ready. In order to prevent a system crash, check whether
- // sCallbacksObj has been created. If not, simply ignore this event from
- // hardware.
- if (sCallbacksObj == NULL) {
- ALOGE("Attempt to use FlpHardwareProvider blocked, because it hasn't been initialized.");
- return false;
- }
-
- return IsValidCallbackThreadEnvOnly();
-}
-
-static void BatchingCapabilitiesCallback(int32_t capabilities) {
- if(!IsValidCallbackThread()) {
- return;
- }
-
- sCallbackEnv->CallVoidMethod(
- sCallbacksObj,
- sOnBatchingCapabilities,
- capabilities
- );
- CheckExceptions(sCallbackEnv, __FUNCTION__);
-}
-
-static void BatchingStatusCallback(int32_t status) {
- if(!IsValidCallbackThread()) {
- return;
- }
-
- sCallbackEnv->CallVoidMethod(
- sCallbacksObj,
- sOnBatchingStatus,
- status
- );
- CheckExceptions(sCallbackEnv, __FUNCTION__);
-}
-
-static int SetThreadEvent(ThreadEvent event) {
- JavaVM* javaVm = AndroidRuntime::getJavaVM();
-
- switch(event) {
- case ASSOCIATE_JVM:
- {
- if(sCallbackEnv != NULL) {
- ALOGE(
- "Attempted to associate callback in '%s'. Callback already associated.",
- __FUNCTION__
- );
- return FLP_RESULT_ERROR;
- }
-
- JavaVMAttachArgs args = {
- JNI_VERSION_1_6,
- "FLP Service Callback Thread",
- /* group */ NULL
- };
-
- jint attachResult = javaVm->AttachCurrentThread(&sCallbackEnv, &args);
- if (attachResult != 0) {
- ALOGE("Callback thread attachment error: %d", attachResult);
- return FLP_RESULT_ERROR;
- }
-
- ALOGV("Callback thread attached: %p", sCallbackEnv);
-
- // Send the version to the upper layer.
- sCallbackEnv->CallVoidMethod(
- sCallbacksObj,
- sSetVersion,
- sFlpInterface->size == sizeof(FlpLocationInterface) ? 2 : 1
- );
- CheckExceptions(sCallbackEnv, __FUNCTION__);
- break;
- }
- case DISASSOCIATE_JVM:
- {
- if (!IsValidCallbackThreadEnvOnly()) {
- ALOGE(
- "Attempted to dissasociate an unnownk callback thread : '%s'.",
- __FUNCTION__
- );
- return FLP_RESULT_ERROR;
- }
-
- if (javaVm->DetachCurrentThread() != 0) {
- return FLP_RESULT_ERROR;
- }
-
- sCallbackEnv = NULL;
- break;
- }
- default:
- ALOGE("Invalid ThreadEvent request %d", event);
- return FLP_RESULT_ERROR;
- }
-
- return FLP_RESULT_SUCCESS;
-}
-
-/*
- * Initializes the FlpHardwareProvider class from the native side by opening
- * the HW module and obtaining the proper interfaces.
- */
-static void ClassInit(JNIEnv* env, jclass clazz) {
- sFlpInterface = NULL;
-
- // get references to the Java provider methods
- sSetVersion = env->GetMethodID(
- clazz,
- "setVersion",
- "(I)V");
- sOnLocationReport = env->GetMethodID(
- clazz,
- "onLocationReport",
- "([Landroid/location/Location;)V");
- sOnDataReport = env->GetMethodID(
- clazz,
- "onDataReport",
- "(Ljava/lang/String;)V"
- );
- sOnBatchingCapabilities = env->GetMethodID(
- clazz,
- "onBatchingCapabilities",
- "(I)V");
- sOnBatchingStatus = env->GetMethodID(
- clazz,
- "onBatchingStatus",
- "(I)V");
- sOnGeofencingCapabilities = env->GetMethodID(
- clazz,
- "onGeofencingCapabilities",
- "(I)V");
- sOnGeofenceTransition = env->GetMethodID(
- clazz,
- "onGeofenceTransition",
- "(ILandroid/location/Location;IJI)V"
- );
- sOnGeofenceMonitorStatus = env->GetMethodID(
- clazz,
- "onGeofenceMonitorStatus",
- "(IILandroid/location/Location;)V"
- );
- sOnGeofenceAdd = env->GetMethodID(clazz, "onGeofenceAdd", "(II)V");
- sOnGeofenceRemove = env->GetMethodID(clazz, "onGeofenceRemove", "(II)V");
- sOnGeofencePause = env->GetMethodID(clazz, "onGeofencePause", "(II)V");
- sOnGeofenceResume = env->GetMethodID(clazz, "onGeofenceResume", "(II)V");
-
- // open the hardware module
- const hw_module_t* module = NULL;
- int err = hw_get_module(FUSED_LOCATION_HARDWARE_MODULE_ID, &module);
- if (err != 0) {
- ALOGE("Error hw_get_module '%s': %d", FUSED_LOCATION_HARDWARE_MODULE_ID, err);
- return;
- }
-
- err = module->methods->open(
- module,
- FUSED_LOCATION_HARDWARE_MODULE_ID,
- &sHardwareDevice);
- if (err != 0) {
- ALOGE("Error opening device '%s': %d", FUSED_LOCATION_HARDWARE_MODULE_ID, err);
- return;
- }
-
- // acquire the interfaces pointers
- flp_device_t* flp_device = reinterpret_cast<flp_device_t*>(sHardwareDevice);
- sFlpInterface = flp_device->get_flp_interface(flp_device);
-
- if (sFlpInterface != NULL) {
- sFlpDiagnosticInterface = reinterpret_cast<const FlpDiagnosticInterface*>(
- sFlpInterface->get_extension(FLP_DIAGNOSTIC_INTERFACE));
-
- sFlpGeofencingInterface = reinterpret_cast<const FlpGeofencingInterface*>(
- sFlpInterface->get_extension(FLP_GEOFENCING_INTERFACE));
-
- sFlpDeviceContextInterface = reinterpret_cast<const FlpDeviceContextInterface*>(
- sFlpInterface->get_extension(FLP_DEVICE_CONTEXT_INTERFACE));
- }
-}
-
-/*
- * Helper function to unwrap a java object back into a FlpLocation structure.
- */
-static void TranslateFromObject(
- JNIEnv* env,
- jobject locationObject,
- FlpLocation& location) {
- location.size = sizeof(FlpLocation);
- location.flags = 0;
-
- jclass locationClass = env->GetObjectClass(locationObject);
-
- jmethodID getLatitude = env->GetMethodID(locationClass, "getLatitude", "()D");
- location.latitude = env->CallDoubleMethod(locationObject, getLatitude);
- jmethodID getLongitude = env->GetMethodID(locationClass, "getLongitude", "()D");
- location.longitude = env->CallDoubleMethod(locationObject, getLongitude);
- jmethodID getTime = env->GetMethodID(locationClass, "getTime", "()J");
- location.timestamp = env->CallLongMethod(locationObject, getTime);
- location.flags |= FLP_LOCATION_HAS_LAT_LONG;
-
- jmethodID hasAltitude = env->GetMethodID(locationClass, "hasAltitude", "()Z");
- if (env->CallBooleanMethod(locationObject, hasAltitude)) {
- jmethodID getAltitude = env->GetMethodID(locationClass, "getAltitude", "()D");
- location.altitude = env->CallDoubleMethod(locationObject, getAltitude);
- location.flags |= FLP_LOCATION_HAS_ALTITUDE;
- }
-
- jmethodID hasSpeed = env->GetMethodID(locationClass, "hasSpeed", "()Z");
- if (env->CallBooleanMethod(locationObject, hasSpeed)) {
- jmethodID getSpeed = env->GetMethodID(locationClass, "getSpeed", "()F");
- location.speed = env->CallFloatMethod(locationObject, getSpeed);
- location.flags |= FLP_LOCATION_HAS_SPEED;
- }
-
- jmethodID hasBearing = env->GetMethodID(locationClass, "hasBearing", "()Z");
- if (env->CallBooleanMethod(locationObject, hasBearing)) {
- jmethodID getBearing = env->GetMethodID(locationClass, "getBearing", "()F");
- location.bearing = env->CallFloatMethod(locationObject, getBearing);
- location.flags |= FLP_LOCATION_HAS_BEARING;
- }
-
- jmethodID hasAccuracy = env->GetMethodID(locationClass, "hasAccuracy", "()Z");
- if (env->CallBooleanMethod(locationObject, hasAccuracy)) {
- jmethodID getAccuracy = env->GetMethodID(
- locationClass,
- "getAccuracy",
- "()F"
- );
- location.accuracy = env->CallFloatMethod(locationObject, getAccuracy);
- location.flags |= FLP_LOCATION_HAS_ACCURACY;
- }
-
- // TODO: wire sources_used if Location class exposes them
-
- env->DeleteLocalRef(locationClass);
-}
-
-/*
- * Helper function to unwrap FlpBatchOptions from the Java Runtime calls.
- */
-static void TranslateFromObject(
- JNIEnv* env,
- jobject batchOptionsObject,
- FlpBatchOptions& batchOptions) {
- jclass batchOptionsClass = env->GetObjectClass(batchOptionsObject);
-
- jmethodID getMaxPower = env->GetMethodID(
- batchOptionsClass,
- "getMaxPowerAllocationInMW",
- "()D"
- );
- batchOptions.max_power_allocation_mW = env->CallDoubleMethod(
- batchOptionsObject,
- getMaxPower
- );
-
- jmethodID getPeriod = env->GetMethodID(
- batchOptionsClass,
- "getPeriodInNS",
- "()J"
- );
- batchOptions.period_ns = env->CallLongMethod(batchOptionsObject, getPeriod);
-
- jmethodID getSourcesToUse = env->GetMethodID(
- batchOptionsClass,
- "getSourcesToUse",
- "()I"
- );
- batchOptions.sources_to_use = env->CallIntMethod(
- batchOptionsObject,
- getSourcesToUse
- );
-
- jmethodID getSmallestDisplacementMeters = env->GetMethodID(
- batchOptionsClass,
- "getSmallestDisplacementMeters",
- "()F"
- );
- batchOptions.smallest_displacement_meters
- = env->CallFloatMethod(batchOptionsObject, getSmallestDisplacementMeters);
-
- jmethodID getFlags = env->GetMethodID(batchOptionsClass, "getFlags", "()I");
- batchOptions.flags = env->CallIntMethod(batchOptionsObject, getFlags);
-
- env->DeleteLocalRef(batchOptionsClass);
-}
-
-/*
- * Helper function to unwrap Geofence structures from the Java Runtime calls.
- */
-static void TranslateGeofenceFromGeofenceHardwareRequestParcelable(
- JNIEnv* env,
- jobject geofenceRequestObject,
- Geofence& geofence) {
- jclass geofenceRequestClass = env->GetObjectClass(geofenceRequestObject);
-
- jmethodID getId = env->GetMethodID(geofenceRequestClass, "getId", "()I");
- geofence.geofence_id = env->CallIntMethod(geofenceRequestObject, getId);
-
- jmethodID getType = env->GetMethodID(geofenceRequestClass, "getType", "()I");
- // this works because GeofenceHardwareRequest.java and fused_location.h have
- // the same notion of geofence types
- GeofenceType type = (GeofenceType)env->CallIntMethod(geofenceRequestObject, getType);
- if(type != TYPE_CIRCLE) {
- ThrowOnError(env, FLP_RESULT_ERROR, __FUNCTION__);
- }
- geofence.data->type = type;
- GeofenceCircle& circle = geofence.data->geofence.circle;
-
- jmethodID getLatitude = env->GetMethodID(
- geofenceRequestClass,
- "getLatitude",
- "()D");
- circle.latitude = env->CallDoubleMethod(geofenceRequestObject, getLatitude);
-
- jmethodID getLongitude = env->GetMethodID(
- geofenceRequestClass,
- "getLongitude",
- "()D");
- circle.longitude = env->CallDoubleMethod(geofenceRequestObject, getLongitude);
-
- jmethodID getRadius = env->GetMethodID(geofenceRequestClass, "getRadius", "()D");
- circle.radius_m = env->CallDoubleMethod(geofenceRequestObject, getRadius);
-
- GeofenceOptions* options = geofence.options;
- jmethodID getMonitorTransitions = env->GetMethodID(
- geofenceRequestClass,
- "getMonitorTransitions",
- "()I");
- options->monitor_transitions = env->CallIntMethod(
- geofenceRequestObject,
- getMonitorTransitions);
-
- jmethodID getUnknownTimer = env->GetMethodID(
- geofenceRequestClass,
- "getUnknownTimer",
- "()I");
- options->unknown_timer_ms = env->CallIntMethod(geofenceRequestObject, getUnknownTimer);
-
- jmethodID getNotificationResponsiveness = env->GetMethodID(
- geofenceRequestClass,
- "getNotificationResponsiveness",
- "()I");
- options->notification_responsivenes_ms = env->CallIntMethod(
- geofenceRequestObject,
- getNotificationResponsiveness);
-
- jmethodID getLastTransition = env->GetMethodID(
- geofenceRequestClass,
- "getLastTransition",
- "()I");
- options->last_transition = env->CallIntMethod(geofenceRequestObject, getLastTransition);
-
- jmethodID getSourceTechnologies =
- env->GetMethodID(geofenceRequestClass, "getSourceTechnologies", "()I");
- options->sources_to_use = env->CallIntMethod(geofenceRequestObject, getSourceTechnologies);
-
- env->DeleteLocalRef(geofenceRequestClass);
-}
-
-/*
- * Helper function to transform FlpLocation into a java object.
- */
-static void TranslateToObject(const FlpLocation* location, jobject& locationObject) {
- jclass locationClass = sCallbackEnv->FindClass(LOCATION_CLASS_NAME);
- jmethodID locationCtor = sCallbackEnv->GetMethodID(
- locationClass,
- "<init>",
- "(Ljava/lang/String;)V"
- );
-
- // the provider is set in the upper JVM layer
- locationObject = sCallbackEnv->NewObject(locationClass, locationCtor, NULL);
- jint flags = location->flags;
-
- // set the valid information in the object
- if (flags & FLP_LOCATION_HAS_LAT_LONG) {
- jmethodID setLatitude = sCallbackEnv->GetMethodID(
- locationClass,
- "setLatitude",
- "(D)V"
- );
- sCallbackEnv->CallVoidMethod(locationObject, setLatitude, location->latitude);
-
- jmethodID setLongitude = sCallbackEnv->GetMethodID(
- locationClass,
- "setLongitude",
- "(D)V"
- );
- sCallbackEnv->CallVoidMethod(
- locationObject,
- setLongitude,
- location->longitude
- );
-
- jmethodID setTime = sCallbackEnv->GetMethodID(
- locationClass,
- "setTime",
- "(J)V"
- );
- sCallbackEnv->CallVoidMethod(locationObject, setTime, location->timestamp);
- }
-
- if (flags & FLP_LOCATION_HAS_ALTITUDE) {
- jmethodID setAltitude = sCallbackEnv->GetMethodID(
- locationClass,
- "setAltitude",
- "(D)V"
- );
- sCallbackEnv->CallVoidMethod(locationObject, setAltitude, location->altitude);
- }
-
- if (flags & FLP_LOCATION_HAS_SPEED) {
- jmethodID setSpeed = sCallbackEnv->GetMethodID(
- locationClass,
- "setSpeed",
- "(F)V"
- );
- sCallbackEnv->CallVoidMethod(locationObject, setSpeed, location->speed);
- }
-
- if (flags & FLP_LOCATION_HAS_BEARING) {
- jmethodID setBearing = sCallbackEnv->GetMethodID(
- locationClass,
- "setBearing",
- "(F)V"
- );
- sCallbackEnv->CallVoidMethod(locationObject, setBearing, location->bearing);
- }
-
- if (flags & FLP_LOCATION_HAS_ACCURACY) {
- jmethodID setAccuracy = sCallbackEnv->GetMethodID(
- locationClass,
- "setAccuracy",
- "(F)V"
- );
- sCallbackEnv->CallVoidMethod(locationObject, setAccuracy, location->accuracy);
- }
-
- // TODO: wire FlpLocation::sources_used when needed
-
- sCallbackEnv->DeleteLocalRef(locationClass);
-}
-
-/*
- * Helper function to serialize FlpLocation structures.
- */
-static void TranslateToObjectArray(
- int32_t locationsCount,
- FlpLocation** locations,
- jobjectArray& locationsArray) {
- jclass locationClass = sCallbackEnv->FindClass(LOCATION_CLASS_NAME);
- locationsArray = sCallbackEnv->NewObjectArray(
- locationsCount,
- locationClass,
- /* initialElement */ NULL
- );
-
- for (int i = 0; i < locationsCount; ++i) {
- jobject locationObject = NULL;
- TranslateToObject(locations[i], locationObject);
- sCallbackEnv->SetObjectArrayElement(locationsArray, i, locationObject);
- sCallbackEnv->DeleteLocalRef(locationObject);
- }
-
- sCallbackEnv->DeleteLocalRef(locationClass);
-}
-
-static void LocationCallback(int32_t locationsCount, FlpLocation** locations) {
- if(!IsValidCallbackThread()) {
- return;
- }
-
- if(locationsCount == 0 || locations == NULL) {
- ALOGE(
- "Invalid LocationCallback. Count: %d, Locations: %p",
- locationsCount,
- locations
- );
- return;
- }
-
- jobjectArray locationsArray = NULL;
- TranslateToObjectArray(locationsCount, locations, locationsArray);
-
- sCallbackEnv->CallVoidMethod(
- sCallbacksObj,
- sOnLocationReport,
- locationsArray
- );
- CheckExceptions(sCallbackEnv, __FUNCTION__);
-
- if(locationsArray != NULL) {
- sCallbackEnv->DeleteLocalRef(locationsArray);
- }
-}
-
-static void AcquireWakelock() {
- acquire_wake_lock(PARTIAL_WAKE_LOCK, WAKE_LOCK_NAME);
-}
-
-static void ReleaseWakelock() {
- release_wake_lock(WAKE_LOCK_NAME);
-}
-
-FlpCallbacks sFlpCallbacks = {
- sizeof(FlpCallbacks),
- LocationCallback,
- AcquireWakelock,
- ReleaseWakelock,
- SetThreadEvent,
- BatchingCapabilitiesCallback,
- BatchingStatusCallback
-};
-
-static void ReportData(char* data, int length) {
- jstring stringData = NULL;
-
- if(length != 0 && data != NULL) {
- stringData = sCallbackEnv->NewString(reinterpret_cast<jchar*>(data), length);
- } else {
- ALOGE("Invalid ReportData callback. Length: %d, Data: %p", length, data);
- return;
- }
-
- sCallbackEnv->CallVoidMethod(sCallbacksObj, sOnDataReport, stringData);
- CheckExceptions(sCallbackEnv, __FUNCTION__);
-}
-
-FlpDiagnosticCallbacks sFlpDiagnosticCallbacks = {
- sizeof(FlpDiagnosticCallbacks),
- SetThreadEvent,
- ReportData
-};
-
-static void GeofenceTransitionCallback(
- int32_t geofenceId,
- FlpLocation* location,
- int32_t transition,
- FlpUtcTime timestamp,
- uint32_t sourcesUsed
- ) {
- if(!IsValidCallbackThread()) {
- return;
- }
-
- if(location == NULL) {
- ALOGE("GeofenceTransition received with invalid location: %p", location);
- return;
- }
-
- jobject locationObject = NULL;
- TranslateToObject(location, locationObject);
-
- sCallbackEnv->CallVoidMethod(
- sCallbacksObj,
- sOnGeofenceTransition,
- geofenceId,
- locationObject,
- transition,
- timestamp,
- sourcesUsed
- );
- CheckExceptions(sCallbackEnv, __FUNCTION__);
-
- if(locationObject != NULL) {
- sCallbackEnv->DeleteLocalRef(locationObject);
- }
-}
-
-static void GeofenceMonitorStatusCallback(
- int32_t status,
- uint32_t source,
- FlpLocation* lastLocation) {
- if(!IsValidCallbackThread()) {
- return;
- }
-
- jobject locationObject = NULL;
- if(lastLocation != NULL) {
- TranslateToObject(lastLocation, locationObject);
- }
-
- sCallbackEnv->CallVoidMethod(
- sCallbacksObj,
- sOnGeofenceMonitorStatus,
- status,
- source,
- locationObject
- );
- CheckExceptions(sCallbackEnv, __FUNCTION__);
-
- if(locationObject != NULL) {
- sCallbackEnv->DeleteLocalRef(locationObject);
- }
-}
-
-static void GeofenceAddCallback(int32_t geofenceId, int32_t result) {
- if(!IsValidCallbackThread()) {
- return;
- }
-
- sCallbackEnv->CallVoidMethod(sCallbacksObj, sOnGeofenceAdd, geofenceId, result);
- CheckExceptions(sCallbackEnv, __FUNCTION__);
-}
-
-static void GeofenceRemoveCallback(int32_t geofenceId, int32_t result) {
- if(!IsValidCallbackThread()) {
- return;
- }
-
- sCallbackEnv->CallVoidMethod(
- sCallbacksObj,
- sOnGeofenceRemove,
- geofenceId,
- result
- );
- CheckExceptions(sCallbackEnv, __FUNCTION__);
-}
-
-static void GeofencePauseCallback(int32_t geofenceId, int32_t result) {
- if(!IsValidCallbackThread()) {
- return;
- }
-
- sCallbackEnv->CallVoidMethod(
- sCallbacksObj,
- sOnGeofencePause,
- geofenceId,
- result
- );
- CheckExceptions(sCallbackEnv, __FUNCTION__);
-}
-
-static void GeofenceResumeCallback(int32_t geofenceId, int32_t result) {
- if(!IsValidCallbackThread()) {
- return;
- }
-
- sCallbackEnv->CallVoidMethod(
- sCallbacksObj,
- sOnGeofenceResume,
- geofenceId,
- result
- );
- CheckExceptions(sCallbackEnv, __FUNCTION__);
-}
-
-static void GeofencingCapabilitiesCallback(int32_t capabilities) {
- if(!IsValidCallbackThread()) {
- return;
- }
-
- sCallbackEnv->CallVoidMethod(
- sCallbacksObj,
- sOnGeofencingCapabilities,
- capabilities
- );
- CheckExceptions(sCallbackEnv, __FUNCTION__);
-}
-
-FlpGeofenceCallbacks sFlpGeofenceCallbacks = {
- sizeof(FlpGeofenceCallbacks),
- GeofenceTransitionCallback,
- GeofenceMonitorStatusCallback,
- GeofenceAddCallback,
- GeofenceRemoveCallback,
- GeofencePauseCallback,
- GeofenceResumeCallback,
- SetThreadEvent,
- GeofencingCapabilitiesCallback
-};
-
-/*
- * Initializes the Fused Location Provider in the native side. It ensures that
- * the Flp interfaces are initialized properly.
- */
-static void Init(JNIEnv* env, jobject obj) {
- if(sCallbacksObj == NULL) {
- sCallbacksObj = env->NewGlobalRef(obj);
- }
-
- // initialize the Flp interfaces
- if(sFlpInterface == NULL || sFlpInterface->init(&sFlpCallbacks) != 0) {
- ThrowOnError(env, FLP_RESULT_ERROR, __FUNCTION__);
- }
-
- if(sFlpDiagnosticInterface != NULL) {
- sFlpDiagnosticInterface->init(&sFlpDiagnosticCallbacks);
- }
-
- if(sFlpGeofencingInterface != NULL) {
- sFlpGeofencingInterface->init(&sFlpGeofenceCallbacks);
- }
-
- // TODO: inject any device context if when needed
-}
-
-static jboolean IsSupported(JNIEnv* /* env */, jclass /* clazz */) {
- if (sFlpInterface == NULL) {
- return JNI_FALSE;
- }
- return JNI_TRUE;
-}
-
-static jint GetBatchSize(JNIEnv* env, jobject /* object */) {
- if(sFlpInterface == NULL) {
- ThrowOnError(env, FLP_RESULT_ERROR, __FUNCTION__);
- }
-
- return sFlpInterface->get_batch_size();
-}
-
-static void StartBatching(
- JNIEnv* env,
- jobject /* object */,
- jint id,
- jobject optionsObject) {
- if(sFlpInterface == NULL || optionsObject == NULL) {
- ThrowOnError(env, FLP_RESULT_ERROR, __FUNCTION__);
- }
-
- FlpBatchOptions options;
- TranslateFromObject(env, optionsObject, options);
- int result = sFlpInterface->start_batching(id, &options);
- ThrowOnError(env, result, __FUNCTION__);
-}
-
-static void UpdateBatchingOptions(
- JNIEnv* env,
- jobject /* object */,
- jint id,
- jobject optionsObject) {
- if(sFlpInterface == NULL || optionsObject == NULL) {
- ThrowOnError(env, FLP_RESULT_ERROR, __FUNCTION__);
- }
-
- FlpBatchOptions options;
- TranslateFromObject(env, optionsObject, options);
- int result = sFlpInterface->update_batching_options(id, &options);
- ThrowOnError(env, result, __FUNCTION__);
-}
-
-static void StopBatching(JNIEnv* env, jobject /* object */, jint id) {
- if(sFlpInterface == NULL) {
- ThrowOnError(env, FLP_RESULT_ERROR, __FUNCTION__);
- }
-
- sFlpInterface->stop_batching(id);
-}
-
-static void Cleanup(JNIEnv* env, jobject /* object */) {
- if(sFlpInterface == NULL) {
- ThrowOnError(env, FLP_RESULT_ERROR, __FUNCTION__);
- }
-
- sFlpInterface->cleanup();
-
- if(sCallbacksObj != NULL) {
- env->DeleteGlobalRef(sCallbacksObj);
- sCallbacksObj = NULL;
- }
-}
-
-static void GetBatchedLocation(JNIEnv* env, jobject /* object */, jint lastNLocations) {
- if(sFlpInterface == NULL) {
- ThrowOnError(env, FLP_RESULT_ERROR, __FUNCTION__);
- }
-
- sFlpInterface->get_batched_location(lastNLocations);
-}
-
-static void FlushBatchedLocations(JNIEnv* env, jobject /* object */) {
- if(sFlpInterface == NULL) {
- ThrowOnError(env, FLP_RESULT_ERROR, __FUNCTION__);
- }
-
- sFlpInterface->flush_batched_locations();
-}
-
-static void InjectLocation(JNIEnv* env, jobject /* object */, jobject locationObject) {
- if(locationObject == NULL) {
- ALOGE("Invalid location for injection: %p", locationObject);
- ThrowOnError(env, FLP_RESULT_ERROR, __FUNCTION__);
- }
-
- if(sFlpInterface == NULL) {
- // there is no listener, bail
- return;
- }
-
- FlpLocation location;
- TranslateFromObject(env, locationObject, location);
- int result = sFlpInterface->inject_location(&location);
- if (result != FLP_RESULT_SUCCESS) {
- // do not throw but log, this operation should be fire and forget
- ALOGE("Error %d in '%s'", result, __FUNCTION__);
- }
-}
-
-static jboolean IsDiagnosticSupported() {
- return sFlpDiagnosticInterface != NULL;
-}
-
-static void InjectDiagnosticData(JNIEnv* env, jobject /* object */, jstring stringData) {
- if(stringData == NULL) {
- ALOGE("Invalid diagnostic data for injection: %p", stringData);
- ThrowOnError(env, FLP_RESULT_ERROR, __FUNCTION__);
- }
-
- if(sFlpDiagnosticInterface == NULL) {
- ThrowOnError(env, FLP_RESULT_ERROR, __FUNCTION__);
- }
-
- int length = env->GetStringLength(stringData);
- const jchar* data = env->GetStringChars(stringData, /* isCopy */ NULL);
- if(data == NULL) {
- ThrowOnError(env, FLP_RESULT_ERROR, __FUNCTION__);
- }
-
- int result = sFlpDiagnosticInterface->inject_data((char*) data, length);
- ThrowOnError(env, result, __FUNCTION__);
-}
-
-static jboolean IsDeviceContextSupported() {
- return sFlpDeviceContextInterface != NULL;
-}
-
-static void InjectDeviceContext(JNIEnv* env, jobject /* object */, jint enabledMask) {
- if(sFlpDeviceContextInterface == NULL) {
- ThrowOnError(env, FLP_RESULT_ERROR, __FUNCTION__);
- }
-
- int result = sFlpDeviceContextInterface->inject_device_context(enabledMask);
- ThrowOnError(env, result, __FUNCTION__);
-}
-
-static jboolean IsGeofencingSupported() {
- return sFlpGeofencingInterface != NULL;
-}
-
-static void AddGeofences(
- JNIEnv* env,
- jobject /* object */,
- jobjectArray geofenceRequestsArray) {
- if(geofenceRequestsArray == NULL) {
- ALOGE("Invalid Geofences to add: %p", geofenceRequestsArray);
- ThrowOnError(env, FLP_RESULT_ERROR, __FUNCTION__);
- }
-
- if (sFlpGeofencingInterface == NULL) {
- ThrowOnError(env, FLP_RESULT_ERROR, __FUNCTION__);
- }
-
- jint geofenceRequestsCount = env->GetArrayLength(geofenceRequestsArray);
- if(geofenceRequestsCount == 0) {
- return;
- }
-
- Geofence* geofences = new Geofence[geofenceRequestsCount];
- if (geofences == NULL) {
- ThrowOnError(env, FLP_RESULT_INSUFFICIENT_MEMORY, __FUNCTION__);
- }
-
- for (int i = 0; i < geofenceRequestsCount; ++i) {
- geofences[i].data = new GeofenceData();
- geofences[i].options = new GeofenceOptions();
- jobject geofenceObject = env->GetObjectArrayElement(geofenceRequestsArray, i);
-
- TranslateGeofenceFromGeofenceHardwareRequestParcelable(env, geofenceObject, geofences[i]);
- env->DeleteLocalRef(geofenceObject);
- }
-
- sFlpGeofencingInterface->add_geofences(geofenceRequestsCount, &geofences);
- if (geofences != NULL) {
- for(int i = 0; i < geofenceRequestsCount; ++i) {
- delete geofences[i].data;
- delete geofences[i].options;
- }
- delete[] geofences;
- }
-}
-
-static void PauseGeofence(JNIEnv* env, jobject /* object */, jint geofenceId) {
- if(sFlpGeofencingInterface == NULL) {
- ThrowOnError(env, FLP_RESULT_ERROR, __FUNCTION__);
- }
-
- sFlpGeofencingInterface->pause_geofence(geofenceId);
-}
-
-static void ResumeGeofence(
- JNIEnv* env,
- jobject /* object */,
- jint geofenceId,
- jint monitorTransitions) {
- if(sFlpGeofencingInterface == NULL) {
- ThrowOnError(env, FLP_RESULT_ERROR, __FUNCTION__);
- }
-
- sFlpGeofencingInterface->resume_geofence(geofenceId, monitorTransitions);
-}
-
-static void ModifyGeofenceOption(
- JNIEnv* env,
- jobject /* object */,
- jint geofenceId,
- jint lastTransition,
- jint monitorTransitions,
- jint notificationResponsiveness,
- jint unknownTimer,
- jint sourcesToUse) {
- if(sFlpGeofencingInterface == NULL) {
- ThrowOnError(env, FLP_RESULT_ERROR, __FUNCTION__);
- }
-
- GeofenceOptions options = {
- lastTransition,
- monitorTransitions,
- notificationResponsiveness,
- unknownTimer,
- (uint32_t)sourcesToUse
- };
-
- sFlpGeofencingInterface->modify_geofence_option(geofenceId, &options);
-}
-
-static void RemoveGeofences(
- JNIEnv* env,
- jobject /* object */,
- jintArray geofenceIdsArray) {
- if(sFlpGeofencingInterface == NULL) {
- ThrowOnError(env, FLP_RESULT_ERROR, __FUNCTION__);
- }
-
- jsize geofenceIdsCount = env->GetArrayLength(geofenceIdsArray);
- jint* geofenceIds = env->GetIntArrayElements(geofenceIdsArray, /* isCopy */ NULL);
- if(geofenceIds == NULL) {
- ThrowOnError(env, FLP_RESULT_ERROR, __FUNCTION__);
- }
-
- sFlpGeofencingInterface->remove_geofences(geofenceIdsCount, geofenceIds);
- env->ReleaseIntArrayElements(geofenceIdsArray, geofenceIds, 0 /*mode*/);
-}
-
-static const JNINativeMethod sMethods[] = {
- //{"name", "signature", functionPointer }
- {"nativeClassInit", "()V", reinterpret_cast<void*>(ClassInit)},
- {"nativeInit", "()V", reinterpret_cast<void*>(Init)},
- {"nativeCleanup", "()V", reinterpret_cast<void*>(Cleanup)},
- {"nativeIsSupported", "()Z", reinterpret_cast<void*>(IsSupported)},
- {"nativeGetBatchSize", "()I", reinterpret_cast<void*>(GetBatchSize)},
- {"nativeStartBatching",
- "(ILandroid/location/FusedBatchOptions;)V",
- reinterpret_cast<void*>(StartBatching)},
- {"nativeUpdateBatchingOptions",
- "(ILandroid/location/FusedBatchOptions;)V",
- reinterpret_cast<void*>(UpdateBatchingOptions)},
- {"nativeStopBatching", "(I)V", reinterpret_cast<void*>(StopBatching)},
- {"nativeRequestBatchedLocation",
- "(I)V",
- reinterpret_cast<void*>(GetBatchedLocation)},
- {"nativeFlushBatchedLocations",
- "()V",
- reinterpret_cast<void*>(FlushBatchedLocations)},
- {"nativeInjectLocation",
- "(Landroid/location/Location;)V",
- reinterpret_cast<void*>(InjectLocation)},
- {"nativeIsDiagnosticSupported",
- "()Z",
- reinterpret_cast<void*>(IsDiagnosticSupported)},
- {"nativeInjectDiagnosticData",
- "(Ljava/lang/String;)V",
- reinterpret_cast<void*>(InjectDiagnosticData)},
- {"nativeIsDeviceContextSupported",
- "()Z",
- reinterpret_cast<void*>(IsDeviceContextSupported)},
- {"nativeInjectDeviceContext",
- "(I)V",
- reinterpret_cast<void*>(InjectDeviceContext)},
- {"nativeIsGeofencingSupported",
- "()Z",
- reinterpret_cast<void*>(IsGeofencingSupported)},
- {"nativeAddGeofences",
- "([Landroid/hardware/location/GeofenceHardwareRequestParcelable;)V",
- reinterpret_cast<void*>(AddGeofences)},
- {"nativePauseGeofence", "(I)V", reinterpret_cast<void*>(PauseGeofence)},
- {"nativeResumeGeofence", "(II)V", reinterpret_cast<void*>(ResumeGeofence)},
- {"nativeModifyGeofenceOption",
- "(IIIIII)V",
- reinterpret_cast<void*>(ModifyGeofenceOption)},
- {"nativeRemoveGeofences", "([I)V", reinterpret_cast<void*>(RemoveGeofences)}
-};
-
-/*
- * Registration method invoked on JNI Load.
- */
-int register_android_server_location_FlpHardwareProvider(JNIEnv* env) {
- return jniRegisterNativeMethods(
- env,
- "com/android/server/location/FlpHardwareProvider",
- sMethods,
- NELEM(sMethods)
- );
-}
-
-} /* name-space Android */
diff --git a/services/core/jni/com_android_server_location_GnssLocationProvider.cpp b/services/core/jni/com_android_server_location_GnssLocationProvider.cpp
index fa6405a1dd87..5a20bed04d6e 100644
--- a/services/core/jni/com_android_server_location_GnssLocationProvider.cpp
+++ b/services/core/jni/com_android_server_location_GnssLocationProvider.cpp
@@ -318,7 +318,10 @@ Return<void> GnssCallback::gnssLocationCb(
static_cast<jdouble>(location.altitudeMeters),
static_cast<jfloat>(location.speedMetersPerSec),
static_cast<jfloat>(location.bearingDegrees),
- static_cast<jfloat>(location.accuracyMeters),
+ static_cast<jfloat>(location.horizontalAccuracyMeters),
+ static_cast<jfloat>(location.verticalAccuracyMeters),
+ static_cast<jfloat>(location.speedAccuracyMetersPerSecond),
+ static_cast<jfloat>(location.bearingAccuracyDegrees),
static_cast<jlong>(location.timestamp));
checkAndClearExceptionFromCallback(env, __FUNCTION__);
return Void();
@@ -458,7 +461,10 @@ Return<void> GnssGeofenceCallback::gnssGeofenceTransitionCb(
static_cast<jdouble>(location.altitudeMeters),
static_cast<jfloat>(location.speedMetersPerSec),
static_cast<jfloat>(location.bearingDegrees),
- static_cast<jfloat>(location.accuracyMeters),
+ static_cast<jfloat>(location.horizontalAccuracyMeters),
+ static_cast<jfloat>(location.verticalAccuracyMeters),
+ static_cast<jfloat>(location.speedAccuracyMetersPerSecond),
+ static_cast<jfloat>(location.bearingAccuracyDegrees),
static_cast<jlong>(location.timestamp),
transition,
timestamp);
@@ -480,7 +486,10 @@ Return<void> GnssGeofenceCallback::gnssGeofenceStatusCb(
static_cast<jdouble>(location.altitudeMeters),
static_cast<jfloat>(location.speedMetersPerSec),
static_cast<jfloat>(location.bearingDegrees),
- static_cast<jfloat>(location.accuracyMeters),
+ static_cast<jfloat>(location.horizontalAccuracyMeters),
+ static_cast<jfloat>(location.verticalAccuracyMeters),
+ static_cast<jfloat>(location.speedAccuracyMetersPerSecond),
+ static_cast<jfloat>(location.bearingAccuracyDegrees),
static_cast<jlong>(location.timestamp));
checkAndClearExceptionFromCallback(env, __FUNCTION__);
return Void();
@@ -663,6 +672,10 @@ jobject GnssMeasurementCallback::translateGnssMeasurement(
SET(SnrInDb, measurement->snrDb);
}
+ if (flags & static_cast<uint32_t>(GnssMeasurementFlags::HAS_AUTOMATIC_GAIN_CONTROL)) {
+ SET(AgcLevelDb, measurement->agcLevelDb);
+ }
+
return object.get();
}
@@ -913,7 +926,7 @@ Return<void> AGnssRilCallback::requestRefLocCb() {
}
static void android_location_GnssLocationProvider_class_init_native(JNIEnv* env, jclass clazz) {
- method_reportLocation = env->GetMethodID(clazz, "reportLocation", "(IDDDFFFJ)V");
+ method_reportLocation = env->GetMethodID(clazz, "reportLocation", "(IDDDFFFFFFJ)V");
method_reportStatus = env->GetMethodID(clazz, "reportStatus", "(I)V");
method_reportSvStatus = env->GetMethodID(clazz, "reportSvStatus", "()V");
method_reportAGpsStatus = env->GetMethodID(clazz, "reportAGpsStatus", "(II[B)V");
@@ -927,9 +940,9 @@ static void android_location_GnssLocationProvider_class_init_native(JNIEnv* env,
method_requestSetID = env->GetMethodID(clazz, "requestSetID", "(I)V");
method_requestUtcTime = env->GetMethodID(clazz, "requestUtcTime", "()V");
method_reportGeofenceTransition = env->GetMethodID(clazz, "reportGeofenceTransition",
- "(IIDDDFFFJIJ)V");
+ "(IIDDDFFFFFFJIJ)V");
method_reportGeofenceStatus = env->GetMethodID(clazz, "reportGeofenceStatus",
- "(IIDDDFFFJ)V");
+ "(IIDDDFFFFFFJ)V");
method_reportGeofenceAddStatus = env->GetMethodID(clazz, "reportGeofenceAddStatus",
"(II)V");
method_reportGeofenceRemoveStatus = env->GetMethodID(clazz, "reportGeofenceRemoveStatus",
@@ -1170,7 +1183,7 @@ enum ShiftWidth: uint8_t {
static jint android_location_GnssLocationProvider_read_sv_status(JNIEnv* env, jobject /* obj */,
jintArray svidWithFlagArray, jfloatArray cn0Array, jfloatArray elevArray,
- jfloatArray azumArray) {
+ jfloatArray azumArray, jfloatArray carrierFreqArray) {
/*
* This method should only be called from within a call to reportSvStatus.
*/
@@ -1178,6 +1191,7 @@ static jint android_location_GnssLocationProvider_read_sv_status(JNIEnv* env, jo
jfloat* cn0s = env->GetFloatArrayElements(cn0Array, 0);
jfloat* elev = env->GetFloatArrayElements(elevArray, 0);
jfloat* azim = env->GetFloatArrayElements(azumArray, 0);
+ jfloat* carrierFreq = env->GetFloatArrayElements(carrierFreqArray, 0);
/*
* Read GNSS SV info.
@@ -1190,12 +1204,14 @@ static jint android_location_GnssLocationProvider_read_sv_status(JNIEnv* env, jo
cn0s[i] = info.cN0Dbhz;
elev[i] = info.elevationDegrees;
azim[i] = info.azimuthDegrees;
+ carrierFreq[i] = info.carrierFrequencyHz;
}
env->ReleaseIntArrayElements(svidWithFlagArray, svidWithFlags, 0);
env->ReleaseFloatArrayElements(cn0Array, cn0s, 0);
env->ReleaseFloatArrayElements(elevArray, elev, 0);
env->ReleaseFloatArrayElements(azumArray, azim, 0);
+ env->ReleaseFloatArrayElements(carrierFreqArray, carrierFreq, 0);
return static_cast<jint>(GnssCallback::sGnssSvListSize);
}
@@ -1378,7 +1394,12 @@ static jstring android_location_GnssLocationProvider_get_internal_state(JNIEnv*
internalState << "Gnss Location Data:: LatitudeDegrees: " << data.position.latitudeDegrees
<< ", LongitudeDegrees: " << data.position.longitudeDegrees
<< ", altitudeMeters: " << data.position.altitudeMeters
- << ", accuracyMeters: " << data.position.accuracyMeters
+ << ", speedMetersPerSecond: " << data.position.speedMetersPerSec
+ << ", bearingDegrees: " << data.position.bearingDegrees
+ << ", horizontalAccuracyMeters: " << data.position.horizontalAccuracyMeters
+ << ", verticalAccuracyMeters: " << data.position.verticalAccuracyMeters
+ << ", speedAccuracyMetersPerSecond: " << data.position.speedAccuracyMetersPerSecond
+ << ", bearingAccuracyDegrees: " << data.position.bearingAccuracyDegrees
<< ", ageSeconds: " << data.position.ageSeconds << std::endl;
}
@@ -1706,7 +1727,7 @@ static const JNINativeMethod sMethods[] = {
"(I)V",
reinterpret_cast<void*>(android_location_GnssLocationProvider_delete_aiding_data)},
{"native_read_sv_status",
- "([I[F[F[F)I",
+ "([I[F[F[F[F)I",
reinterpret_cast<void *>(android_location_GnssLocationProvider_read_sv_status)},
{"native_read_nmea", "([BI)I", reinterpret_cast<void *>(
android_location_GnssLocationProvider_read_nmea)},
diff --git a/services/core/jni/onload.cpp b/services/core/jni/onload.cpp
index 975867852f5c..6f505d51b15c 100644
--- a/services/core/jni/onload.cpp
+++ b/services/core/jni/onload.cpp
@@ -38,7 +38,6 @@ int register_android_server_vr_VrManagerService(JNIEnv* env);
int register_android_server_VibratorService(JNIEnv* env);
int register_android_server_location_ContextHubService(JNIEnv* env);
int register_android_server_location_GnssLocationProvider(JNIEnv* env);
-int register_android_server_location_FlpHardwareProvider(JNIEnv* env);
int register_android_server_connectivity_Vpn(JNIEnv* env);
int register_android_server_hdmi_HdmiCecController(JNIEnv* env);
int register_android_server_tv_TvUinputBridge(JNIEnv* env);
@@ -76,7 +75,6 @@ extern "C" jint JNI_OnLoad(JavaVM* vm, void* /* reserved */)
register_android_server_SystemServer(env);
register_android_server_location_ContextHubService(env);
register_android_server_location_GnssLocationProvider(env);
- register_android_server_location_FlpHardwareProvider(env);
register_android_server_connectivity_Vpn(env);
register_android_server_ConsumerIrService(env);
register_android_server_BatteryStatsService(env);
diff --git a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
index f1a28e2b8a18..040188dded2c 100644
--- a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
+++ b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
@@ -17,7 +17,6 @@
package com.android.server.devicepolicy;
import static android.Manifest.permission.MANAGE_CA_CERTIFICATES;
-import static android.app.admin.DevicePolicyManager.ACTION_SHOW_DEVICE_MONITORING_DIALOG;
import static android.app.admin.DevicePolicyManager.CODE_ACCOUNTS_NOT_EMPTY;
import static android.app.admin.DevicePolicyManager.CODE_ADD_MANAGED_PROFILE_DISALLOWED;
import static android.app.admin.DevicePolicyManager.CODE_CANNOT_ADD_MANAGED_PROFILE;
@@ -49,7 +48,6 @@ import android.Manifest.permission;
import android.accessibilityservice.AccessibilityServiceInfo;
import android.accounts.Account;
import android.accounts.AccountManager;
-import android.annotation.IntDef;
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.annotation.UserIdInt;
@@ -180,8 +178,6 @@ import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.PrintWriter;
-import java.lang.annotation.Retention;
-import java.lang.annotation.RetentionPolicy;
import java.nio.charset.StandardCharsets;
import java.security.cert.CertificateException;
import java.security.cert.CertificateFactory;
@@ -337,7 +333,8 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub {
private static final long MINIMUM_STRONG_AUTH_TIMEOUT_MS = 1 * 60 * 60 * 1000; // 1h
/**
- * Strings logged with {@link #PROVISIONING_ENTRY_POINT_ADB}.
+ * Strings logged with {@link
+ * com.android.internal.logging.nano.MetricsProto.MetricsEvent#PROVISIONING_ENTRY_POINT_ADB}.
*/
private static final String LOG_TAG_PROFILE_OWNER = "profile-owner";
private static final String LOG_TAG_DEVICE_OWNER = "device-owner";
@@ -552,11 +549,25 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub {
}
if (Intent.ACTION_USER_ADDED.equals(action)) {
sendUserAddedOrRemovedCommand(DeviceAdminReceiver.ACTION_USER_ADDED, userHandle);
- disableDeviceOwnerManagedSingleUserFeaturesIfNeeded();
+ synchronized (DevicePolicyManagerService.this) {
+ // It might take a while for the user to become affiliated. Make security
+ // and network logging unavailable in the meantime.
+ maybePauseDeviceWideLoggingLocked();
+ }
} else if (Intent.ACTION_USER_REMOVED.equals(action)) {
sendUserAddedOrRemovedCommand(DeviceAdminReceiver.ACTION_USER_REMOVED, userHandle);
- disableDeviceOwnerManagedSingleUserFeaturesIfNeeded();
- removeUserData(userHandle);
+ synchronized (DevicePolicyManagerService.this) {
+ // Check whether the user is affiliated, *before* removing its data.
+ boolean isRemovedUserAffiliated = isUserAffiliatedWithDeviceLocked(userHandle);
+ removeUserData(userHandle);
+ if (!isRemovedUserAffiliated) {
+ // We discard the logs when unaffiliated users are deleted (so that the
+ // device owner cannot retrieve data about that user after it's gone).
+ discardDeviceWideLogsLocked();
+ // Resume logging if all remaining users are affiliated.
+ maybeResumeDeviceWideLoggingLocked();
+ }
+ }
} else if (Intent.ACTION_USER_STARTED.equals(action)) {
synchronized (DevicePolicyManagerService.this) {
// Reset the policy data
@@ -1858,9 +1869,10 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub {
if (mOwners.hasDeviceOwner()) {
mInjector.systemPropertiesSet(PROPERTY_DEVICE_OWNER_PRESENT, "true");
Slog.i(LOG_TAG, "Set ro.device_owner property to true");
- disableDeviceOwnerManagedSingleUserFeaturesIfNeeded();
+
if (mInjector.securityLogGetLoggingEnabledProperty()) {
mSecurityLogMonitor.start();
+ maybePauseDeviceWideLoggingLocked();
}
} else {
mInjector.systemPropertiesSet(PROPERTY_DEVICE_OWNER_PRESENT, "false");
@@ -2218,15 +2230,13 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub {
/**
* Send an update to all admins of a user that enforce a specified policy.
*/
- void sendAdminCommandLocked(String action, int reqPolicy, int userHandle) {
+ void sendAdminCommandLocked(String action, int reqPolicy, int userHandle, Bundle adminExtras) {
final DevicePolicyData policy = getUserData(userHandle);
final int count = policy.mAdminList.size();
- if (count > 0) {
- for (int i = 0; i < count; i++) {
- final ActiveAdmin admin = policy.mAdminList.get(i);
- if (admin.info.usesPolicy(reqPolicy)) {
- sendAdminCommandLocked(admin, action);
- }
+ for (int i = 0; i < count; i++) {
+ final ActiveAdmin admin = policy.mAdminList.get(i);
+ if (admin.info.usesPolicy(reqPolicy)) {
+ sendAdminCommandLocked(admin, action, adminExtras, null);
}
}
}
@@ -2236,10 +2246,10 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub {
* enforce a specified policy.
*/
private void sendAdminCommandToSelfAndProfilesLocked(String action, int reqPolicy,
- int userHandle) {
+ int userHandle, Bundle adminExtras) {
int[] profileIds = mUserManager.getProfileIdsWithDisabled(userHandle);
for (int profileId : profileIds) {
- sendAdminCommandLocked(action, reqPolicy, profileId);
+ sendAdminCommandLocked(action, reqPolicy, profileId, adminExtras);
}
}
@@ -2248,10 +2258,12 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub {
*/
private void sendAdminCommandForLockscreenPoliciesLocked(
String action, int reqPolicy, int userHandle) {
+ final Bundle extras = new Bundle();
+ extras.putParcelable(Intent.EXTRA_USER, UserHandle.of(userHandle));
if (isSeparateProfileChallengeEnabled(userHandle)) {
- sendAdminCommandLocked(action, reqPolicy, userHandle);
+ sendAdminCommandLocked(action, reqPolicy, userHandle, extras);
} else {
- sendAdminCommandToSelfAndProfilesLocked(action, reqPolicy, userHandle);
+ sendAdminCommandToSelfAndProfilesLocked(action, reqPolicy, userHandle, extras);
}
}
@@ -2829,6 +2841,9 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub {
}
private void handlePasswordExpirationNotification(int userHandle) {
+ final Bundle adminExtras = new Bundle();
+ adminExtras.putParcelable(Intent.EXTRA_USER, UserHandle.of(userHandle));
+
synchronized (this) {
final long now = System.currentTimeMillis();
@@ -2842,7 +2857,7 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub {
&& now >= admin.passwordExpirationDate - EXPIRATION_GRACE_PERIOD_MS
&& admin.passwordExpirationDate > 0L) {
sendAdminCommandLocked(admin,
- DeviceAdminReceiver.ACTION_PASSWORD_EXPIRING);
+ DeviceAdminReceiver.ACTION_PASSWORD_EXPIRING, adminExtras, null);
}
}
setExpirationAlarmCheckLocked(mContext, userHandle, /* parent */ false);
@@ -3152,7 +3167,7 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub {
}
// It's temporary solution to clear DISALLOW_ADD_USER after CTS
- // TODO: b/31952368 when the restriction is moved from system to the device owner,
+ // STOPSHIP(b/31952368) when the restriction is moved from system to the device owner,
// it can be removed.
private void clearDeviceOwnerUserRestrictionLocked(UserHandle userHandle) {
if (mUserManager.hasUserRestriction(UserManager.DISALLOW_ADD_USER, userHandle)) {
@@ -5647,34 +5662,12 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub {
}
}
- private boolean isDeviceOwnerManagedSingleUserDevice() {
- synchronized (this) {
- if (!mOwners.hasDeviceOwner()) {
- return false;
- }
- }
- final long callingIdentity = mInjector.binderClearCallingIdentity();
- try {
- if (mInjector.userManagerIsSplitSystemUser()) {
- // In split system user mode, only allow the case where the device owner is managing
- // the only non-system user of the device
- return (mUserManager.getUserCount() == 2
- && mOwners.getDeviceOwnerUserId() != UserHandle.USER_SYSTEM);
- } else {
- return mUserManager.getUserCount() == 1;
- }
- } finally {
- mInjector.binderRestoreCallingIdentity(callingIdentity);
- }
- }
-
- private void ensureDeviceOwnerManagingSingleUser(ComponentName who) throws SecurityException {
+ private void ensureDeviceOwnerAndAllUsersAffiliated(ComponentName who) throws SecurityException {
synchronized (this) {
getActiveAdminForCallerLocked(who, DeviceAdminInfo.USES_POLICY_DEVICE_OWNER);
- }
- if (!isDeviceOwnerManagedSingleUserDevice()) {
- throw new SecurityException(
- "There should only be one user, managed by Device Owner");
+ if (!areAllUsersAffiliatedWithDeviceLocked()) {
+ throw new SecurityException("Not all users are affiliated.");
+ }
}
}
@@ -5684,7 +5677,11 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub {
return false;
}
Preconditions.checkNotNull(who, "ComponentName is null");
- ensureDeviceOwnerManagingSingleUser(who);
+
+ // TODO: If an unaffiliated user is removed, the admin will be able to request a bugreport
+ // which could still contain data related to that user. Should we disallow that, e.g. until
+ // next boot? Might not be needed given that this still requires user consent.
+ ensureDeviceOwnerAndAllUsersAffiliated(who);
if (mRemoteBugreportServiceIsActive.get()
|| (getDeviceOwnerRemoteBugreportUri() != null)) {
@@ -5709,7 +5706,8 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub {
mRemoteBugreportServiceIsActive.set(true);
mRemoteBugreportSharingAccepted.set(false);
registerRemoteBugreportReceivers();
- mInjector.getNotificationManager().notifyAsUser(LOG_TAG, RemoteBugreportUtils.NOTIFICATION_ID,
+ mInjector.getNotificationManager().notifyAsUser(LOG_TAG,
+ RemoteBugreportUtils.NOTIFICATION_ID,
RemoteBugreportUtils.buildNotification(mContext,
DevicePolicyManager.NOTIFICATION_BUGREPORT_STARTED), UserHandle.ALL);
mHandler.postDelayed(mRemoteBugreportTimeoutRunnable,
@@ -6256,6 +6254,7 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub {
admin.userRestrictions = null;
admin.defaultEnabledRestrictionsAlreadySet.clear();
admin.forceEphemeralUsers = false;
+ admin.isNetworkLoggingEnabled = false;
mUserManagerInternal.setForceEphemeralUsers(admin.forceEphemeralUsers);
final DevicePolicyData policyData = getUserData(UserHandle.USER_SYSTEM);
policyData.mLastSecurityLogRetrievalTime = -1;
@@ -6268,7 +6267,11 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub {
mOwners.clearDeviceOwner();
mOwners.writeDeviceOwner();
updateDeviceOwnerLocked();
- disableDeviceOwnerManagedSingleUserFeaturesIfNeeded();
+
+ mInjector.securityLogSetLoggingEnabledProperty(false);
+ mSecurityLogMonitor.stop();
+ setNetworkLoggingActiveInternal(false);
+
try {
if (mInjector.getIBackupManager() != null) {
// Reactivate backup service.
@@ -7604,7 +7607,8 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub {
try {
// Install the profile owner if not present.
if (!mIPackageManager.isPackageAvailable(adminPkg, userHandle)) {
- mIPackageManager.installExistingPackageAsUser(adminPkg, userHandle);
+ mIPackageManager.installExistingPackageAsUser(adminPkg, userHandle,
+ PackageManager.INSTALL_REASON_POLICY);
}
} catch (RemoteException e) {
Slog.e(LOG_TAG, "Failed to make remote calls for createAndManageUser, "
@@ -7903,7 +7907,8 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub {
}
// Install the app.
- mIPackageManager.installExistingPackageAsUser(packageName, userId);
+ mIPackageManager.installExistingPackageAsUser(packageName, userId,
+ PackageManager.INSTALL_REASON_POLICY);
} catch (RemoteException re) {
// shouldn't happen
@@ -7945,7 +7950,8 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub {
String packageName = info.activityInfo.packageName;
if (isSystemApp(mIPackageManager, packageName, parentUserId)) {
numberOfAppsInstalled++;
- mIPackageManager.installExistingPackageAsUser(packageName, userId);
+ mIPackageManager.installExistingPackageAsUser(packageName, userId,
+ PackageManager.INSTALL_REASON_POLICY);
} else {
Slog.d(LOG_TAG, "Not enabling " + packageName + " since is not a"
+ " system app");
@@ -8255,7 +8261,7 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub {
synchronized (this) {
getActiveAdminForCallerLocked(who, DeviceAdminInfo.USES_POLICY_PROFILE_OWNER);
final int userHandle = mInjector.userHandleGetCallingUserId();
- if (isUserAffiliatedWithDevice(userHandle)) {
+ if (isUserAffiliatedWithDeviceLocked(userHandle)) {
setLockTaskPackagesLocked(userHandle, new ArrayList<>(Arrays.asList(packages)));
} else {
throw new SecurityException("Admin " + who +
@@ -9436,6 +9442,12 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub {
getUserData(UserHandle.USER_SYSTEM).mAffiliationIds = affiliationIds;
saveSettingsLocked(UserHandle.USER_SYSTEM);
}
+
+ // Affiliation status for any user, not just the calling user, might have changed.
+ // The device owner user will still be affiliated after changing its affiliation ids,
+ // but as a result of that other users might become affiliated or un-affiliated.
+ maybePauseDeviceWideLoggingLocked();
+ maybeResumeDeviceWideLoggingLocked();
}
}
@@ -9455,84 +9467,78 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub {
@Override
public boolean isAffiliatedUser() {
- return isUserAffiliatedWithDevice(mInjector.userHandleGetCallingUserId());
- }
+ if (!mHasFeature) {
+ return false;
+ }
- private boolean isUserAffiliatedWithDevice(int userId) {
synchronized (this) {
- if (!mOwners.hasDeviceOwner()) {
- return false;
- }
- if (userId == mOwners.getDeviceOwnerUserId()) {
- // The user that the DO is installed on is always affiliated with the device.
- return true;
- }
- if (userId == UserHandle.USER_SYSTEM) {
- // The system user is always affiliated in a DO device, even if the DO is set on a
- // different user. This could be the case if the DO is set in the primary user
- // of a split user device.
- return true;
- }
- final ComponentName profileOwner = getProfileOwner(userId);
- if (profileOwner == null) {
- return false;
- }
- final Set<String> userAffiliationIds = getUserData(userId).mAffiliationIds;
- final Set<String> deviceAffiliationIds =
- getUserData(UserHandle.USER_SYSTEM).mAffiliationIds;
- for (String id : userAffiliationIds) {
- if (deviceAffiliationIds.contains(id)) {
- return true;
- }
- }
+ return isUserAffiliatedWithDeviceLocked(mInjector.userHandleGetCallingUserId());
}
- return false;
}
- private synchronized void disableDeviceOwnerManagedSingleUserFeaturesIfNeeded() {
- final boolean isSingleUserManagedDevice = isDeviceOwnerManagedSingleUserDevice();
-
- // disable security logging if needed
- if (!isSingleUserManagedDevice) {
- mInjector.securityLogSetLoggingEnabledProperty(false);
- Slog.w(LOG_TAG, "Security logging turned off as it's no longer a single user managed"
- + " device.");
+ private boolean isUserAffiliatedWithDeviceLocked(int userId) {
+ if (!mOwners.hasDeviceOwner()) {
+ return false;
}
-
- // disable backup service if needed
- // note: when clearing DO, the backup service shouldn't be disabled if it was enabled by
- // the device owner
- if (mOwners.hasDeviceOwner() && !isSingleUserManagedDevice) {
- setBackupServiceEnabledInternal(false);
- Slog.w(LOG_TAG, "Backup is off as it's a managed device that has more that one user.");
+ if (userId == mOwners.getDeviceOwnerUserId()) {
+ // The user that the DO is installed on is always affiliated with the device.
+ return true;
+ }
+ if (userId == UserHandle.USER_SYSTEM) {
+ // The system user is always affiliated in a DO device, even if the DO is set on a
+ // different user. This could be the case if the DO is set in the primary user
+ // of a split user device.
+ return true;
+ }
+ final ComponentName profileOwner = getProfileOwner(userId);
+ if (profileOwner == null) {
+ return false;
+ }
+ final Set<String> userAffiliationIds = getUserData(userId).mAffiliationIds;
+ final Set<String> deviceAffiliationIds =
+ getUserData(UserHandle.USER_SYSTEM).mAffiliationIds;
+ for (String id : userAffiliationIds) {
+ if (deviceAffiliationIds.contains(id)) {
+ return true;
+ }
}
+ return false;
+ }
- // disable network logging if needed
- if (!isSingleUserManagedDevice) {
- setNetworkLoggingActiveInternal(false);
- Slog.w(LOG_TAG, "Network logging turned off as it's no longer a single user managed"
- + " device.");
- // if there still is a device owner, disable logging policy, otherwise the admin
- // has been nuked
- if (mOwners.hasDeviceOwner()) {
- getDeviceOwnerAdminLocked().isNetworkLoggingEnabled = false;
- saveSettingsLocked(mOwners.getDeviceOwnerUserId());
+ private boolean areAllUsersAffiliatedWithDeviceLocked() {
+ final long ident = mInjector.binderClearCallingIdentity();
+ try {
+ final List<UserInfo> userInfos = mUserManager.getUsers();
+ for (int i = 0; i < userInfos.size(); i++) {
+ int userId = userInfos.get(i).id;
+ if (!isUserAffiliatedWithDeviceLocked(userId)) {
+ Slog.d(LOG_TAG, "User id " + userId + " not affiliated.");
+ return false;
+ }
}
+ } finally {
+ mInjector.binderRestoreCallingIdentity(ident);
}
+
+ return true;
}
@Override
public void setSecurityLoggingEnabled(ComponentName admin, boolean enabled) {
+ if (!mHasFeature) {
+ return;
+ }
Preconditions.checkNotNull(admin);
- ensureDeviceOwnerManagingSingleUser(admin);
synchronized (this) {
+ getActiveAdminForCallerLocked(admin, DeviceAdminInfo.USES_POLICY_DEVICE_OWNER);
if (enabled == mInjector.securityLogGetLoggingEnabledProperty()) {
return;
}
mInjector.securityLogSetLoggingEnabledProperty(enabled);
if (enabled) {
mSecurityLogMonitor.start();
+ maybePauseDeviceWideLoggingLocked();
} else {
mSecurityLogMonitor.stop();
}
@@ -9541,6 +9547,10 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub {
@Override
public boolean isSecurityLoggingEnabled(ComponentName admin) {
+ if (!mHasFeature) {
+ return false;
+ }
+
Preconditions.checkNotNull(admin);
synchronized (this) {
getActiveAdminForCallerLocked(admin, DeviceAdminInfo.USES_POLICY_DEVICE_OWNER);
@@ -9559,10 +9569,15 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub {
@Override
public ParceledListSlice<SecurityEvent> retrievePreRebootSecurityLogs(ComponentName admin) {
+ if (!mHasFeature) {
+ return null;
+ }
+
Preconditions.checkNotNull(admin);
- ensureDeviceOwnerManagingSingleUser(admin);
+ ensureDeviceOwnerAndAllUsersAffiliated(admin);
- if (!mContext.getResources().getBoolean(R.bool.config_supportPreRebootSecurityLogs)) {
+ if (!mContext.getResources().getBoolean(R.bool.config_supportPreRebootSecurityLogs)
+ || !mInjector.securityLogGetLoggingEnabledProperty()) {
return null;
}
@@ -9580,8 +9595,16 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub {
@Override
public ParceledListSlice<SecurityEvent> retrieveSecurityLogs(ComponentName admin) {
+ if (!mHasFeature) {
+ return null;
+ }
+
Preconditions.checkNotNull(admin);
- ensureDeviceOwnerManagingSingleUser(admin);
+ ensureDeviceOwnerAndAllUsersAffiliated(admin);
+
+ if (!mInjector.securityLogGetLoggingEnabledProperty()) {
+ return null;
+ }
recordSecurityLogRetrievalTime();
@@ -9788,18 +9811,21 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub {
}
}
+ // TODO(b/22388012): When backup is available for secondary users and profiles, consider
+ // whether there are any privacy/security implications of enabling the backup service here
+ // if there are other users or profiles unmanaged or managed by a different entity (i.e. not
+ // affiliated).
@Override
public void setBackupServiceEnabled(ComponentName admin, boolean enabled) {
- Preconditions.checkNotNull(admin);
if (!mHasFeature) {
return;
}
- ensureDeviceOwnerManagingSingleUser(admin);
- setBackupServiceEnabledInternal(enabled);
- }
+ Preconditions.checkNotNull(admin);
+ synchronized (this) {
+ getActiveAdminForCallerLocked(admin, DeviceAdminInfo.USES_POLICY_DEVICE_OWNER);
+ }
- private synchronized void setBackupServiceEnabledInternal(boolean enabled) {
- long ident = mInjector.binderClearCallingIdentity();
+ final long ident = mInjector.binderClearCallingIdentity();
try {
IBackupManager ibm = mInjector.getIBackupManager();
if (ibm != null) {
@@ -9900,7 +9926,7 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub {
final boolean isCallerDeviceOwner = isDeviceOwner(callingOwner);
final boolean isCallerManagedProfile = isManagedProfile(callingUserId);
if ((!isCallerDeviceOwner && !isCallerManagedProfile)
- || !isUserAffiliatedWithDevice(callingUserId)) {
+ || !isUserAffiliatedWithDeviceLocked(callingUserId)) {
return targetUsers;
}
@@ -9920,7 +9946,7 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub {
// Both must be the same package and be affiliated in order to bind.
if (callingOwnerPackage.equals(targetOwnerPackage)
- && isUserAffiliatedWithDevice(userId)) {
+ && isUserAffiliatedWithDeviceLocked(userId)) {
targetUsers.add(UserHandle.of(userId));
}
}
@@ -10018,7 +10044,7 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub {
return;
}
Preconditions.checkNotNull(admin);
- ensureDeviceOwnerManagingSingleUser(admin);
+ getActiveAdminForCallerLocked(admin, DeviceAdminInfo.USES_POLICY_DEVICE_OWNER);
if (enabled == isNetworkLoggingEnabledInternalLocked()) {
// already in the requested state
@@ -10045,10 +10071,10 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub {
Slog.wtf(LOG_TAG, "Network logging could not be started due to the logging"
+ " service not being available yet.");
}
+ maybePauseDeviceWideLoggingLocked();
sendNetworkLoggingNotificationLocked();
} else {
if (mNetworkLogger != null && !mNetworkLogger.stopNetworkLogging()) {
- mNetworkLogger = null;
Slog.wtf(LOG_TAG, "Network logging could not be stopped due to the logging"
+ " service not being available yet.");
}
@@ -10060,6 +10086,44 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub {
}
}
+ /** Pauses security and network logging if there are unaffiliated users on the device */
+ private void maybePauseDeviceWideLoggingLocked() {
+ if (!areAllUsersAffiliatedWithDeviceLocked()) {
+ Slog.i(LOG_TAG, "There are unaffiliated users, security and network logging will be "
+ + "paused if enabled.");
+ mSecurityLogMonitor.pause();
+ if (mNetworkLogger != null) {
+ mNetworkLogger.pause();
+ }
+ }
+ }
+
+ /** Resumes security and network logging (if they are enabled) if all users are affiliated */
+ private void maybeResumeDeviceWideLoggingLocked() {
+ if (areAllUsersAffiliatedWithDeviceLocked()) {
+ final long ident = mInjector.binderClearCallingIdentity();
+ try {
+ mSecurityLogMonitor.resume();
+ if (mNetworkLogger != null) {
+ mNetworkLogger.resume();
+ }
+ } finally {
+ mInjector.binderRestoreCallingIdentity(ident);
+ }
+ }
+ }
+
+ /** Deletes any security and network logs that might have been collected so far */
+ private void discardDeviceWideLogsLocked() {
+ mSecurityLogMonitor.discardLogs();
+ if (mNetworkLogger != null) {
+ mNetworkLogger.discardLogs();
+ }
+ // TODO: We should discard pre-boot security logs here too, as otherwise those
+ // logs (which might contain data from the user just removed) will be
+ // available after next boot.
+ }
+
@Override
public boolean isNetworkLoggingEnabled(ComponentName admin) {
if (!mHasFeature) {
@@ -10084,32 +10148,27 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub {
* @see NetworkLoggingHandler#MAX_EVENTS_PER_BATCH
*/
@Override
- public synchronized List<NetworkEvent> retrieveNetworkLogs(ComponentName admin,
- long batchToken) {
+ public List<NetworkEvent> retrieveNetworkLogs(ComponentName admin, long batchToken) {
if (!mHasFeature) {
return null;
}
Preconditions.checkNotNull(admin);
- ensureDeviceOwnerManagingSingleUser(admin);
-
- if (mNetworkLogger == null) {
- return null;
- }
+ ensureDeviceOwnerAndAllUsersAffiliated(admin);
- if (!isNetworkLoggingEnabledInternalLocked()) {
- return null;
- }
-
- final long currentTime = System.currentTimeMillis();
synchronized (this) {
+ if (mNetworkLogger == null
+ || !isNetworkLoggingEnabledInternalLocked()) {
+ return null;
+ }
+
+ final long currentTime = System.currentTimeMillis();
DevicePolicyData policyData = getUserData(UserHandle.USER_SYSTEM);
if (currentTime > policyData.mLastNetworkLogsRetrievalTime) {
policyData.mLastNetworkLogsRetrievalTime = currentTime;
saveSettingsLocked(UserHandle.USER_SYSTEM);
}
+ return mNetworkLogger.retrieveLogs(batchToken);
}
-
- return mNetworkLogger.retrieveLogs(batchToken);
}
private void sendNetworkLoggingNotificationLocked() {
diff --git a/services/devicepolicy/java/com/android/server/devicepolicy/NetworkLogger.java b/services/devicepolicy/java/com/android/server/devicepolicy/NetworkLogger.java
index b82cb3cfbeaf..00859311af84 100644
--- a/services/devicepolicy/java/com/android/server/devicepolicy/NetworkLogger.java
+++ b/services/devicepolicy/java/com/android/server/devicepolicy/NetworkLogger.java
@@ -31,7 +31,6 @@ import android.util.Slog;
import com.android.server.ServiceThread;
-import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.atomic.AtomicBoolean;
@@ -130,6 +129,8 @@ final class NetworkLogger {
Log.d(TAG, "Stopping network logging");
// stop the logging regardless of whether we fail to unregister listener
mIsLoggingEnabled.set(false);
+ discardLogs();
+
try {
if (!checkIpConnectivityMetricsService()) {
// the IIpConnectivityMetrics service should have been present at this point
@@ -140,9 +141,43 @@ final class NetworkLogger {
return mIpConnectivityMetrics.unregisterNetdEventCallback();
} catch (RemoteException re) {
Slog.wtf(TAG, "Failed to make remote calls to unregister the callback", re);
- } finally {
- mHandlerThread.quitSafely();
return true;
+ } finally {
+ if (mHandlerThread != null) {
+ mHandlerThread.quitSafely();
+ }
+ }
+ }
+
+ /**
+ * If logs are being collected, keep collecting them but stop notifying the device owner that
+ * new logs are available (since they cannot be retrieved)
+ */
+ void pause() {
+ if (mNetworkLoggingHandler != null) {
+ mNetworkLoggingHandler.pause();
+ }
+ }
+
+ /**
+ * If logs are being collected, start notifying the device owner when logs are ready to be
+ * collected again (if it was paused).
+ * <p>If logging is enabled and there are logs ready to be retrieved, this method will attempt
+ * to notify the device owner. Therefore calling identity should be cleared before calling it
+ * (in case the method is called from a user other than the DO's user).
+ */
+ void resume() {
+ if (mNetworkLoggingHandler != null) {
+ mNetworkLoggingHandler.resume();
+ }
+ }
+
+ /**
+ * Discard all collected logs.
+ */
+ void discardLogs() {
+ if (mNetworkLoggingHandler != null) {
+ mNetworkLoggingHandler.discardLogs();
}
}
diff --git a/services/devicepolicy/java/com/android/server/devicepolicy/NetworkLoggingHandler.java b/services/devicepolicy/java/com/android/server/devicepolicy/NetworkLoggingHandler.java
index baa4c13f5190..7d6841248dcd 100644
--- a/services/devicepolicy/java/com/android/server/devicepolicy/NetworkLoggingHandler.java
+++ b/services/devicepolicy/java/com/android/server/devicepolicy/NetworkLoggingHandler.java
@@ -55,10 +55,16 @@ final class NetworkLoggingHandler extends Handler {
@GuardedBy("this")
private ArrayList<NetworkEvent> mFullBatch;
- // each full batch is represented by its token, which the DPC has to provide back to revieve it
+ @GuardedBy("this")
+ private boolean mPaused = false;
+
+ // each full batch is represented by its token, which the DPC has to provide back to retrieve it
@GuardedBy("this")
private long mCurrentFullBatchToken;
+ @GuardedBy("this")
+ private long mLastRetrievedFullBatchToken;
+
NetworkLoggingHandler(Looper looper, DevicePolicyManagerService dpm) {
super(looper);
mDpm = dpm;
@@ -70,15 +76,19 @@ final class NetworkLoggingHandler extends Handler {
case LOG_NETWORK_EVENT_MSG: {
NetworkEvent networkEvent = msg.getData().getParcelable(NETWORK_EVENT_KEY);
if (networkEvent != null) {
- mNetworkEvents.add(networkEvent);
- if (mNetworkEvents.size() >= MAX_EVENTS_PER_BATCH) {
- finalizeBatchAndNotifyDeviceOwnerIfNotEmpty();
+ synchronized (NetworkLoggingHandler.this) {
+ mNetworkEvents.add(networkEvent);
+ if (mNetworkEvents.size() >= MAX_EVENTS_PER_BATCH) {
+ finalizeBatchAndNotifyDeviceOwnerLocked();
+ }
}
}
break;
}
case FINALIZE_BATCH_MSG: {
- finalizeBatchAndNotifyDeviceOwnerIfNotEmpty();
+ synchronized (NetworkLoggingHandler.this) {
+ finalizeBatchAndNotifyDeviceOwnerLocked();
+ }
break;
}
}
@@ -91,22 +101,49 @@ final class NetworkLoggingHandler extends Handler {
+ "ms from now.");
}
- private synchronized void finalizeBatchAndNotifyDeviceOwnerIfNotEmpty() {
+ synchronized void pause() {
+ Log.d(TAG, "Paused network logging");
+ mPaused = true;
+ }
+
+ synchronized void resume() {
+ if (!mPaused) {
+ Log.d(TAG, "Attempted to resume network logging, but logging is not paused.");
+ return;
+ }
+
+ Log.d(TAG, "Resumed network logging. Current batch="
+ + mCurrentFullBatchToken + ", LastRetrievedBatch=" + mLastRetrievedFullBatchToken);
+ mPaused = false;
+
+ // If there is a full batch ready that the device owner hasn't been notified about, do it
+ // now.
+ if (mFullBatch != null && mFullBatch.size() > 0
+ && mLastRetrievedFullBatchToken != mCurrentFullBatchToken) {
+ scheduleBatchFinalization();
+ notifyDeviceOwnerLocked();
+ }
+ }
+
+ synchronized void discardLogs() {
+ mFullBatch = null;
+ mNetworkEvents = new ArrayList<NetworkEvent>();
+ Log.d(TAG, "Discarded all network logs");
+ }
+
+ @GuardedBy("this")
+ private void finalizeBatchAndNotifyDeviceOwnerLocked() {
if (mNetworkEvents.size() > 0) {
// finalize the batch and start a new one from scratch
mFullBatch = mNetworkEvents;
mCurrentFullBatchToken++;
mNetworkEvents = new ArrayList<NetworkEvent>();
- // notify DO that there's a new non-empty batch waiting
- Bundle extras = new Bundle();
- extras.putLong(DeviceAdminReceiver.EXTRA_NETWORK_LOGS_TOKEN, mCurrentFullBatchToken);
- extras.putInt(DeviceAdminReceiver.EXTRA_NETWORK_LOGS_COUNT, mFullBatch.size());
- Log.d(TAG, "Sending network logging batch broadcast to device owner, batchToken: "
- + mCurrentFullBatchToken);
- mDpm.sendDeviceOwnerCommand(DeviceAdminReceiver.ACTION_NETWORK_LOGS_AVAILABLE, extras);
+ if (!mPaused) {
+ notifyDeviceOwnerLocked();
+ }
} else {
// don't notify the DO, since there are no events; DPC can still retrieve
- // the last full batch
+ // the last full batch if not paused.
Log.d(TAG, "Was about to finalize the batch, but there were no events to send to"
+ " the DPC, the batchToken of last available batch: "
+ mCurrentFullBatchToken);
@@ -115,10 +152,21 @@ final class NetworkLoggingHandler extends Handler {
scheduleBatchFinalization();
}
+ @GuardedBy("this")
+ private void notifyDeviceOwnerLocked() {
+ Bundle extras = new Bundle();
+ extras.putLong(DeviceAdminReceiver.EXTRA_NETWORK_LOGS_TOKEN, mCurrentFullBatchToken);
+ extras.putInt(DeviceAdminReceiver.EXTRA_NETWORK_LOGS_COUNT, mFullBatch.size());
+ Log.d(TAG, "Sending network logging batch broadcast to device owner, batchToken: "
+ + mCurrentFullBatchToken);
+ mDpm.sendDeviceOwnerCommand(DeviceAdminReceiver.ACTION_NETWORK_LOGS_AVAILABLE, extras);
+ }
+
synchronized List<NetworkEvent> retrieveFullLogBatch(long batchToken) {
if (batchToken != mCurrentFullBatchToken) {
return null;
}
+ mLastRetrievedFullBatchToken = mCurrentFullBatchToken;
return mFullBatch;
}
}
diff --git a/services/devicepolicy/java/com/android/server/devicepolicy/SecurityLogMonitor.java b/services/devicepolicy/java/com/android/server/devicepolicy/SecurityLogMonitor.java
index 79702a8de505..18f06be063cd 100644
--- a/services/devicepolicy/java/com/android/server/devicepolicy/SecurityLogMonitor.java
+++ b/services/devicepolicy/java/com/android/server/devicepolicy/SecurityLogMonitor.java
@@ -19,6 +19,7 @@ package com.android.server.devicepolicy;
import android.app.admin.DeviceAdminReceiver;
import android.app.admin.SecurityLog;
import android.app.admin.SecurityLog.SecurityEvent;
+import android.os.SystemClock;
import android.util.Log;
import android.util.Slog;
@@ -50,7 +51,7 @@ class SecurityLogMonitor implements Runnable {
mService = service;
}
- private static final boolean DEBUG = false;
+ private static final boolean DEBUG = false; // STOPSHIP if true.
private static final String TAG = "SecurityLogMonitor";
/**
* Each log entry can hold up to 4K bytes (but as of {@link android.os.Build.VERSION_CODES#N}
@@ -78,17 +79,25 @@ class SecurityLogMonitor implements Runnable {
private ArrayList<SecurityEvent> mPendingLogs = new ArrayList<SecurityEvent>();
@GuardedBy("mLock")
private boolean mAllowedToRetrieve = false;
- // When DO will be allowed to retrieves the log, in milliseconds.
+
+ /**
+ * When DO will be allowed to retrieve the log, in milliseconds since boot (as per
+ * {@link SystemClock#elapsedRealtime()})
+ */
+ @GuardedBy("mLock")
+ private long mNextAllowedRetrievalTimeMillis = -1;
@GuardedBy("mLock")
- private long mNextAllowedRetrivalTimeMillis = -1;
+ private boolean mPaused = false;
void start() {
+ Slog.i(TAG, "Starting security logging.");
mLock.lock();
try {
if (mMonitorThread == null) {
mPendingLogs = new ArrayList<SecurityEvent>();
mAllowedToRetrieve = false;
- mNextAllowedRetrivalTimeMillis = -1;
+ mNextAllowedRetrievalTimeMillis = -1;
+ mPaused = false;
mMonitorThread = new Thread(this);
mMonitorThread.start();
@@ -99,6 +108,7 @@ class SecurityLogMonitor implements Runnable {
}
void stop() {
+ Slog.i(TAG, "Stopping security logging.");
mLock.lock();
try {
if (mMonitorThread != null) {
@@ -111,7 +121,8 @@ class SecurityLogMonitor implements Runnable {
// Reset state and clear buffer
mPendingLogs = new ArrayList<SecurityEvent>();
mAllowedToRetrieve = false;
- mNextAllowedRetrivalTimeMillis = -1;
+ mNextAllowedRetrievalTimeMillis = -1;
+ mPaused = false;
mMonitorThread = null;
}
} finally {
@@ -120,6 +131,58 @@ class SecurityLogMonitor implements Runnable {
}
/**
+ * If logs are being collected, keep collecting them but stop notifying the device owner that
+ * new logs are available (since they cannot be retrieved).
+ */
+ void pause() {
+ Slog.i(TAG, "Paused.");
+
+ mLock.lock();
+ mPaused = true;
+ mAllowedToRetrieve = false;
+ mLock.unlock();
+ }
+
+ /**
+ * If logs are being collected, start notifying the device owner when logs are ready to be
+ * retrieved again (if it was paused).
+ * <p>If logging is enabled and there are logs ready to be retrieved, this method will attempt
+ * to notify the device owner. Therefore calling identity should be cleared before calling it
+ * (in case the method is called from a user other than the DO's user).
+ */
+ void resume() {
+ mLock.lock();
+ try {
+ if (!mPaused) {
+ Log.d(TAG, "Attempted to resume, but logging is not paused.");
+ return;
+ }
+ mPaused = false;
+ mAllowedToRetrieve = false;
+ } finally {
+ mLock.unlock();
+ }
+
+ Slog.i(TAG, "Resumed.");
+ try {
+ notifyDeviceOwnerIfNeeded();
+ } catch (InterruptedException e) {
+ Log.w(TAG, "Thread interrupted.", e);
+ }
+ }
+
+ /**
+ * Discard all collected logs.
+ */
+ void discardLogs() {
+ mLock.lock();
+ mAllowedToRetrieve = false;
+ mPendingLogs = new ArrayList<SecurityEvent>();
+ mLock.unlock();
+ Slog.i(TAG, "Discarded all logs.");
+ }
+
+ /**
* Returns the new batch of logs since the last call to this method. Returns null if
* rate limit is exceeded.
*/
@@ -128,7 +191,7 @@ class SecurityLogMonitor implements Runnable {
try {
if (mAllowedToRetrieve) {
mAllowedToRetrieve = false;
- mNextAllowedRetrivalTimeMillis = System.currentTimeMillis()
+ mNextAllowedRetrievalTimeMillis = SystemClock.elapsedRealtime()
+ RATE_LIMIT_INTERVAL_MILLISECONDS;
List<SecurityEvent> result = mPendingLogs;
mPendingLogs = new ArrayList<SecurityEvent>();
@@ -163,7 +226,7 @@ class SecurityLogMonitor implements Runnable {
SecurityLog.readEventsSince(lastLogTimestampNanos + 1, logs);
}
if (!logs.isEmpty()) {
- if (DEBUG) Slog.d(TAG, "processing new logs");
+ if (DEBUG) Slog.d(TAG, "processing new logs. Events: " + logs.size());
mLock.lockInterruptibly();
try {
mPendingLogs.addAll(logs);
@@ -172,6 +235,7 @@ class SecurityLogMonitor implements Runnable {
mPendingLogs = new ArrayList<SecurityEvent>(mPendingLogs.subList(
mPendingLogs.size() - (BUFFER_ENTRIES_MAXIMUM_LEVEL / 2),
mPendingLogs.size()));
+ Slog.i(TAG, "Pending logs buffer full. Discarding old logs.");
}
} finally {
mLock.unlock();
@@ -188,7 +252,7 @@ class SecurityLogMonitor implements Runnable {
break;
}
}
- if (DEBUG) Slog.d(TAG, "MonitorThread exit.");
+ Slog.i(TAG, "MonitorThread exit.");
}
private void notifyDeviceOwnerIfNeeded() throws InterruptedException {
@@ -196,15 +260,24 @@ class SecurityLogMonitor implements Runnable {
boolean allowToRetrieveNow = false;
mLock.lockInterruptibly();
try {
+ if (mPaused) {
+ return;
+ }
+
+ // STOPSHIP(b/34186771): If the previous notification didn't reach the DO and logs were
+ // not retrieved (e.g. the broadcast was sent before the user was unlocked), no more
+ // subsequent callbacks will be sent. We should make sure that the DO gets notified
+ // before logs are lost.
int logSize = mPendingLogs.size();
if (logSize >= BUFFER_ENTRIES_NOTIFICATION_LEVEL) {
// Allow DO to retrieve logs if too many pending logs
allowToRetrieveNow = true;
+ if (DEBUG) Slog.d(TAG, "Number of log entries over threshold: " + logSize);
} else if (logSize > 0) {
- if (mNextAllowedRetrivalTimeMillis == -1 ||
- System.currentTimeMillis() >= mNextAllowedRetrivalTimeMillis) {
+ if (SystemClock.elapsedRealtime() >= mNextAllowedRetrievalTimeMillis) {
// Rate limit reset
allowToRetrieveNow = true;
+ if (DEBUG) Slog.d(TAG, "Timeout reached");
}
}
shouldNotifyDO = (!mAllowedToRetrieve) && allowToRetrieveNow;
@@ -213,7 +286,7 @@ class SecurityLogMonitor implements Runnable {
mLock.unlock();
}
if (shouldNotifyDO) {
- if (DEBUG) Slog.d(TAG, "notify DO");
+ Slog.i(TAG, "notify DO");
mService.sendDeviceOwnerCommand(DeviceAdminReceiver.ACTION_SECURITY_LOGS_AVAILABLE,
null);
}
diff --git a/services/java/com/android/server/SystemServer.java b/services/java/com/android/server/SystemServer.java
index 68cb0c5e3eaa..08fb5911374f 100644
--- a/services/java/com/android/server/SystemServer.java
+++ b/services/java/com/android/server/SystemServer.java
@@ -175,6 +175,8 @@ public final class SystemServer {
"com.google.android.clockwork.bluetooth.WearBluetoothService";
private static final String WEAR_WIFI_MEDIATOR_SERVICE_CLASS =
"com.google.android.clockwork.wifi.WearWifiMediatorService";
+ private static final String WEAR_CELLULAR_MEDIATOR_SERVICE_CLASS =
+ "com.google.android.clockwork.cellular.WearCellularMediatorService";
private static final String WEAR_TIME_SERVICE_CLASS =
"com.google.android.clockwork.time.WearTimeService";
private static final String ACCOUNT_SERVICE_CLASS =
@@ -1381,6 +1383,13 @@ public final class SystemServer {
traceBeginAndSlog("StartWearWifiMediator");
mSystemServiceManager.startService(WEAR_WIFI_MEDIATOR_SERVICE_CLASS);
traceEnd();
+
+ if (SystemProperties.getBoolean("config.enable_cellmediator", false)) {
+ traceBeginAndSlog("StartWearCellularMediator");
+ mSystemServiceManager.startService(WEAR_CELLULAR_MEDIATOR_SERVICE_CLASS);
+ traceEnd();
+ }
+
if (!disableNonCoreServices) {
traceBeginAndSlog("StartWearTimeService");
mSystemServiceManager.startService(WEAR_TIME_SERVICE_CLASS);
diff --git a/services/retaildemo/java/com/android/server/retaildemo/PreloadAppsInstaller.java b/services/retaildemo/java/com/android/server/retaildemo/PreloadAppsInstaller.java
index 2038c9e35f0c..3fa72dc92a88 100644
--- a/services/retaildemo/java/com/android/server/retaildemo/PreloadAppsInstaller.java
+++ b/services/retaildemo/java/com/android/server/retaildemo/PreloadAppsInstaller.java
@@ -103,7 +103,8 @@ class PreloadAppsInstaller {
Log.d(TAG, "installExistingPackage " + packageName + " u" + userId);
}
try {
- mPackageManager.installExistingPackageAsUser(packageName, userId);
+ mPackageManager.installExistingPackageAsUser(packageName, userId,
+ PackageManager.INSTALL_REASON_UNKNOWN);
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
} finally {
diff --git a/services/tests/servicestests/src/com/android/server/devicepolicy/DevicePolicyManagerTest.java b/services/tests/servicestests/src/com/android/server/devicepolicy/DevicePolicyManagerTest.java
index 182f0457de3d..60f436019fa8 100644
--- a/services/tests/servicestests/src/com/android/server/devicepolicy/DevicePolicyManagerTest.java
+++ b/services/tests/servicestests/src/com/android/server/devicepolicy/DevicePolicyManagerTest.java
@@ -71,6 +71,7 @@ import static org.mockito.Matchers.isNull;
import static org.mockito.Mockito.atLeast;
import static org.mockito.Mockito.doAnswer;
import static org.mockito.Mockito.doReturn;
+import static org.mockito.Mockito.never;
import static org.mockito.Mockito.reset;
import static org.mockito.Mockito.times;
import static org.mockito.Mockito.verify;
@@ -544,6 +545,83 @@ public class DevicePolicyManagerTest extends DpmTestBase {
}
/**
+ * Test for: @{link DevicePolicyManager#setActivePasswordState}
+ *
+ * Validates that when the password for a user changes, the notification broadcast intent
+ * {@link DeviceAdminReceiver#ACTION_PASSWORD_CHANGED} is sent to managed profile owners, in
+ * addition to ones in the original user.
+ */
+ public void testSetActivePasswordState_sendToProfiles() throws Exception {
+ mContext.callerPermissions.add(permission.BIND_DEVICE_ADMIN);
+
+ final int MANAGED_PROFILE_USER_ID = 78;
+ final int MANAGED_PROFILE_ADMIN_UID =
+ UserHandle.getUid(MANAGED_PROFILE_USER_ID, DpmMockContext.SYSTEM_UID);
+
+ // Setup device owner.
+ mContext.binder.callingUid = DpmMockContext.SYSTEM_UID;
+ mContext.packageName = admin1.getPackageName();
+ setupDeviceOwner();
+
+ // Add a managed profile belonging to the system user.
+ addManagedProfile(admin1, MANAGED_PROFILE_ADMIN_UID, admin1);
+
+ // Change the parent user's password.
+ dpm.reportPasswordChanged(UserHandle.USER_SYSTEM);
+
+ // Both the device owner and the managed profile owner should receive this broadcast.
+ final Intent intent = new Intent(DeviceAdminReceiver.ACTION_PASSWORD_CHANGED);
+ intent.setComponent(admin1);
+ intent.putExtra(Intent.EXTRA_USER, UserHandle.of(UserHandle.USER_SYSTEM));
+
+ verify(mContext.spiedContext, times(1)).sendBroadcastAsUser(
+ MockUtils.checkIntent(intent),
+ MockUtils.checkUserHandle(UserHandle.USER_SYSTEM));
+ verify(mContext.spiedContext, times(1)).sendBroadcastAsUser(
+ MockUtils.checkIntent(intent),
+ MockUtils.checkUserHandle(MANAGED_PROFILE_USER_ID));
+ }
+
+ /**
+ * Test for: @{link DevicePolicyManager#setActivePasswordState}
+ *
+ * Validates that when the password for a managed profile changes, the notification broadcast
+ * intent {@link DeviceAdminReceiver#ACTION_PASSWORD_CHANGED} is only sent to the profile, not
+ * its parent.
+ */
+ public void testSetActivePasswordState_notSentToParent() throws Exception {
+ mContext.callerPermissions.add(permission.BIND_DEVICE_ADMIN);
+
+ final int MANAGED_PROFILE_USER_ID = 78;
+ final int MANAGED_PROFILE_ADMIN_UID =
+ UserHandle.getUid(MANAGED_PROFILE_USER_ID, DpmMockContext.SYSTEM_UID);
+
+ // Setup device owner.
+ mContext.binder.callingUid = DpmMockContext.SYSTEM_UID;
+ mContext.packageName = admin1.getPackageName();
+ doReturn(true).when(mContext.lockPatternUtils)
+ .isSeparateProfileChallengeEnabled(MANAGED_PROFILE_USER_ID);
+ setupDeviceOwner();
+
+ // Add a managed profile belonging to the system user.
+ addManagedProfile(admin1, MANAGED_PROFILE_ADMIN_UID, admin1);
+
+ // Change the profile's password.
+ dpm.reportPasswordChanged(MANAGED_PROFILE_USER_ID);
+
+ // Both the device owner and the managed profile owner should receive this broadcast.
+ final Intent intent = new Intent(DeviceAdminReceiver.ACTION_PASSWORD_CHANGED);
+ intent.setComponent(admin1);
+ intent.putExtra(Intent.EXTRA_USER, UserHandle.of(MANAGED_PROFILE_USER_ID));
+
+ verify(mContext.spiedContext, never()).sendBroadcastAsUser(
+ MockUtils.checkIntent(intent),
+ MockUtils.checkUserHandle(UserHandle.USER_SYSTEM));
+ verify(mContext.spiedContext, times(1)).sendBroadcastAsUser(
+ MockUtils.checkIntent(intent),
+ MockUtils.checkUserHandle(MANAGED_PROFILE_USER_ID));
+ }
+ /**
* Test for: {@link DevicePolicyManager#setDeviceOwner} DO on system user installs successfully.
*/
public void testSetDeviceOwner() throws Exception {
@@ -2767,7 +2845,10 @@ public class DevicePolicyManagerTest extends DpmTestBase {
public void testGetLastSecurityLogRetrievalTime() throws Exception {
mContext.binder.callingUid = DpmMockContext.CALLER_SYSTEM_USER_UID;
setupDeviceOwner();
- when(mContext.userManager.getUserCount()).thenReturn(1);
+
+ // setUp() adds a secondary user for CALLER_USER_HANDLE. Remove it as otherwise the
+ // feature is disabled because there are non-affiliated secondary users.
+ mContext.removeUser(DpmMockContext.CALLER_USER_HANDLE);
when(mContext.resources.getBoolean(R.bool.config_supportPreRebootSecurityLogs))
.thenReturn(true);
@@ -2776,6 +2857,10 @@ public class DevicePolicyManagerTest extends DpmTestBase {
// Enabling logging should not change the timestamp.
dpm.setSecurityLoggingEnabled(admin1, true);
+ verify(mContext.settings)
+ .securityLogSetLoggingEnabledProperty(true);
+ when(mContext.settings.securityLogGetLoggingEnabledProperty())
+ .thenReturn(true);
assertEquals(-1, dpm.getLastSecurityLogRetrievalTime());
// Retrieving the logs should update the timestamp.
@@ -2828,7 +2913,7 @@ public class DevicePolicyManagerTest extends DpmTestBase {
public void testGetLastBugReportRequestTime() throws Exception {
mContext.binder.callingUid = DpmMockContext.CALLER_SYSTEM_USER_UID;
setupDeviceOwner();
- when(mContext.userManager.getUserCount()).thenReturn(1);
+
mContext.packageName = admin1.getPackageName();
mContext.applicationInfo = new ApplicationInfo();
when(mContext.resources.getColor(eq(R.color.notification_action_list), anyObject()))
@@ -2836,6 +2921,10 @@ public class DevicePolicyManagerTest extends DpmTestBase {
when(mContext.resources.getColor(eq(R.color.notification_material_background_color),
anyObject())).thenReturn(Color.WHITE);
+ // setUp() adds a secondary user for CALLER_USER_HANDLE. Remove it as otherwise the
+ // feature is disabled because there are non-affiliated secondary users.
+ mContext.removeUser(DpmMockContext.CALLER_USER_HANDLE);
+
// No bug reports were requested so far.
assertEquals(-1, dpm.getLastBugReportRequestTime());
@@ -2873,7 +2962,16 @@ public class DevicePolicyManagerTest extends DpmTestBase {
public void testGetLastNetworkLogRetrievalTime() throws Exception {
mContext.binder.callingUid = DpmMockContext.CALLER_SYSTEM_USER_UID;
setupDeviceOwner();
- when(mContext.userManager.getUserCount()).thenReturn(1);
+ mContext.packageName = admin1.getPackageName();
+ mContext.applicationInfo = new ApplicationInfo();
+ when(mContext.resources.getColor(eq(R.color.notification_action_list), anyObject()))
+ .thenReturn(Color.WHITE);
+ when(mContext.resources.getColor(eq(R.color.notification_material_background_color),
+ anyObject())).thenReturn(Color.WHITE);
+
+ // setUp() adds a secondary user for CALLER_USER_HANDLE. Remove it as otherwise the
+ // feature is disabled because there are non-affiliated secondary users.
+ mContext.removeUser(DpmMockContext.CALLER_USER_HANDLE);
when(mContext.iipConnectivityMetrics.registerNetdEventCallback(anyObject()))
.thenReturn(true);
diff --git a/services/tests/servicestests/src/com/android/server/devicepolicy/DpmMockContext.java b/services/tests/servicestests/src/com/android/server/devicepolicy/DpmMockContext.java
index 65255d9ef7ad..44bf547460dd 100644
--- a/services/tests/servicestests/src/com/android/server/devicepolicy/DpmMockContext.java
+++ b/services/tests/servicestests/src/com/android/server/devicepolicy/DpmMockContext.java
@@ -18,6 +18,7 @@ package com.android.server.devicepolicy;
import android.accounts.Account;
import android.accounts.AccountManager;
+import android.app.AlarmManager;
import android.app.IActivityManager;
import android.app.NotificationManager;
import android.app.backup.IBackupManager;
@@ -276,6 +277,7 @@ public class DpmMockContext extends MockContext {
public final MockContentResolver contentResolver;
public final TelephonyManager telephonyManager;
public final AccountManager accountManager;
+ public final AlarmManager alarmManager;
/** Note this is a partial mock, not a real mock. */
public final PackageManager packageManager;
@@ -319,6 +321,7 @@ public class DpmMockContext extends MockContext {
settings = mock(SettingsForMock.class);
telephonyManager = mock(TelephonyManager.class);
accountManager = mock(AccountManager.class);
+ alarmManager = mock(AlarmManager.class);
// Package manager is huge, so we use a partial mock instead.
packageManager = spy(context.getPackageManager());
@@ -327,7 +330,8 @@ public class DpmMockContext extends MockContext {
contentResolver = new MockContentResolver();
- // Add the system user
+ // Add the system user with a fake profile group already set up (this can happen in the real
+ // world if a managed profile is added and then removed).
systemUserDataDir =
addUser(UserHandle.USER_SYSTEM, UserInfo.FLAG_PRIMARY, UserHandle.USER_SYSTEM);
@@ -389,6 +393,18 @@ public class DpmMockContext extends MockContext {
return dir;
}
+ public void removeUser(int userId) {
+ for (int i = 0; i < mUserInfos.size(); i++) {
+ if (mUserInfos.get(i).id == userId) {
+ mUserInfos.remove(i);
+ break;
+ }
+ }
+ when(userManager.getUserInfo(eq(userId))).thenReturn(null);
+
+ when(userManager.isUserRunning(eq(new UserHandle(userId)))).thenReturn(false);
+ }
+
private UserInfo getUserInfo(int userId) {
for (UserInfo ui : mUserInfos) {
if (ui.id == userId) {
@@ -410,9 +426,9 @@ public class DpmMockContext extends MockContext {
if (parent == null) {
return ret;
}
- ret.add(parent);
for (UserInfo ui : mUserInfos) {
- if (ui.profileGroupId != UserInfo.NO_PROFILE_GROUP_ID
+ if (ui == parent
+ || ui.profileGroupId != UserInfo.NO_PROFILE_GROUP_ID
&& ui.profileGroupId == parent.profileGroupId) {
ret.add(ui);
}
@@ -463,6 +479,8 @@ public class DpmMockContext extends MockContext {
@Override
public Object getSystemService(String name) {
switch (name) {
+ case Context.ALARM_SERVICE:
+ return alarmManager;
case Context.USER_SERVICE:
return userManager;
case Context.POWER_SERVICE:
diff --git a/services/tests/servicestests/src/com/android/server/retaildemo/PreloadAppsInstallerTest.java b/services/tests/servicestests/src/com/android/server/retaildemo/PreloadAppsInstallerTest.java
index 3eeeaf679891..346dc4235e6c 100644
--- a/services/tests/servicestests/src/com/android/server/retaildemo/PreloadAppsInstallerTest.java
+++ b/services/tests/servicestests/src/com/android/server/retaildemo/PreloadAppsInstallerTest.java
@@ -103,7 +103,8 @@ public class PreloadAppsInstallerTest {
observer.getValue().onPackageInstalled(path, PackageManager.INSTALL_SUCCEEDED,
null, null);
// Verify that we try to install the package in system user.
- verify(mIpm).installExistingPackageAsUser(path, UserHandle.USER_SYSTEM);
+ verify(mIpm).installExistingPackageAsUser(path, UserHandle.USER_SYSTEM,
+ PackageManager.INSTALL_REASON_UNKNOWN);
}
assertEquals("DEMO_USER_SETUP should be set to 1 after preloaded apps are installed",
"1",
@@ -139,7 +140,8 @@ public class PreloadAppsInstallerTest {
observer.getValue().onPackageInstalled(path, PackageManager.INSTALL_SUCCEEDED,
null, null);
// Verify that we try to install the package in system user.
- verify(mIpm).installExistingPackageAsUser(path, UserHandle.USER_SYSTEM);
+ verify(mIpm).installExistingPackageAsUser(path, UserHandle.USER_SYSTEM,
+ PackageManager.INSTALL_REASON_UNKNOWN);
}
assertEquals("DEMO_USER_SETUP should be set to 1 after preloaded apps are installed",
"1",
diff --git a/services/usage/java/com/android/server/usage/UserUsageStatsService.java b/services/usage/java/com/android/server/usage/UserUsageStatsService.java
index ba770ef5053c..d9f352cbbf7b 100644
--- a/services/usage/java/com/android/server/usage/UserUsageStatsService.java
+++ b/services/usage/java/com/android/server/usage/UserUsageStatsService.java
@@ -188,7 +188,6 @@ class UserUsageStatsService {
String[] annotations = event.mContentAnnotations;
if (annotations != null) {
for (String annotation : annotations) {
- // TODO(kanlig): update with confidences of annotations.
stats.updateChooserCounts(event.mPackage, annotation, event.mAction);
}
}
diff --git a/test-runner/src/android/test/mock/MockPackageManager.java b/test-runner/src/android/test/mock/MockPackageManager.java
index 715896971137..c46d4a5aff99 100644
--- a/test-runner/src/android/test/mock/MockPackageManager.java
+++ b/test-runner/src/android/test/mock/MockPackageManager.java
@@ -1064,4 +1064,11 @@ public class MockPackageManager extends PackageManager {
public Drawable loadUnbadgedItemIcon(PackageItemInfo itemInfo, ApplicationInfo appInfo) {
throw new UnsupportedOperationException();
}
+
+ /**
+ * @hide
+ */
+ public int getInstallReason(String packageName, UserHandle user) {
+ throw new UnsupportedOperationException();
+ }
}
diff --git a/tests/net/java/com/android/server/connectivity/IpConnectivityEventBuilderTest.java b/tests/net/java/com/android/server/connectivity/IpConnectivityEventBuilderTest.java
index f9cac43fff55..415911ed4ce6 100644
--- a/tests/net/java/com/android/server/connectivity/IpConnectivityEventBuilderTest.java
+++ b/tests/net/java/com/android/server/connectivity/IpConnectivityEventBuilderTest.java
@@ -58,8 +58,8 @@ public class IpConnectivityEventBuilderTest extends TestCase {
String want = joinLines(
"dropped_events: 0",
"events <",
+ " link_layer: 0",
" time_ms: 1",
- " transport: 0",
" default_network_event <",
" network_id <",
" network_id: 102",
@@ -89,8 +89,8 @@ public class IpConnectivityEventBuilderTest extends TestCase {
String want = joinLines(
"dropped_events: 0",
"events <",
+ " link_layer: 0",
" time_ms: 1",
- " transport: 0",
" dhcp_event <",
" duration_ms: 192",
" if_name: \"wlan0\"",
@@ -112,8 +112,8 @@ public class IpConnectivityEventBuilderTest extends TestCase {
String want = joinLines(
"dropped_events: 0",
"events <",
+ " link_layer: 0",
" time_ms: 1",
- " transport: 0",
" dhcp_event <",
" duration_ms: 0",
" if_name: \"wlan0\"",
@@ -137,8 +137,8 @@ public class IpConnectivityEventBuilderTest extends TestCase {
String want = joinLines(
"dropped_events: 0",
"events <",
+ " link_layer: 0",
" time_ms: 1",
- " transport: 0",
" dns_lookup_batch <",
" event_types: 1",
" event_types: 1",
@@ -185,8 +185,8 @@ public class IpConnectivityEventBuilderTest extends TestCase {
String want = joinLines(
"dropped_events: 0",
"events <",
+ " link_layer: 0",
" time_ms: 1",
- " transport: 0",
" ip_provisioning_event <",
" event_type: 1",
" if_name: \"wlan0\"",
@@ -208,8 +208,8 @@ public class IpConnectivityEventBuilderTest extends TestCase {
String want = joinLines(
"dropped_events: 0",
"events <",
+ " link_layer: 0",
" time_ms: 1",
- " transport: 0",
" ip_reachability_event <",
" event_type: 512",
" if_name: \"wlan0\"",
@@ -231,8 +231,8 @@ public class IpConnectivityEventBuilderTest extends TestCase {
String want = joinLines(
"dropped_events: 0",
"events <",
+ " link_layer: 0",
" time_ms: 1",
- " transport: 0",
" network_event <",
" event_type: 5",
" latency_ms: 20410",
@@ -258,8 +258,8 @@ public class IpConnectivityEventBuilderTest extends TestCase {
String want = joinLines(
"dropped_events: 0",
"events <",
+ " link_layer: 0",
" time_ms: 1",
- " transport: 0",
" validation_probe_event <",
" latency_ms: 40730",
" network_id <",
@@ -287,8 +287,8 @@ public class IpConnectivityEventBuilderTest extends TestCase {
String want = joinLines(
"dropped_events: 0",
"events <",
+ " link_layer: 0",
" time_ms: 1",
- " transport: 0",
" apf_program_event <",
" current_ras: 9",
" drop_multicast: true",
@@ -319,8 +319,8 @@ public class IpConnectivityEventBuilderTest extends TestCase {
String want = joinLines(
"dropped_events: 0",
"events <",
+ " link_layer: 0",
" time_ms: 1",
- " transport: 0",
" apf_statistics <",
" dropped_ras: 2",
" duration_ms: 45000",
@@ -351,8 +351,8 @@ public class IpConnectivityEventBuilderTest extends TestCase {
String want = joinLines(
"dropped_events: 0",
"events <",
+ " link_layer: 0",
" time_ms: 1",
- " transport: 0",
" ra_event <",
" dnssl_lifetime: -1",
" prefix_preferred_lifetime: 300",
diff --git a/tests/net/java/com/android/server/connectivity/IpConnectivityMetricsTest.java b/tests/net/java/com/android/server/connectivity/IpConnectivityMetricsTest.java
index e9257fa619ba..f56f3f80ef69 100644
--- a/tests/net/java/com/android/server/connectivity/IpConnectivityMetricsTest.java
+++ b/tests/net/java/com/android/server/connectivity/IpConnectivityMetricsTest.java
@@ -158,16 +158,16 @@ public class IpConnectivityMetricsTest extends TestCase {
String want = joinLines(
"dropped_events: 0",
"events <",
+ " link_layer: 0",
" time_ms: 100",
- " transport: 0",
" ip_reachability_event <",
" event_type: 512",
" if_name: \"wlan0\"",
" >",
">",
"events <",
+ " link_layer: 0",
" time_ms: 200",
- " transport: 0",
" dhcp_event <",
" duration_ms: 192",
" if_name: \"wlan0\"",
@@ -175,8 +175,8 @@ public class IpConnectivityMetricsTest extends TestCase {
" >",
">",
"events <",
+ " link_layer: 0",
" time_ms: 300",
- " transport: 0",
" default_network_event <",
" network_id <",
" network_id: 102",
@@ -191,8 +191,8 @@ public class IpConnectivityMetricsTest extends TestCase {
" >",
">",
"events <",
+ " link_layer: 0",
" time_ms: 400",
- " transport: 0",
" ip_provisioning_event <",
" event_type: 1",
" if_name: \"wlan0\"",
@@ -200,8 +200,8 @@ public class IpConnectivityMetricsTest extends TestCase {
" >",
">",
"events <",
+ " link_layer: 0",
" time_ms: 500",
- " transport: 0",
" validation_probe_event <",
" latency_ms: 40730",
" network_id <",
@@ -212,8 +212,8 @@ public class IpConnectivityMetricsTest extends TestCase {
" >",
">",
"events <",
+ " link_layer: 0",
" time_ms: 600",
- " transport: 0",
" apf_statistics <",
" dropped_ras: 2",
" duration_ms: 45000",
@@ -226,8 +226,8 @@ public class IpConnectivityMetricsTest extends TestCase {
" >",
">",
"events <",
+ " link_layer: 0",
" time_ms: 700",
- " transport: 0",
" ra_event <",
" dnssl_lifetime: -1",
" prefix_preferred_lifetime: 300",
diff --git a/tests/net/java/com/android/server/connectivity/NetdEventListenerServiceTest.java b/tests/net/java/com/android/server/connectivity/NetdEventListenerServiceTest.java
index 75d2f1a9edfd..cfd559802e3e 100644
--- a/tests/net/java/com/android/server/connectivity/NetdEventListenerServiceTest.java
+++ b/tests/net/java/com/android/server/connectivity/NetdEventListenerServiceTest.java
@@ -213,8 +213,8 @@ public class NetdEventListenerServiceTest extends TestCase {
IpConnectivityEvent got = events.get(0);
String want = String.join("\n",
+ "link_layer: 0",
"time_ms: 0",
- "transport: 0",
"connect_statistics <",
" connect_count: 12",
" errnos_counters <",
diff --git a/tests/net/java/com/android/server/connectivity/NetworkNotificationManagerTest.java b/tests/net/java/com/android/server/connectivity/NetworkNotificationManagerTest.java
index 98073ce1e59b..21c2de79d68b 100644
--- a/tests/net/java/com/android/server/connectivity/NetworkNotificationManagerTest.java
+++ b/tests/net/java/com/android/server/connectivity/NetworkNotificationManagerTest.java
@@ -80,6 +80,7 @@ public class NetworkNotificationManagerTest extends TestCase {
when(mCtx.getResources()).thenReturn(mResources);
when(mCtx.getPackageManager()).thenReturn(mPm);
when(mCtx.getApplicationInfo()).thenReturn(new ApplicationInfo());
+ when(mNetworkInfo.getExtraInfo()).thenReturn("extra");
when(mResources.getColor(anyInt(), any())).thenReturn(0xFF607D8B);
mManager = new NetworkNotificationManager(mCtx, mTelephonyManager, mNotificationManager);
diff --git a/tools/layoutlib/bridge/src/android/animation/AnimationThread.java b/tools/layoutlib/bridge/src/android/animation/AnimationThread.java
index b10ec9fec2ad..ce2aec790119 100644
--- a/tools/layoutlib/bridge/src/android/animation/AnimationThread.java
+++ b/tools/layoutlib/bridge/src/android/animation/AnimationThread.java
@@ -25,7 +25,6 @@ import com.android.layoutlib.bridge.impl.RenderSessionImpl;
import android.os.Handler;
import android.os.Handler_Delegate;
-import android.os.Handler_Delegate.IHandlerCallback;
import android.os.Message;
import java.util.PriorityQueue;
diff --git a/tools/layoutlib/bridge/src/android/content/res/Resources_Delegate.java b/tools/layoutlib/bridge/src/android/content/res/Resources_Delegate.java
index c3d4cef61b35..e0f8e1c33bc6 100644
--- a/tools/layoutlib/bridge/src/android/content/res/Resources_Delegate.java
+++ b/tools/layoutlib/bridge/src/android/content/res/Resources_Delegate.java
@@ -21,6 +21,7 @@ import com.android.ide.common.rendering.api.ArrayResourceValue;
import com.android.ide.common.rendering.api.DensityBasedResourceValue;
import com.android.ide.common.rendering.api.LayoutLog;
import com.android.ide.common.rendering.api.LayoutlibCallback;
+import com.android.ide.common.rendering.api.PluralsResourceValue;
import com.android.ide.common.rendering.api.RenderResources;
import com.android.ide.common.rendering.api.ResourceValue;
import com.android.layoutlib.bridge.Bridge;
@@ -43,6 +44,7 @@ import android.annotation.Nullable;
import android.content.res.Resources.NotFoundException;
import android.content.res.Resources.Theme;
import android.graphics.drawable.Drawable;
+import android.icu.text.PluralRules;
import android.util.AttributeSet;
import android.util.DisplayMetrics;
import android.util.LruCache;
@@ -405,9 +407,6 @@ public class Resources_Delegate {
rv = resources.mContext.getRenderResources().resolveResValue(rv);
if (rv != null) {
return rv.getValue();
- } else {
- Bridge.getLog().error(LayoutLog.TAG_RESOURCES_RESOLVE,
- "Unable to resolve resource " + ref, null);
}
}
// Not a reference.
@@ -738,6 +737,48 @@ public class Resources_Delegate {
}
@LayoutlibDelegate
+ static String getQuantityString(Resources resources, int id, int quantity) throws
+ NotFoundException {
+ Pair<String, ResourceValue> value = getResourceValue(resources, id, mPlatformResourceFlag);
+
+ if (value != null) {
+ if (value.getSecond() instanceof PluralsResourceValue) {
+ PluralsResourceValue pluralsResourceValue = (PluralsResourceValue) value.getSecond();
+ PluralRules pluralRules = PluralRules.forLocale(resources.getConfiguration().getLocales()
+ .get(0));
+ String strValue = pluralsResourceValue.getValue(pluralRules.select(quantity));
+ if (strValue == null) {
+ strValue = pluralsResourceValue.getValue(PluralRules.KEYWORD_OTHER);
+ }
+
+ return strValue;
+ }
+ else {
+ return value.getSecond().getValue();
+ }
+ }
+
+ // id was not found or not resolved. Throw a NotFoundException.
+ throwException(resources, id);
+
+ // this is not used since the method above always throws
+ return null;
+ }
+
+ @LayoutlibDelegate
+ static String getQuantityString(Resources resources, int id, int quantity, Object... formatArgs)
+ throws NotFoundException {
+ String raw = getQuantityString(resources, id, quantity);
+ return String.format(resources.getConfiguration().getLocales().get(0), raw, formatArgs);
+ }
+
+ @LayoutlibDelegate
+ static CharSequence getQuantityText(Resources resources, int id, int quantity) throws
+ NotFoundException {
+ return getQuantityString(resources, id, quantity);
+ }
+
+ @LayoutlibDelegate
static void getValue(Resources resources, int id, TypedValue outValue, boolean resolveRefs)
throws NotFoundException {
Pair<String, ResourceValue> value = getResourceValue(resources, id, mPlatformResourceFlag);
diff --git a/tools/layoutlib/bridge/src/android/graphics/BidiRenderer.java b/tools/layoutlib/bridge/src/android/graphics/BidiRenderer.java
index 21f36ceb0c1b..c6827a3c9c5f 100644
--- a/tools/layoutlib/bridge/src/android/graphics/BidiRenderer.java
+++ b/tools/layoutlib/bridge/src/android/graphics/BidiRenderer.java
@@ -30,6 +30,7 @@ import java.awt.Graphics2D;
import java.awt.Toolkit;
import java.awt.font.FontRenderContext;
import java.awt.font.GlyphVector;
+import java.awt.geom.AffineTransform;
import java.awt.geom.Rectangle2D;
import java.util.ArrayList;
import java.util.LinkedList;
@@ -41,6 +42,7 @@ import java.util.List;
*/
@SuppressWarnings("deprecation")
public class BidiRenderer {
+ private static String JAVA_VENDOR = System.getProperty("java.vendor");
private static class ScriptRun {
int start;
@@ -221,9 +223,16 @@ public class BidiRenderer {
frc = mGraphics.getFontRenderContext();
} else {
frc = Toolkit.getDefaultToolkit().getFontMetrics(font).getFontRenderContext();
+
// Metrics obtained this way don't have anti-aliasing set. So,
// we create a new FontRenderContext with anti-aliasing set.
- frc = new FontRenderContext(font.getTransform(), mPaint.isAntiAliased(), frc.usesFractionalMetrics());
+ AffineTransform transform = font.getTransform();
+ if (mPaint.isAntiAliased() &&
+ // Workaround for http://b.android.com/211659
+ (transform.getScaleX() <= 9.9 ||
+ !"JetBrains s.r.o".equals(JAVA_VENDOR))) {
+ frc = new FontRenderContext(transform, true, frc.usesFractionalMetrics());
+ }
}
GlyphVector gv = font.layoutGlyphVector(frc, mText, start, limit, flag);
int ng = gv.getNumGlyphs();
diff --git a/tools/layoutlib/bridge/src/android/graphics/Canvas_Delegate.java b/tools/layoutlib/bridge/src/android/graphics/Canvas_Delegate.java
index 43a0ff5a23dc..c599e9d6d6bc 100644
--- a/tools/layoutlib/bridge/src/android/graphics/Canvas_Delegate.java
+++ b/tools/layoutlib/bridge/src/android/graphics/Canvas_Delegate.java
@@ -20,24 +20,14 @@ import com.android.ide.common.rendering.api.LayoutLog;
import com.android.layoutlib.bridge.Bridge;
import com.android.layoutlib.bridge.impl.DelegateManager;
import com.android.layoutlib.bridge.impl.GcSnapshot;
-import com.android.layoutlib.bridge.impl.PorterDuffUtility;
-import com.android.ninepatch.NinePatchChunk;
import com.android.tools.layoutlib.annotations.LayoutlibDelegate;
import android.annotation.Nullable;
import android.graphics.Bitmap.Config;
-import android.text.TextUtils;
-import java.awt.Color;
-import java.awt.Composite;
import java.awt.Graphics2D;
import java.awt.Rectangle;
-import java.awt.RenderingHints;
-import java.awt.Shape;
import java.awt.geom.AffineTransform;
-import java.awt.geom.Arc2D;
-import java.awt.geom.Rectangle2D;
-import java.awt.image.BufferedImage;
import libcore.util.NativeAllocationRegistry_Delegate;
@@ -401,23 +391,6 @@ public final class Canvas_Delegate extends BaseCanvas_Delegate {
}
@LayoutlibDelegate
- public static boolean nClipRegion(long nativeCanvas,
- long nativeRegion,
- int regionOp) {
- Canvas_Delegate canvasDelegate = Canvas_Delegate.getDelegate(nativeCanvas);
- if (canvasDelegate == null) {
- return true;
- }
-
- Region_Delegate region = Region_Delegate.getDelegate(nativeRegion);
- if (region == null) {
- return true;
- }
-
- return canvasDelegate.mSnapshot.clip(region.getJavaArea(), regionOp);
- }
-
- @LayoutlibDelegate
public static void nSetDrawFilter(long nativeCanvas, long nativeFilter) {
Canvas_Delegate canvasDelegate = Canvas_Delegate.getDelegate(nativeCanvas);
if (canvasDelegate == null) {
diff --git a/tools/layoutlib/bridge/src/android/graphics/FontFamily_Delegate.java b/tools/layoutlib/bridge/src/android/graphics/FontFamily_Delegate.java
index 50efc7f7db86..d32693575ef0 100644
--- a/tools/layoutlib/bridge/src/android/graphics/FontFamily_Delegate.java
+++ b/tools/layoutlib/bridge/src/android/graphics/FontFamily_Delegate.java
@@ -248,14 +248,17 @@ public class FontFamily_Delegate {
// ---- delegate methods ----
@LayoutlibDelegate
/*package*/ static boolean addFont(FontFamily thisFontFamily, String path, int ttcIndex) {
- final FontFamily_Delegate delegate = getDelegate(thisFontFamily.mNativePtr);
+ if (thisFontFamily.mBuilderPtr == 0) {
+ throw new IllegalStateException("Unable to call addFont after freezing.");
+ }
+ final FontFamily_Delegate delegate = getDelegate(thisFontFamily.mBuilderPtr);
return delegate != null && delegate.addFont(path, ttcIndex);
}
// ---- native methods ----
@LayoutlibDelegate
- /*package*/ static long nCreateFamily(String lang, int variant) {
+ /*package*/ static long nInitBuilder(String lang, int variant) {
// TODO: support lang. This is required for japanese locale.
FontFamily_Delegate delegate = new FontFamily_Delegate();
// variant can be 0, 1 or 2.
@@ -270,6 +273,11 @@ public class FontFamily_Delegate {
}
@LayoutlibDelegate
+ /*package*/ static long nCreateFamily(long builderPtr) {
+ return builderPtr;
+ }
+
+ @LayoutlibDelegate
/*package*/ static void nUnrefFamily(long nativePtr) {
// Removing the java reference for the object doesn't mean that it's freed for garbage
// collection. Typeface_Delegate may still hold a reference for it.
@@ -277,22 +285,22 @@ public class FontFamily_Delegate {
}
@LayoutlibDelegate
- /*package*/ static boolean nAddFont(long nativeFamily, ByteBuffer font, int ttcIndex) {
+ /*package*/ static boolean nAddFont(long builderPtr, ByteBuffer font, int ttcIndex) {
assert false : "The only client of this method has been overriden.";
return false;
}
@LayoutlibDelegate
- /*package*/ static boolean nAddFontWeightStyle(long nativeFamily, ByteBuffer font,
+ /*package*/ static boolean nAddFontWeightStyle(long builderPtr, ByteBuffer font,
int ttcIndex, List<FontListParser.Axis> listOfAxis,
int weight, boolean isItalic) {
assert false : "The only client of this method has been overriden.";
return false;
}
- static boolean addFont(long nativeFamily, final String path, final int weight,
+ static boolean addFont(long builderPtr, final String path, final int weight,
final boolean isItalic) {
- final FontFamily_Delegate delegate = getDelegate(nativeFamily);
+ final FontFamily_Delegate delegate = getDelegate(builderPtr);
if (delegate != null) {
if (sFontLocation == null) {
delegate.mPostInitRunnables.add(() -> delegate.addFont(path, weight, isItalic));
@@ -304,8 +312,8 @@ public class FontFamily_Delegate {
}
@LayoutlibDelegate
- /*package*/ static boolean nAddFontFromAsset(long nativeFamily, AssetManager mgr, String path) {
- FontFamily_Delegate ffd = sManager.getDelegate(nativeFamily);
+ /*package*/ static boolean nAddFontFromAsset(long builderPtr, AssetManager mgr, String path) {
+ FontFamily_Delegate ffd = sManager.getDelegate(builderPtr);
if (ffd == null) {
return false;
}
diff --git a/tools/layoutlib/bridge/src/android/graphics/Typeface_Delegate.java b/tools/layoutlib/bridge/src/android/graphics/Typeface_Delegate.java
index 5cd34f6e000f..a554b6da31eb 100644
--- a/tools/layoutlib/bridge/src/android/graphics/Typeface_Delegate.java
+++ b/tools/layoutlib/bridge/src/android/graphics/Typeface_Delegate.java
@@ -212,9 +212,10 @@ public final class Typeface_Delegate {
Map<String, ByteBuffer> bufferForPath) {
FontFamily fontFamily = new FontFamily(family.lang, family.variant);
for (FontListParser.Font font : family.fonts) {
- FontFamily_Delegate.addFont(fontFamily.mNativePtr, font.fontName, font.weight,
+ FontFamily_Delegate.addFont(fontFamily.mBuilderPtr, font.fontName, font.weight,
font.isItalic);
}
+ fontFamily.freeze();
return fontFamily;
}
diff --git a/tools/layoutlib/bridge/src/android/view/PointerIcon_Delegate.java b/tools/layoutlib/bridge/src/android/view/PointerIcon_Delegate.java
new file mode 100644
index 000000000000..4a5ea9b5bb65
--- /dev/null
+++ b/tools/layoutlib/bridge/src/android/view/PointerIcon_Delegate.java
@@ -0,0 +1,33 @@
+/*
+ * 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 android.view;
+
+import com.android.tools.layoutlib.annotations.LayoutlibDelegate;
+
+import android.content.Context;
+import android.content.res.Resources;
+
+public class PointerIcon_Delegate {
+
+ @LayoutlibDelegate
+ /*package*/ static void loadResource(PointerIcon icon, Context context, Resources resources,
+ int resourceId) {
+ // HACK: This bypasses the problem of having an enum resolved as a resourceId.
+ // PointerIcon would not be displayed by layoutlib anyway, so we always return the null
+ // icon.
+ }
+}
diff --git a/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/BridgeRenderSession.java b/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/BridgeRenderSession.java
index 3ac1889dbe72..77b131fa9d86 100644
--- a/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/BridgeRenderSession.java
+++ b/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/BridgeRenderSession.java
@@ -76,6 +76,23 @@ public class BridgeRenderSession extends RenderSession {
}
@Override
+ public Result measure(long timeout) {
+ try {
+ Bridge.prepareThread();
+ mLastResult = mSession.acquire(timeout);
+ if (mLastResult.isSuccess()) {
+ mSession.invalidateRenderingSize();
+ mLastResult = mSession.measure();
+ }
+ } finally {
+ mSession.release();
+ Bridge.cleanupThread();
+ }
+
+ return mLastResult;
+ }
+
+ @Override
public Result render(long timeout, boolean forceMeasure) {
try {
Bridge.prepareThread();
diff --git a/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/android/BridgeContext.java b/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/android/BridgeContext.java
index 1b3b563fb51e..663e56d2ccdf 100644
--- a/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/android/BridgeContext.java
+++ b/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/android/BridgeContext.java
@@ -66,7 +66,6 @@ import android.graphics.Bitmap;
import android.graphics.drawable.Drawable;
import android.hardware.display.DisplayManager;
import android.net.Uri;
-import android.os.Build.VERSION_CODES;
import android.os.Bundle;
import android.os.Handler;
import android.os.IBinder;
@@ -104,6 +103,7 @@ import java.util.IdentityHashMap;
import java.util.List;
import java.util.Map;
+import static android.os.Build.VERSION_CODES.JELLY_BEAN_MR1;
import static com.android.layoutlib.bridge.android.RenderParamsFlags.FLAG_KEY_APPLICATION_PACKAGE;
/**
@@ -113,6 +113,28 @@ import static com.android.layoutlib.bridge.android.RenderParamsFlags.FLAG_KEY_AP
public final class BridgeContext extends Context {
private static final String PREFIX_THEME_APPCOMPAT = "Theme.AppCompat";
+ private static final Map<String, ResourceValue> FRAMEWORK_PATCHED_VALUES = new HashMap<>(2);
+ private static final Map<String, ResourceValue> FRAMEWORK_REPLACE_VALUES = new HashMap<>(3);
+
+ static {
+ FRAMEWORK_PATCHED_VALUES.put("animateFirstView", new ResourceValue(
+ ResourceType.BOOL, "animateFirstView", "false", false));
+ FRAMEWORK_PATCHED_VALUES.put("animateLayoutChanges",
+ new ResourceValue(ResourceType.BOOL, "animateLayoutChanges", "false", false));
+
+
+ FRAMEWORK_REPLACE_VALUES.put("textEditSuggestionItemLayout",
+ new ResourceValue(ResourceType.LAYOUT, "textEditSuggestionItemLayout",
+ "text_edit_suggestion_item", true));
+ FRAMEWORK_REPLACE_VALUES.put("textEditSuggestionContainerLayout",
+ new ResourceValue(ResourceType.LAYOUT, "textEditSuggestionContainerLayout",
+ "text_edit_suggestion_container", true));
+ FRAMEWORK_REPLACE_VALUES.put("textEditSuggestionHighlightStyle",
+ new ResourceValue(ResourceType.STYLE, "textEditSuggestionHighlightStyle",
+ "TextAppearance.Holo.SuggestionHighlight", true));
+
+ }
+
/** The map adds cookies to each view so that IDE can link xml tags to views. */
private final HashMap<View, Object> mViewKeyMap = new HashMap<>();
/**
@@ -312,7 +334,7 @@ public final class BridgeContext extends Context {
* Returns the current parser at the top the of the stack.
* @return a parser or null.
*/
- public BridgeXmlBlockParser getCurrentParser() {
+ private BridgeXmlBlockParser getCurrentParser() {
return mParserStack.peek();
}
@@ -406,7 +428,8 @@ public final class BridgeContext extends Context {
}
public Pair<View, Boolean> inflateView(ResourceReference resource, ViewGroup parent,
- boolean attachToRoot, boolean skipCallbackParser) {
+ @SuppressWarnings("SameParameterValue") boolean attachToRoot,
+ boolean skipCallbackParser) {
boolean isPlatformLayout = resource.isFramework();
if (!isPlatformLayout && !skipCallbackParser) {
@@ -711,11 +734,7 @@ public final class BridgeContext extends Context {
Object key = parser.getViewCookie();
if (key != null) {
- defaultPropMap = mDefaultPropMaps.get(key);
- if (defaultPropMap == null) {
- defaultPropMap = new PropertiesMap();
- mDefaultPropMaps.put(key, defaultPropMap);
- }
+ defaultPropMap = mDefaultPropMaps.computeIfAbsent(key, k -> new PropertiesMap());
}
} else if (set instanceof BridgeLayoutParamsMapAttributes) {
@@ -909,6 +928,16 @@ public final class BridgeContext extends Context {
// if there's no direct value for this attribute in the XML, we look for default
// values in the widget defStyle, and then in the theme.
if (value == null) {
+ if (frameworkAttr) {
+ // For some framework values, layoutlib patches the actual value in the
+ // theme when it helps to improve the final preview. In most cases
+ // we just disable animations.
+ ResourceValue patchedValue = FRAMEWORK_PATCHED_VALUES.get(attrName);
+ if (patchedValue != null) {
+ defaultValue = patchedValue;
+ }
+ }
+
// if we found a value, we make sure this doesn't reference another value.
// So we resolve it.
if (defaultValue != null) {
@@ -916,16 +945,21 @@ public final class BridgeContext extends Context {
// exist, we should log a warning and omit it.
String val = defaultValue.getValue();
if (val != null && val.startsWith(SdkConstants.PREFIX_THEME_REF)) {
- if (!attrName.equals(RTL_ATTRS.get(val)) ||
- getApplicationInfo().targetSdkVersion <
- VERSION_CODES.JELLY_BEAN_MR1) {
+ // Because we always use the latest framework code, some resources might
+ // fail to resolve when using old themes (they haven't been backported).
+ // Since this is an artifact caused by us using always the latest
+ // code, we check for some of those values and replace them here.
+ defaultValue = FRAMEWORK_REPLACE_VALUES.get(attrName);
+
+ if (defaultValue == null &&
+ (getApplicationInfo().targetSdkVersion < JELLY_BEAN_MR1 ||
+ !attrName.equals(RTL_ATTRS.get(val)))) {
// Only log a warning if the referenced value isn't one of the RTL
// attributes, or the app targets old API.
Bridge.getLog().warning(LayoutLog.TAG_RESOURCES_RESOLVE_THEME_ATTR,
String.format("Failed to find '%s' in current theme.", val),
val);
}
- defaultValue = null;
}
}
@@ -1944,7 +1978,7 @@ public final class BridgeContext extends Context {
Map<List<StyleResourceValue>,
Map<Integer, Pair<BridgeTypedArray, PropertiesMap>>>> mCache;
- public TypedArrayCache() {
+ private TypedArrayCache() {
mCache = new IdentityHashMap<>();
}
@@ -1965,17 +1999,9 @@ public final class BridgeContext extends Context {
public void put(int[] attrs, List<StyleResourceValue> themes, int resId,
Pair<BridgeTypedArray, PropertiesMap> value) {
Map<List<StyleResourceValue>, Map<Integer, Pair<BridgeTypedArray, PropertiesMap>>>
- cacheFromThemes = mCache.get(attrs);
- if (cacheFromThemes == null) {
- cacheFromThemes = new HashMap<>();
- mCache.put(attrs, cacheFromThemes);
- }
+ cacheFromThemes = mCache.computeIfAbsent(attrs, k -> new HashMap<>());
Map<Integer, Pair<BridgeTypedArray, PropertiesMap>> cacheFromResId =
- cacheFromThemes.get(themes);
- if (cacheFromResId == null) {
- cacheFromResId = new HashMap<>();
- cacheFromThemes.put(themes, cacheFromResId);
- }
+ cacheFromThemes.computeIfAbsent(themes, k -> new HashMap<>());
cacheFromResId.put(resId, value);
}
diff --git a/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/android/BridgePackageManager.java b/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/android/BridgePackageManager.java
index 8835b5845175..bc7bc74ae2c8 100644
--- a/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/android/BridgePackageManager.java
+++ b/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/android/BridgePackageManager.java
@@ -868,4 +868,9 @@ public class BridgePackageManager extends PackageManager {
public boolean isPackageAvailable(String packageName) {
return false;
}
+
+ @Override
+ public int getInstallReason(String packageName, UserHandle user) {
+ return INSTALL_REASON_UNKNOWN;
+ }
}
diff --git a/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/android/view/WindowManagerImpl.java b/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/android/view/WindowManagerImpl.java
index 30317012fb52..d7d16cf6d764 100644
--- a/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/android/view/WindowManagerImpl.java
+++ b/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/android/view/WindowManagerImpl.java
@@ -17,6 +17,7 @@ package com.android.layoutlib.bridge.android.view;
import android.util.DisplayMetrics;
import android.view.Display;
+import android.view.Display.Mode;
import android.view.DisplayAdjustments;
import android.view.DisplayInfo;
import android.view.View;
@@ -33,6 +34,9 @@ public class WindowManagerImpl implements WindowManager {
DisplayInfo info = new DisplayInfo();
info.logicalHeight = mMetrics.heightPixels;
info.logicalWidth = mMetrics.widthPixels;
+ info.supportedModes = new Mode[] {
+ new Mode(0, mMetrics.widthPixels, mMetrics.heightPixels, 60f)
+ };
mDisplay = new Display(null, Display.DEFAULT_DISPLAY, info,
DisplayAdjustments.DEFAULT_DISPLAY_ADJUSTMENTS);
}
diff --git a/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/impl/RenderSessionImpl.java b/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/impl/RenderSessionImpl.java
index 91a783ae7efc..85fe2a450e4f 100644
--- a/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/impl/RenderSessionImpl.java
+++ b/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/impl/RenderSessionImpl.java
@@ -208,7 +208,7 @@ public class RenderSessionImpl extends RenderAction<SessionParams> {
/**
* Measures the the current layout if needed (see {@link #invalidateRenderingSize}).
*/
- private void measure(@NonNull SessionParams params) {
+ private void measureLayout(@NonNull SessionParams params) {
// only do the screen measure when needed.
if (mMeasuredScreenWidth != -1) {
return;
@@ -353,7 +353,7 @@ public class RenderSessionImpl extends RenderAction<SessionParams> {
setActiveToolbar(view, context, params);
- measure(params);
+ measureLayout(params);
measureView(mViewRoot, null /*measuredView*/,
mMeasuredScreenWidth, MeasureSpec.EXACTLY,
mMeasuredScreenHeight, MeasureSpec.EXACTLY);
@@ -385,13 +385,10 @@ public class RenderSessionImpl extends RenderAction<SessionParams> {
}
/**
- * Renders the given view hierarchy to the passed canvas and returns the result of the render
- * operation.
- * @param canvas an optional canvas to render the views to. If null, only the measure and
- * layout steps will be executed.
+ * Runs a layout pass for the given view root
*/
- private static Result render(@NonNull BridgeContext context, @NonNull ViewGroup viewRoot,
- @Nullable Canvas canvas, int width, int height) {
+ private static void doLayout(@NonNull BridgeContext context, @NonNull ViewGroup viewRoot,
+ int width, int height) {
// measure again with the size we need
// This must always be done before the call to layout
measureView(viewRoot, null /*measuredView*/,
@@ -401,7 +398,16 @@ public class RenderSessionImpl extends RenderAction<SessionParams> {
// now do the layout.
viewRoot.layout(0, 0, width, height);
handleScrolling(context, viewRoot);
+ }
+ /**
+ * Renders the given view hierarchy to the passed canvas and returns the result of the render
+ * operation.
+ * @param canvas an optional canvas to render the views to. If null, only the measure and
+ * layout steps will be executed.
+ */
+ private static Result renderAndBuildResult(@NonNull BridgeContext context, @NonNull ViewGroup viewRoot,
+ @Nullable Canvas canvas, int width, int height) {
if (canvas == null) {
return SUCCESS.createResult();
}
@@ -428,6 +434,40 @@ public class RenderSessionImpl extends RenderAction<SessionParams> {
* @see RenderSession#render(long)
*/
public Result render(boolean freshRender) {
+ return renderAndBuildResult(freshRender, false);
+ }
+
+ /**
+ * Measures the layout
+ * <p>
+ * {@link #acquire(long)} must have been called before this.
+ *
+ * @throws IllegalStateException if the current context is different than the one owned by
+ * the scene, or if {@link #acquire(long)} was not called.
+ *
+ * @see SessionParams#getRenderingMode()
+ * @see RenderSession#render(long)
+ */
+ public Result measure() {
+ return renderAndBuildResult(false, true);
+ }
+
+ /**
+ * Renders the scene.
+ * <p>
+ * {@link #acquire(long)} must have been called before this.
+ *
+ * @param freshRender whether the render is a new one and should erase the existing bitmap (in
+ * the case where bitmaps are reused). This is typically needed when not playing
+ * animations.)
+ *
+ * @throws IllegalStateException if the current context is different than the one owned by
+ * the scene, or if {@link #acquire(long)} was not called.
+ *
+ * @see SessionParams#getRenderingMode()
+ * @see RenderSession#render(long)
+ */
+ private Result renderAndBuildResult(boolean freshRender, boolean onlyMeasure) {
checkLock();
SessionParams params = getParams();
@@ -437,14 +477,15 @@ public class RenderSessionImpl extends RenderAction<SessionParams> {
return ERROR_NOT_INFLATED.createResult();
}
- measure(params);
+ measureLayout(params);
HardwareConfig hardwareConfig = params.getHardwareConfig();
Result renderResult = SUCCESS.createResult();
- if (params.isLayoutOnly()) {
+ if (onlyMeasure) {
// delete the canvas and image to reset them on the next full rendering
mImage = null;
mCanvas = null;
+ doLayout(getContext(), mViewRoot, mMeasuredScreenWidth, mMeasuredScreenHeight);
} else {
// draw the views
// create the BufferedImage into which the layout will be rendered.
@@ -505,11 +546,12 @@ public class RenderSessionImpl extends RenderAction<SessionParams> {
gc.dispose();
}
+ doLayout(getContext(), mViewRoot, mMeasuredScreenWidth, mMeasuredScreenHeight);
if (mElapsedFrameTimeNanos >= 0) {
long initialTime = System_Delegate.nanoTime();
if (!mFirstFrameExecuted) {
// We need to run an initial draw call to initialize the animations
- render(getContext(), mViewRoot, NOP_CANVAS, mMeasuredScreenWidth, mMeasuredScreenHeight);
+ renderAndBuildResult(getContext(), mViewRoot, NOP_CANVAS, mMeasuredScreenWidth, mMeasuredScreenHeight);
// The first frame will initialize the animations
Choreographer_Delegate.doFrame(initialTime);
@@ -518,7 +560,7 @@ public class RenderSessionImpl extends RenderAction<SessionParams> {
// Second frame will move the animations
Choreographer_Delegate.doFrame(initialTime + mElapsedFrameTimeNanos);
}
- renderResult = render(getContext(), mViewRoot, mCanvas, mMeasuredScreenWidth,
+ renderResult = renderAndBuildResult(getContext(), mViewRoot, mCanvas, mMeasuredScreenWidth,
mMeasuredScreenHeight);
}
diff --git a/tools/layoutlib/bridge/src/libcore/util/NativeAllocationRegistry_Delegate.java b/tools/layoutlib/bridge/src/libcore/util/NativeAllocationRegistry_Delegate.java
index 6246ec1b8661..04fabc242454 100644
--- a/tools/layoutlib/bridge/src/libcore/util/NativeAllocationRegistry_Delegate.java
+++ b/tools/layoutlib/bridge/src/libcore/util/NativeAllocationRegistry_Delegate.java
@@ -52,9 +52,15 @@ public class NativeAllocationRegistry_Delegate {
@LayoutlibDelegate
/*package*/ static void applyFreeFunction(long freeFunction, long nativePtr) {
- NativeAllocationRegistry_Delegate delegate = sManager.getDelegate(freeFunction);
- if (delegate != null) {
- delegate.mFinalizer.free(nativePtr);
+ // This method MIGHT run in the context of the finalizer thread. If the delegate method
+ // crashes, it could bring down the VM. That's why we catch all the exceptions and ignore
+ // them.
+ try {
+ NativeAllocationRegistry_Delegate delegate = sManager.getDelegate(freeFunction);
+ if (delegate != null) {
+ delegate.mFinalizer.free(nativePtr);
+ }
+ } catch (Throwable ignore) {
}
}
diff --git a/tools/layoutlib/bridge/tests/res/testApp/MyApplication/golden/simple_activity-old-theme.png b/tools/layoutlib/bridge/tests/res/testApp/MyApplication/golden/simple_activity-old-theme.png
new file mode 100644
index 000000000000..eb431b0280da
--- /dev/null
+++ b/tools/layoutlib/bridge/tests/res/testApp/MyApplication/golden/simple_activity-old-theme.png
Binary files differ
diff --git a/tools/layoutlib/bridge/tests/res/testApp/MyApplication/src/main/res/layout/allwidgets.xml b/tools/layoutlib/bridge/tests/res/testApp/MyApplication/src/main/res/layout/allwidgets.xml
index adb58a322abb..05a3665deab1 100644
--- a/tools/layoutlib/bridge/tests/res/testApp/MyApplication/src/main/res/layout/allwidgets.xml
+++ b/tools/layoutlib/bridge/tests/res/testApp/MyApplication/src/main/res/layout/allwidgets.xml
@@ -28,7 +28,8 @@
android:layout_alignParentStart="true"
android:layout_below="@id/frameLayout"
android:text="Large Text"
- android:textAppearance="?android:attr/textAppearanceLarge" />
+ android:textAppearance="?android:attr/textAppearanceLarge"
+ android:pointerIcon="hand" />
<TextView
android:id="@id/textView3"
diff --git a/tools/layoutlib/bridge/tests/res/testApp/MyApplication/src/main/res/values/arrays.xml b/tools/layoutlib/bridge/tests/res/testApp/MyApplication/src/main/res/values/arrays.xml
index f6e14d2b83b9..5f58d390bf32 100644
--- a/tools/layoutlib/bridge/tests/res/testApp/MyApplication/src/main/res/values/arrays.xml
+++ b/tools/layoutlib/bridge/tests/res/testApp/MyApplication/src/main/res/values/arrays.xml
@@ -17,6 +17,7 @@
<!-- theme ref in android NS. value = @string/candidates_style = <u>candidates</u> -->
<item>?android:attr/candidatesTextStyleSpans</item>
<item>@android:string/unknownName</item> <!-- value = Unknown -->
+ <item>?EC</item>
</string-array>
<!-- resources that the above array can refer to -->
diff --git a/tools/layoutlib/bridge/tests/res/testApp/MyApplication/src/main/res/values/styles.xml b/tools/layoutlib/bridge/tests/res/testApp/MyApplication/src/main/res/values/styles.xml
index c8a5fec71f09..debe33bea72e 100644
--- a/tools/layoutlib/bridge/tests/res/testApp/MyApplication/src/main/res/values/styles.xml
+++ b/tools/layoutlib/bridge/tests/res/testApp/MyApplication/src/main/res/values/styles.xml
@@ -3,7 +3,6 @@
<!-- Base application theme. -->
<style name="AppTheme" parent="android:Theme.Material.Light.DarkActionBar">
<item name="myattr">@integer/ten</item>
- <item name="android:animateFirstView">false</item>
</style>
</resources>
diff --git a/tools/layoutlib/bridge/tests/src/com/android/layoutlib/bridge/intensive/Main.java b/tools/layoutlib/bridge/tests/src/com/android/layoutlib/bridge/intensive/Main.java
index cdcae89097b5..d0c04d7e3de4 100644
--- a/tools/layoutlib/bridge/tests/src/com/android/layoutlib/bridge/intensive/Main.java
+++ b/tools/layoutlib/bridge/tests/src/com/android/layoutlib/bridge/intensive/Main.java
@@ -69,6 +69,7 @@ import java.util.concurrent.TimeUnit;
import com.google.android.collect.Lists;
+import static org.junit.Assert.assertArrayEquals;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertNotNull;
@@ -313,6 +314,12 @@ public class Main {
sBridge = new Bridge();
sBridge.init(ConfigGenerator.loadProperties(buildProp), fontLocation,
ConfigGenerator.getEnumMap(attrs), getLayoutLog());
+ Bridge.getLock().lock();
+ try {
+ Bridge.setLog(getLayoutLog());
+ } finally {
+ Bridge.getLock().unlock();
+ }
}
@Before
@@ -327,6 +334,20 @@ public class Main {
}
@Test
+ public void testActivityOnOldTheme() throws ClassNotFoundException {
+ LayoutLibTestCallback layoutLibCallback =
+ new LayoutLibTestCallback(getLogger(), mDefaultClassLoader);
+ layoutLibCallback.initResources();
+
+ LayoutPullParser parser = createLayoutPullParser("simple_activity.xml");
+ SessionParams params = getSessionParams(parser, ConfigGenerator.NEXUS_5,
+ layoutLibCallback, "Theme.NoTitleBar", false,
+ RenderingMode.NORMAL, 22);
+
+ renderAndVerify(params, "simple_activity-old-theme.png");
+ }
+
+ @Test
public void testTranslucentBars() throws ClassNotFoundException {
LayoutLibTestCallback layoutLibCallback =
new LayoutLibTestCallback(getLogger(), mDefaultClassLoader);
@@ -555,7 +576,7 @@ public class Main {
/** Test activity.xml */
@Test
- public void testScrolling() throws ClassNotFoundException {
+ public void testScrollingAndMeasure() throws ClassNotFoundException {
// Create the layout pull parser.
LayoutPullParser parser = createLayoutPullParser("scrolled.xml");
// Create LayoutLibCallback.
@@ -569,7 +590,10 @@ public class Main {
params.setForceNoDecor();
params.setExtendedViewInfoMode(true);
- RenderResult result = renderAndVerify(params, "scrolled.png");
+ // Do an only-measure pass
+ RenderSession session = sBridge.createSession(params);
+ session.measure();
+ RenderResult result = RenderResult.getFromSession(session);
assertNotNull(result);
assertNotNull(result.getResult());
assertTrue(result.getResult().isSuccess());
@@ -586,6 +610,20 @@ public class Main {
assertEquals(90, rootLayout.getChildren().get(5).getChildren().get(0).getLeft());
assertEquals(-270, rootLayout.getChildren().get(5).getChildren().get(0).getBottom());
assertEquals(690, rootLayout.getChildren().get(5).getChildren().get(0).getRight());
+
+ // Do a full render pass
+ parser = createLayoutPullParser("scrolled.xml");
+
+ params = getSessionParams(parser, ConfigGenerator.NEXUS_5,
+ layoutLibCallback, "Theme.Material.NoActionBar.Fullscreen", false,
+ RenderingMode.V_SCROLL, 22);
+ params.setForceNoDecor();
+ params.setExtendedViewInfoMode(true);
+
+ result = renderAndVerify(params, "scrolled.png");
+ assertNotNull(result);
+ assertNotNull(result.getResult());
+ assertTrue(result.getResult().isSuccess());
}
@Test
@@ -624,6 +662,36 @@ public class Main {
assertEquals("app_name", resources.getResourceEntryName(id));
}
+ @Test
+ public void testStringEscaping() throws Exception {
+ // Setup
+ // Create the layout pull parser for our resources (empty.xml can not be part of the test
+ // app as it won't compile).
+ LayoutPullParser parser = new LayoutPullParser("/empty.xml");
+ // Create LayoutLibCallback.
+ LayoutLibTestCallback layoutLibCallback =
+ new LayoutLibTestCallback(getLogger(), mDefaultClassLoader);
+ layoutLibCallback.initResources();
+ SessionParams params = getSessionParams(parser, ConfigGenerator.NEXUS_4,
+ layoutLibCallback, "AppTheme", true, RenderingMode.NORMAL, 22);
+ AssetManager assetManager = AssetManager.getSystem();
+ DisplayMetrics metrics = new DisplayMetrics();
+ Configuration configuration = RenderAction.getConfiguration(params);
+ Resources resources = new Resources(assetManager, metrics, configuration);
+ resources.mLayoutlibCallback = params.getLayoutlibCallback();
+ resources.mContext =
+ new BridgeContext(params.getProjectKey(), metrics, params.getResources(),
+ params.getAssets(), params.getLayoutlibCallback(), configuration,
+ params.getTargetSdkVersion(), params.isRtlSupported());
+
+ int id = resources.mLayoutlibCallback.getResourceId(ResourceType.ARRAY, "string_array");
+ String[] strings = resources.getStringArray(id);
+ assertArrayEquals(
+ new String[]{"mystring", "Hello world!", "candidates", "Unknown", "?EC"},
+ strings);
+ assertTrue(sRenderMessages.isEmpty());
+ }
+
@NonNull
private LayoutPullParser createLayoutPullParser(String layoutPath) {
return new LayoutPullParser(APP_TEST_RES + "/layout/" + layoutPath);
diff --git a/tools/layoutlib/create/src/com/android/tools/layoutlib/create/CreateInfo.java b/tools/layoutlib/create/src/com/android/tools/layoutlib/create/CreateInfo.java
index 92fd75cc5a36..741eb27558ed 100644
--- a/tools/layoutlib/create/src/com/android/tools/layoutlib/create/CreateInfo.java
+++ b/tools/layoutlib/create/src/com/android/tools/layoutlib/create/CreateInfo.java
@@ -154,6 +154,8 @@ public final class CreateInfo implements ICreateInfo {
"android.content.res.Resources#getIntArray",
"android.content.res.Resources#getInteger",
"android.content.res.Resources#getLayout",
+ "android.content.res.Resources#getQuantityString",
+ "android.content.res.Resources#getQuantityText",
"android.content.res.Resources#getResourceEntryName",
"android.content.res.Resources#getResourceName",
"android.content.res.Resources#getResourcePackageName",
@@ -232,6 +234,7 @@ public final class CreateInfo implements ICreateInfo {
"android.view.RenderNode#nSetScaleY",
"android.view.RenderNode#nGetScaleY",
"android.view.RenderNode#nIsPivotExplicitlySet",
+ "android.view.PointerIcon#loadResource",
"android.view.ViewGroup#drawChild",
"com.android.internal.view.menu.MenuBuilder#createNewMenuItem",
"com.android.internal.util.XmlUtils#convertValueToInt",
@@ -336,7 +339,8 @@ public final class CreateInfo implements ICreateInfo {
*/
private final static String[] PROMOTED_FIELDS = new String[] {
"android.graphics.drawable.VectorDrawable#mVectorState",
- "android.view.Choreographer#mLastFrameTimeNanos"
+ "android.view.Choreographer#mLastFrameTimeNanos",
+ "android.graphics.FontFamily#mBuilderPtr"
};
/**